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::{AcceptEditPredictionBinding, 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::DB;
  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};
  224use zed_actions::editor::{MoveDown, MoveUp};
  225
  226use crate::{
  227    code_context_menus::CompletionsMenuSource,
  228    editor_settings::MultiCursorModifier,
  229    hover_links::{find_url, find_url_from_range},
  230    inlays::{
  231        InlineValueCache,
  232        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  233    },
  234    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  235    scroll::{ScrollOffset, ScrollPixelOffset},
  236    selections_collection::resolve_selections_wrapping_blocks,
  237    semantic_tokens::SemanticTokenState,
  238    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  239};
  240
  241pub const FILE_HEADER_HEIGHT: u32 = 2;
  242pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  243pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  244const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  245const MAX_LINE_LEN: usize = 1024;
  246const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  247const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  248pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  249#[doc(hidden)]
  250pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  251pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  252
  253pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  254pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  256pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  257
  258pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  259pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  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
  704impl EditPredictionPreview {
  705    pub fn released_too_fast(&self) -> bool {
  706        match self {
  707            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  708            EditPredictionPreview::Active { .. } => false,
  709        }
  710    }
  711
  712    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  713        if let EditPredictionPreview::Active {
  714            previous_scroll_position,
  715            ..
  716        } = self
  717        {
  718            *previous_scroll_position = scroll_position;
  719        }
  720    }
  721}
  722
  723pub struct ContextMenuOptions {
  724    pub min_entries_visible: usize,
  725    pub max_entries_visible: usize,
  726    pub placement: Option<ContextMenuPlacement>,
  727}
  728
  729#[derive(Debug, Clone, PartialEq, Eq)]
  730pub enum ContextMenuPlacement {
  731    Above,
  732    Below,
  733}
  734
  735#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  736struct EditorActionId(usize);
  737
  738impl EditorActionId {
  739    pub fn post_inc(&mut self) -> Self {
  740        let answer = self.0;
  741
  742        *self = Self(answer + 1);
  743
  744        Self(answer)
  745    }
  746}
  747
  748// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  749// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  750
  751type BackgroundHighlight = (
  752    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  753    Arc<[Range<Anchor>]>,
  754);
  755type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  756
  757#[derive(Default)]
  758struct ScrollbarMarkerState {
  759    scrollbar_size: Size<Pixels>,
  760    dirty: bool,
  761    markers: Arc<[PaintQuad]>,
  762    pending_refresh: Option<Task<Result<()>>>,
  763}
  764
  765impl ScrollbarMarkerState {
  766    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  767        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  768    }
  769}
  770
  771#[derive(Clone, Copy, PartialEq, Eq)]
  772pub enum MinimapVisibility {
  773    Disabled,
  774    Enabled {
  775        /// The configuration currently present in the users settings.
  776        setting_configuration: bool,
  777        /// Whether to override the currently set visibility from the users setting.
  778        toggle_override: bool,
  779    },
  780}
  781
  782impl MinimapVisibility {
  783    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  784        if mode.is_full() {
  785            Self::Enabled {
  786                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  787                toggle_override: false,
  788            }
  789        } else {
  790            Self::Disabled
  791        }
  792    }
  793
  794    fn hidden(&self) -> Self {
  795        match *self {
  796            Self::Enabled {
  797                setting_configuration,
  798                ..
  799            } => Self::Enabled {
  800                setting_configuration,
  801                toggle_override: setting_configuration,
  802            },
  803            Self::Disabled => Self::Disabled,
  804        }
  805    }
  806
  807    fn disabled(&self) -> bool {
  808        matches!(*self, Self::Disabled)
  809    }
  810
  811    fn settings_visibility(&self) -> bool {
  812        match *self {
  813            Self::Enabled {
  814                setting_configuration,
  815                ..
  816            } => setting_configuration,
  817            _ => false,
  818        }
  819    }
  820
  821    fn visible(&self) -> bool {
  822        match *self {
  823            Self::Enabled {
  824                setting_configuration,
  825                toggle_override,
  826            } => setting_configuration ^ toggle_override,
  827            _ => false,
  828        }
  829    }
  830
  831    fn toggle_visibility(&self) -> Self {
  832        match *self {
  833            Self::Enabled {
  834                toggle_override,
  835                setting_configuration,
  836            } => Self::Enabled {
  837                setting_configuration,
  838                toggle_override: !toggle_override,
  839            },
  840            Self::Disabled => Self::Disabled,
  841        }
  842    }
  843}
  844
  845#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  846pub enum BufferSerialization {
  847    All,
  848    NonDirtyBuffers,
  849}
  850
  851impl BufferSerialization {
  852    fn new(restore_unsaved_buffers: bool) -> Self {
  853        if restore_unsaved_buffers {
  854            Self::All
  855        } else {
  856            Self::NonDirtyBuffers
  857        }
  858    }
  859}
  860
  861/// Addons allow storing per-editor state in other crates (e.g. Vim)
  862pub trait Addon: 'static {
  863    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  864
  865    fn render_buffer_header_controls(
  866        &self,
  867        _: &ExcerptInfo,
  868        _: &Window,
  869        _: &App,
  870    ) -> Option<AnyElement> {
  871        None
  872    }
  873
  874    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  875        None
  876    }
  877
  878    fn to_any(&self) -> &dyn std::any::Any;
  879
  880    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  881        None
  882    }
  883}
  884
  885struct ChangeLocation {
  886    current: Option<Vec<Anchor>>,
  887    original: Vec<Anchor>,
  888}
  889impl ChangeLocation {
  890    fn locations(&self) -> &[Anchor] {
  891        self.current.as_ref().unwrap_or(&self.original)
  892    }
  893}
  894
  895/// A set of caret positions, registered when the editor was edited.
  896pub struct ChangeList {
  897    changes: Vec<ChangeLocation>,
  898    /// Currently "selected" change.
  899    position: Option<usize>,
  900}
  901
  902impl ChangeList {
  903    pub fn new() -> Self {
  904        Self {
  905            changes: Vec::new(),
  906            position: None,
  907        }
  908    }
  909
  910    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  911    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  912    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  913        if self.changes.is_empty() {
  914            return None;
  915        }
  916
  917        let prev = self.position.unwrap_or(self.changes.len());
  918        let next = if direction == Direction::Prev {
  919            prev.saturating_sub(count)
  920        } else {
  921            (prev + count).min(self.changes.len() - 1)
  922        };
  923        self.position = Some(next);
  924        self.changes.get(next).map(|change| change.locations())
  925    }
  926
  927    /// Adds a new change to the list, resetting the change list position.
  928    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  929        self.position.take();
  930        if let Some(last) = self.changes.last_mut()
  931            && group
  932        {
  933            last.current = Some(new_positions)
  934        } else {
  935            self.changes.push(ChangeLocation {
  936                original: new_positions,
  937                current: None,
  938            });
  939        }
  940    }
  941
  942    pub fn last(&self) -> Option<&[Anchor]> {
  943        self.changes.last().map(|change| change.locations())
  944    }
  945
  946    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  947        self.changes.last().map(|change| change.original.as_slice())
  948    }
  949
  950    pub fn invert_last_group(&mut self) {
  951        if let Some(last) = self.changes.last_mut()
  952            && let Some(current) = last.current.as_mut()
  953        {
  954            mem::swap(&mut last.original, current);
  955        }
  956    }
  957}
  958
  959#[derive(Clone)]
  960struct InlineBlamePopoverState {
  961    scroll_handle: ScrollHandle,
  962    commit_message: Option<ParsedCommitMessage>,
  963    markdown: Entity<Markdown>,
  964}
  965
  966struct InlineBlamePopover {
  967    position: gpui::Point<Pixels>,
  968    hide_task: Option<Task<()>>,
  969    popover_bounds: Option<Bounds<Pixels>>,
  970    popover_state: InlineBlamePopoverState,
  971    keyboard_grace: bool,
  972}
  973
  974enum SelectionDragState {
  975    /// State when no drag related activity is detected.
  976    None,
  977    /// State when the mouse is down on a selection that is about to be dragged.
  978    ReadyToDrag {
  979        selection: Selection<Anchor>,
  980        click_position: gpui::Point<Pixels>,
  981        mouse_down_time: Instant,
  982    },
  983    /// State when the mouse is dragging the selection in the editor.
  984    Dragging {
  985        selection: Selection<Anchor>,
  986        drop_cursor: Selection<Anchor>,
  987        hide_drop_cursor: bool,
  988    },
  989}
  990
  991enum ColumnarSelectionState {
  992    FromMouse {
  993        selection_tail: Anchor,
  994        display_point: Option<DisplayPoint>,
  995    },
  996    FromSelection {
  997        selection_tail: Anchor,
  998    },
  999}
 1000
 1001/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1002/// a breakpoint on them.
 1003#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1004struct PhantomBreakpointIndicator {
 1005    display_row: DisplayRow,
 1006    /// There's a small debounce between hovering over the line and showing the indicator.
 1007    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1008    is_active: bool,
 1009    collides_with_existing_breakpoint: bool,
 1010}
 1011
 1012/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1013/// in diff view mode.
 1014#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1015pub(crate) struct PhantomDiffReviewIndicator {
 1016    /// The starting anchor of the selection (or the only row if not dragging).
 1017    pub start: Anchor,
 1018    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1019    pub end: Anchor,
 1020    /// There's a small debounce between hovering over the line and showing the indicator.
 1021    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1022    pub is_active: bool,
 1023}
 1024
 1025#[derive(Clone, Debug)]
 1026pub(crate) struct DiffReviewDragState {
 1027    pub start_anchor: Anchor,
 1028    pub current_anchor: Anchor,
 1029}
 1030
 1031impl DiffReviewDragState {
 1032    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1033        let start = self.start_anchor.to_display_point(snapshot).row();
 1034        let current = self.current_anchor.to_display_point(snapshot).row();
 1035
 1036        (start..=current).sorted()
 1037    }
 1038}
 1039
 1040/// Identifies a specific hunk in the diff buffer.
 1041/// Used as a key to group comments by their location.
 1042#[derive(Clone, Debug)]
 1043pub struct DiffHunkKey {
 1044    /// The file path (relative to worktree) this hunk belongs to.
 1045    pub file_path: Arc<util::rel_path::RelPath>,
 1046    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1047    pub hunk_start_anchor: Anchor,
 1048}
 1049
 1050/// A review comment stored locally before being sent to the Agent panel.
 1051#[derive(Clone)]
 1052pub struct StoredReviewComment {
 1053    /// Unique identifier for this comment (for edit/delete operations).
 1054    pub id: usize,
 1055    /// The comment text entered by the user.
 1056    pub comment: String,
 1057    /// Anchors for the code range being reviewed.
 1058    pub range: Range<Anchor>,
 1059    /// Timestamp when the comment was created (for chronological ordering).
 1060    pub created_at: Instant,
 1061    /// Whether this comment is currently being edited inline.
 1062    pub is_editing: bool,
 1063}
 1064
 1065impl StoredReviewComment {
 1066    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1067        Self {
 1068            id,
 1069            comment,
 1070            range: anchor_range,
 1071            created_at: Instant::now(),
 1072            is_editing: false,
 1073        }
 1074    }
 1075}
 1076
 1077/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1078pub(crate) struct DiffReviewOverlay {
 1079    pub anchor_range: Range<Anchor>,
 1080    /// The block ID for the overlay.
 1081    pub block_id: CustomBlockId,
 1082    /// The editor entity for the review input.
 1083    pub prompt_editor: Entity<Editor>,
 1084    /// The hunk key this overlay belongs to.
 1085    pub hunk_key: DiffHunkKey,
 1086    /// Whether the comments section is expanded.
 1087    pub comments_expanded: bool,
 1088    /// Editors for comments currently being edited inline.
 1089    /// Key: comment ID, Value: Editor entity for inline editing.
 1090    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1091    /// Subscriptions for inline edit editors' action handlers.
 1092    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1093    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1094    /// The current user's avatar URI for display in comment rows.
 1095    pub user_avatar_uri: Option<SharedUri>,
 1096    /// Subscription to keep the action handler alive.
 1097    _subscription: Subscription,
 1098}
 1099
 1100/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1101///
 1102/// See the [module level documentation](self) for more information.
 1103pub struct Editor {
 1104    focus_handle: FocusHandle,
 1105    last_focused_descendant: Option<WeakFocusHandle>,
 1106    /// The text buffer being edited
 1107    buffer: Entity<MultiBuffer>,
 1108    /// Map of how text in the buffer should be displayed.
 1109    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1110    pub display_map: Entity<DisplayMap>,
 1111    placeholder_display_map: Option<Entity<DisplayMap>>,
 1112    pub selections: SelectionsCollection,
 1113    pub scroll_manager: ScrollManager,
 1114    /// When inline assist editors are linked, they all render cursors because
 1115    /// typing enters text into each of them, even the ones that aren't focused.
 1116    pub(crate) show_cursor_when_unfocused: bool,
 1117    columnar_selection_state: Option<ColumnarSelectionState>,
 1118    add_selections_state: Option<AddSelectionsState>,
 1119    select_next_state: Option<SelectNextState>,
 1120    select_prev_state: Option<SelectNextState>,
 1121    selection_history: SelectionHistory,
 1122    defer_selection_effects: bool,
 1123    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1124    autoclose_regions: Vec<AutocloseRegion>,
 1125    snippet_stack: InvalidationStack<SnippetState>,
 1126    select_syntax_node_history: SelectSyntaxNodeHistory,
 1127    ime_transaction: Option<TransactionId>,
 1128    pub diagnostics_max_severity: DiagnosticSeverity,
 1129    active_diagnostics: ActiveDiagnostic,
 1130    show_inline_diagnostics: bool,
 1131    inline_diagnostics_update: Task<()>,
 1132    inline_diagnostics_enabled: bool,
 1133    diagnostics_enabled: bool,
 1134    word_completions_enabled: bool,
 1135    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1136    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1137    hard_wrap: Option<usize>,
 1138    project: Option<Entity<Project>>,
 1139    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1140    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1141    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1142    blink_manager: Entity<BlinkManager>,
 1143    show_cursor_names: bool,
 1144    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1145    pub show_local_selections: bool,
 1146    mode: EditorMode,
 1147    show_breadcrumbs: bool,
 1148    show_gutter: bool,
 1149    show_scrollbars: ScrollbarAxes,
 1150    minimap_visibility: MinimapVisibility,
 1151    offset_content: bool,
 1152    disable_expand_excerpt_buttons: bool,
 1153    delegate_expand_excerpts: bool,
 1154    delegate_stage_and_restore: bool,
 1155    delegate_open_excerpts: bool,
 1156    enable_lsp_data: bool,
 1157    enable_runnables: bool,
 1158    show_line_numbers: Option<bool>,
 1159    use_relative_line_numbers: Option<bool>,
 1160    show_git_diff_gutter: Option<bool>,
 1161    show_code_actions: Option<bool>,
 1162    show_runnables: Option<bool>,
 1163    show_breakpoints: Option<bool>,
 1164    show_diff_review_button: bool,
 1165    show_wrap_guides: Option<bool>,
 1166    show_indent_guides: Option<bool>,
 1167    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1168    highlight_order: usize,
 1169    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1170    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1171    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1172    scrollbar_marker_state: ScrollbarMarkerState,
 1173    active_indent_guides_state: ActiveIndentGuidesState,
 1174    nav_history: Option<ItemNavHistory>,
 1175    context_menu: RefCell<Option<CodeContextMenu>>,
 1176    context_menu_options: Option<ContextMenuOptions>,
 1177    mouse_context_menu: Option<MouseContextMenu>,
 1178    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1179    inline_blame_popover: Option<InlineBlamePopover>,
 1180    inline_blame_popover_show_task: Option<Task<()>>,
 1181    signature_help_state: SignatureHelpState,
 1182    auto_signature_help: Option<bool>,
 1183    find_all_references_task_sources: Vec<Anchor>,
 1184    next_completion_id: CompletionId,
 1185    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1186    code_actions_task: Option<Task<Result<()>>>,
 1187    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1188    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1189    debounced_selection_highlight_complete: bool,
 1190    document_highlights_task: Option<Task<()>>,
 1191    linked_editing_range_task: Option<Task<Option<()>>>,
 1192    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1193    pending_rename: Option<RenameState>,
 1194    searchable: bool,
 1195    cursor_shape: CursorShape,
 1196    /// Whether the cursor is offset one character to the left when something is
 1197    /// selected (needed for vim visual mode)
 1198    cursor_offset_on_selection: bool,
 1199    current_line_highlight: Option<CurrentLineHighlight>,
 1200    /// Whether to collapse search match ranges to just their start position.
 1201    /// When true, navigating to a match positions the cursor at the match
 1202    /// without selecting the matched text.
 1203    collapse_matches: bool,
 1204    autoindent_mode: Option<AutoindentMode>,
 1205    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1206    input_enabled: bool,
 1207    expects_character_input: bool,
 1208    use_modal_editing: bool,
 1209    read_only: bool,
 1210    leader_id: Option<CollaboratorId>,
 1211    remote_id: Option<ViewId>,
 1212    pub hover_state: HoverState,
 1213    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1214    prev_pressure_stage: Option<PressureStage>,
 1215    gutter_hovered: bool,
 1216    hovered_link_state: Option<HoveredLinkState>,
 1217    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1218    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1219    active_edit_prediction: Option<EditPredictionState>,
 1220    /// Used to prevent flickering as the user types while the menu is open
 1221    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1222    edit_prediction_settings: EditPredictionSettings,
 1223    edit_predictions_hidden_for_vim_mode: bool,
 1224    show_edit_predictions_override: Option<bool>,
 1225    show_completions_on_input_override: Option<bool>,
 1226    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1227    edit_prediction_preview: EditPredictionPreview,
 1228    edit_prediction_indent_conflict: bool,
 1229    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1230    next_inlay_id: usize,
 1231    next_color_inlay_id: usize,
 1232    _subscriptions: Vec<Subscription>,
 1233    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1234    gutter_dimensions: GutterDimensions,
 1235    style: Option<EditorStyle>,
 1236    text_style_refinement: Option<TextStyleRefinement>,
 1237    next_editor_action_id: EditorActionId,
 1238    editor_actions: Rc<
 1239        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1240    >,
 1241    use_autoclose: bool,
 1242    use_auto_surround: bool,
 1243    auto_replace_emoji_shortcode: bool,
 1244    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1245    show_git_blame_gutter: bool,
 1246    show_git_blame_inline: bool,
 1247    show_git_blame_inline_delay_task: Option<Task<()>>,
 1248    git_blame_inline_enabled: bool,
 1249    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1250    buffer_serialization: Option<BufferSerialization>,
 1251    show_selection_menu: Option<bool>,
 1252    blame: Option<Entity<GitBlame>>,
 1253    blame_subscription: Option<Subscription>,
 1254    custom_context_menu: Option<
 1255        Box<
 1256            dyn 'static
 1257                + Fn(
 1258                    &mut Self,
 1259                    DisplayPoint,
 1260                    &mut Window,
 1261                    &mut Context<Self>,
 1262                ) -> Option<Entity<ui::ContextMenu>>,
 1263        >,
 1264    >,
 1265    last_bounds: Option<Bounds<Pixels>>,
 1266    last_position_map: Option<Rc<PositionMap>>,
 1267    expect_bounds_change: Option<Bounds<Pixels>>,
 1268    runnables: RunnableData,
 1269    breakpoint_store: Option<Entity<BreakpointStore>>,
 1270    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1271    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1272    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1273    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1274    /// when hunks have comments stored.
 1275    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1276    /// Stored review comments grouped by hunk.
 1277    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1278    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1279    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1280    /// Counter for generating unique comment IDs.
 1281    next_review_comment_id: usize,
 1282    hovered_diff_hunk_row: Option<DisplayRow>,
 1283    pull_diagnostics_task: Task<()>,
 1284    in_project_search: bool,
 1285    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1286    breadcrumb_header: Option<String>,
 1287    focused_block: Option<FocusedBlock>,
 1288    next_scroll_position: NextScrollCursorCenterTopBottom,
 1289    addons: HashMap<TypeId, Box<dyn Addon>>,
 1290    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1291    load_diff_task: Option<Shared<Task<()>>>,
 1292    /// Whether we are temporarily displaying a diff other than git's
 1293    temporary_diff_override: bool,
 1294    selection_mark_mode: bool,
 1295    toggle_fold_multiple_buffers: Task<()>,
 1296    _scroll_cursor_center_top_bottom_task: Task<()>,
 1297    serialize_selections: Task<()>,
 1298    serialize_folds: Task<()>,
 1299    mouse_cursor_hidden: bool,
 1300    minimap: Option<Entity<Self>>,
 1301    hide_mouse_mode: HideMouseMode,
 1302    pub change_list: ChangeList,
 1303    inline_value_cache: InlineValueCache,
 1304    number_deleted_lines: bool,
 1305
 1306    selection_drag_state: SelectionDragState,
 1307    colors: Option<LspColorData>,
 1308    post_scroll_update: Task<()>,
 1309    refresh_colors_task: Task<()>,
 1310    use_document_folding_ranges: bool,
 1311    refresh_folding_ranges_task: Task<()>,
 1312    inlay_hints: Option<LspInlayHintData>,
 1313    folding_newlines: Task<()>,
 1314    select_next_is_case_sensitive: Option<bool>,
 1315    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1316    on_local_selections_changed:
 1317        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1318    suppress_selection_callback: bool,
 1319    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1320    accent_data: Option<AccentData>,
 1321    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1322    semantic_token_state: SemanticTokenState,
 1323    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1324    refresh_document_symbols_task: Shared<Task<()>>,
 1325    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1326    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1327    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1328    sticky_headers_task: Task<()>,
 1329    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1330    pub(crate) colorize_brackets_task: Task<()>,
 1331}
 1332
 1333#[derive(Debug, PartialEq)]
 1334struct AccentData {
 1335    colors: AccentColors,
 1336    overrides: Vec<SharedString>,
 1337}
 1338
 1339fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1340    if debounce_ms > 0 {
 1341        Some(Duration::from_millis(debounce_ms))
 1342    } else {
 1343        None
 1344    }
 1345}
 1346
 1347#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1348enum NextScrollCursorCenterTopBottom {
 1349    #[default]
 1350    Center,
 1351    Top,
 1352    Bottom,
 1353}
 1354
 1355impl NextScrollCursorCenterTopBottom {
 1356    fn next(&self) -> Self {
 1357        match self {
 1358            Self::Center => Self::Top,
 1359            Self::Top => Self::Bottom,
 1360            Self::Bottom => Self::Center,
 1361        }
 1362    }
 1363}
 1364
 1365#[derive(Clone)]
 1366pub struct EditorSnapshot {
 1367    pub mode: EditorMode,
 1368    show_gutter: bool,
 1369    offset_content: bool,
 1370    show_line_numbers: Option<bool>,
 1371    number_deleted_lines: bool,
 1372    show_git_diff_gutter: Option<bool>,
 1373    show_code_actions: Option<bool>,
 1374    show_runnables: Option<bool>,
 1375    show_breakpoints: Option<bool>,
 1376    git_blame_gutter_max_author_length: Option<usize>,
 1377    pub display_snapshot: DisplaySnapshot,
 1378    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1379    is_focused: bool,
 1380    scroll_anchor: SharedScrollAnchor,
 1381    ongoing_scroll: OngoingScroll,
 1382    current_line_highlight: CurrentLineHighlight,
 1383    gutter_hovered: bool,
 1384    semantic_tokens_enabled: bool,
 1385}
 1386
 1387#[derive(Default, Debug, Clone, Copy)]
 1388pub struct GutterDimensions {
 1389    pub left_padding: Pixels,
 1390    pub right_padding: Pixels,
 1391    pub width: Pixels,
 1392    pub margin: Pixels,
 1393    pub git_blame_entries_width: Option<Pixels>,
 1394}
 1395
 1396impl GutterDimensions {
 1397    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1398        Self {
 1399            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1400            ..Default::default()
 1401        }
 1402    }
 1403
 1404    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1405        -cx.text_system().descent(font_id, font_size)
 1406    }
 1407    /// The full width of the space taken up by the gutter.
 1408    pub fn full_width(&self) -> Pixels {
 1409        self.margin + self.width
 1410    }
 1411
 1412    /// The width of the space reserved for the fold indicators,
 1413    /// use alongside 'justify_end' and `gutter_width` to
 1414    /// right align content with the line numbers
 1415    pub fn fold_area_width(&self) -> Pixels {
 1416        self.margin + self.right_padding
 1417    }
 1418}
 1419
 1420struct CharacterDimensions {
 1421    em_width: Pixels,
 1422    em_advance: Pixels,
 1423    line_height: Pixels,
 1424}
 1425
 1426#[derive(Debug)]
 1427pub struct RemoteSelection {
 1428    pub replica_id: ReplicaId,
 1429    pub selection: Selection<Anchor>,
 1430    pub cursor_shape: CursorShape,
 1431    pub collaborator_id: CollaboratorId,
 1432    pub line_mode: bool,
 1433    pub user_name: Option<SharedString>,
 1434    pub color: PlayerColor,
 1435}
 1436
 1437#[derive(Clone, Debug)]
 1438struct SelectionHistoryEntry {
 1439    selections: Arc<[Selection<Anchor>]>,
 1440    select_next_state: Option<SelectNextState>,
 1441    select_prev_state: Option<SelectNextState>,
 1442    add_selections_state: Option<AddSelectionsState>,
 1443}
 1444
 1445#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1446enum SelectionHistoryMode {
 1447    #[default]
 1448    Normal,
 1449    Undoing,
 1450    Redoing,
 1451    Skipping,
 1452}
 1453
 1454#[derive(Clone, PartialEq, Eq, Hash)]
 1455struct HoveredCursor {
 1456    replica_id: ReplicaId,
 1457    selection_id: usize,
 1458}
 1459
 1460#[derive(Debug)]
 1461/// SelectionEffects controls the side-effects of updating the selection.
 1462///
 1463/// The default behaviour does "what you mostly want":
 1464/// - it pushes to the nav history if the cursor moved by >10 lines
 1465/// - it re-triggers completion requests
 1466/// - it scrolls to fit
 1467///
 1468/// You might want to modify these behaviours. For example when doing a "jump"
 1469/// like go to definition, we always want to add to nav history; but when scrolling
 1470/// in vim mode we never do.
 1471///
 1472/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1473/// move.
 1474#[derive(Clone)]
 1475pub struct SelectionEffects {
 1476    nav_history: Option<bool>,
 1477    completions: bool,
 1478    scroll: Option<Autoscroll>,
 1479}
 1480
 1481impl Default for SelectionEffects {
 1482    fn default() -> Self {
 1483        Self {
 1484            nav_history: None,
 1485            completions: true,
 1486            scroll: Some(Autoscroll::fit()),
 1487        }
 1488    }
 1489}
 1490impl SelectionEffects {
 1491    pub fn scroll(scroll: Autoscroll) -> Self {
 1492        Self {
 1493            scroll: Some(scroll),
 1494            ..Default::default()
 1495        }
 1496    }
 1497
 1498    pub fn no_scroll() -> Self {
 1499        Self {
 1500            scroll: None,
 1501            ..Default::default()
 1502        }
 1503    }
 1504
 1505    pub fn completions(self, completions: bool) -> Self {
 1506        Self {
 1507            completions,
 1508            ..self
 1509        }
 1510    }
 1511
 1512    pub fn nav_history(self, nav_history: bool) -> Self {
 1513        Self {
 1514            nav_history: Some(nav_history),
 1515            ..self
 1516        }
 1517    }
 1518}
 1519
 1520struct DeferredSelectionEffectsState {
 1521    changed: bool,
 1522    effects: SelectionEffects,
 1523    old_cursor_position: Anchor,
 1524    history_entry: SelectionHistoryEntry,
 1525}
 1526
 1527#[derive(Default)]
 1528struct SelectionHistory {
 1529    #[allow(clippy::type_complexity)]
 1530    selections_by_transaction:
 1531        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1532    mode: SelectionHistoryMode,
 1533    undo_stack: VecDeque<SelectionHistoryEntry>,
 1534    redo_stack: VecDeque<SelectionHistoryEntry>,
 1535}
 1536
 1537impl SelectionHistory {
 1538    #[track_caller]
 1539    fn insert_transaction(
 1540        &mut self,
 1541        transaction_id: TransactionId,
 1542        selections: Arc<[Selection<Anchor>]>,
 1543    ) {
 1544        if selections.is_empty() {
 1545            log::error!(
 1546                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1547                std::panic::Location::caller()
 1548            );
 1549            return;
 1550        }
 1551        self.selections_by_transaction
 1552            .insert(transaction_id, (selections, None));
 1553    }
 1554
 1555    #[allow(clippy::type_complexity)]
 1556    fn transaction(
 1557        &self,
 1558        transaction_id: TransactionId,
 1559    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1560        self.selections_by_transaction.get(&transaction_id)
 1561    }
 1562
 1563    #[allow(clippy::type_complexity)]
 1564    fn transaction_mut(
 1565        &mut self,
 1566        transaction_id: TransactionId,
 1567    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1568        self.selections_by_transaction.get_mut(&transaction_id)
 1569    }
 1570
 1571    fn push(&mut self, entry: SelectionHistoryEntry) {
 1572        if !entry.selections.is_empty() {
 1573            match self.mode {
 1574                SelectionHistoryMode::Normal => {
 1575                    self.push_undo(entry);
 1576                    self.redo_stack.clear();
 1577                }
 1578                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1579                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1580                SelectionHistoryMode::Skipping => {}
 1581            }
 1582        }
 1583    }
 1584
 1585    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1586        if self
 1587            .undo_stack
 1588            .back()
 1589            .is_none_or(|e| e.selections != entry.selections)
 1590        {
 1591            self.undo_stack.push_back(entry);
 1592            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1593                self.undo_stack.pop_front();
 1594            }
 1595        }
 1596    }
 1597
 1598    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1599        if self
 1600            .redo_stack
 1601            .back()
 1602            .is_none_or(|e| e.selections != entry.selections)
 1603        {
 1604            self.redo_stack.push_back(entry);
 1605            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1606                self.redo_stack.pop_front();
 1607            }
 1608        }
 1609    }
 1610}
 1611
 1612#[derive(Clone, Copy)]
 1613pub struct RowHighlightOptions {
 1614    pub autoscroll: bool,
 1615    pub include_gutter: bool,
 1616}
 1617
 1618impl Default for RowHighlightOptions {
 1619    fn default() -> Self {
 1620        Self {
 1621            autoscroll: Default::default(),
 1622            include_gutter: true,
 1623        }
 1624    }
 1625}
 1626
 1627struct RowHighlight {
 1628    index: usize,
 1629    range: Range<Anchor>,
 1630    color: Hsla,
 1631    options: RowHighlightOptions,
 1632    type_id: TypeId,
 1633}
 1634
 1635#[derive(Clone, Debug)]
 1636struct AddSelectionsState {
 1637    groups: Vec<AddSelectionsGroup>,
 1638}
 1639
 1640#[derive(Clone, Debug)]
 1641struct AddSelectionsGroup {
 1642    above: bool,
 1643    stack: Vec<usize>,
 1644}
 1645
 1646#[derive(Clone)]
 1647struct SelectNextState {
 1648    query: AhoCorasick,
 1649    wordwise: bool,
 1650    done: bool,
 1651}
 1652
 1653impl std::fmt::Debug for SelectNextState {
 1654    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1655        f.debug_struct(std::any::type_name::<Self>())
 1656            .field("wordwise", &self.wordwise)
 1657            .field("done", &self.done)
 1658            .finish()
 1659    }
 1660}
 1661
 1662#[derive(Debug)]
 1663struct AutocloseRegion {
 1664    selection_id: usize,
 1665    range: Range<Anchor>,
 1666    pair: BracketPair,
 1667}
 1668
 1669#[derive(Debug)]
 1670struct SnippetState {
 1671    ranges: Vec<Vec<Range<Anchor>>>,
 1672    active_index: usize,
 1673    choices: Vec<Option<Vec<String>>>,
 1674}
 1675
 1676#[doc(hidden)]
 1677pub struct RenameState {
 1678    pub range: Range<Anchor>,
 1679    pub old_name: Arc<str>,
 1680    pub editor: Entity<Editor>,
 1681    block_id: CustomBlockId,
 1682}
 1683
 1684struct InvalidationStack<T>(Vec<T>);
 1685
 1686struct RegisteredEditPredictionDelegate {
 1687    provider: Arc<dyn EditPredictionDelegateHandle>,
 1688    _subscription: Subscription,
 1689}
 1690
 1691#[derive(Debug, PartialEq, Eq)]
 1692pub struct ActiveDiagnosticGroup {
 1693    pub active_range: Range<Anchor>,
 1694    pub active_message: String,
 1695    pub group_id: usize,
 1696    pub blocks: HashSet<CustomBlockId>,
 1697}
 1698
 1699#[derive(Debug, PartialEq, Eq)]
 1700
 1701pub(crate) enum ActiveDiagnostic {
 1702    None,
 1703    All,
 1704    Group(ActiveDiagnosticGroup),
 1705}
 1706
 1707#[derive(Serialize, Deserialize, Clone, Debug)]
 1708pub struct ClipboardSelection {
 1709    /// The number of bytes in this selection.
 1710    pub len: usize,
 1711    /// Whether this was a full-line selection.
 1712    pub is_entire_line: bool,
 1713    /// The indentation of the first line when this content was originally copied.
 1714    pub first_line_indent: u32,
 1715    #[serde(default)]
 1716    pub file_path: Option<PathBuf>,
 1717    #[serde(default)]
 1718    pub line_range: Option<RangeInclusive<u32>>,
 1719}
 1720
 1721impl ClipboardSelection {
 1722    pub fn for_buffer(
 1723        len: usize,
 1724        is_entire_line: bool,
 1725        range: Range<Point>,
 1726        buffer: &MultiBufferSnapshot,
 1727        project: Option<&Entity<Project>>,
 1728        cx: &App,
 1729    ) -> Self {
 1730        let first_line_indent = buffer
 1731            .indent_size_for_line(MultiBufferRow(range.start.row))
 1732            .len;
 1733
 1734        let file_path = util::maybe!({
 1735            let project = project?.read(cx);
 1736            let file = buffer.file_at(range.start)?;
 1737            let project_path = ProjectPath {
 1738                worktree_id: file.worktree_id(cx),
 1739                path: file.path().clone(),
 1740            };
 1741            project.absolute_path(&project_path, cx)
 1742        });
 1743
 1744        let line_range = file_path.as_ref().and_then(|_| {
 1745            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1746            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1747            if start_excerpt_id == end_excerpt_id {
 1748                Some(start_point.row..=end_point.row)
 1749            } else {
 1750                None
 1751            }
 1752        });
 1753
 1754        Self {
 1755            len,
 1756            is_entire_line,
 1757            first_line_indent,
 1758            file_path,
 1759            line_range,
 1760        }
 1761    }
 1762}
 1763
 1764// selections, scroll behavior, was newest selection reversed
 1765type SelectSyntaxNodeHistoryState = (
 1766    Box<[Selection<Anchor>]>,
 1767    SelectSyntaxNodeScrollBehavior,
 1768    bool,
 1769);
 1770
 1771#[derive(Default)]
 1772struct SelectSyntaxNodeHistory {
 1773    stack: Vec<SelectSyntaxNodeHistoryState>,
 1774    // disable temporarily to allow changing selections without losing the stack
 1775    pub disable_clearing: bool,
 1776}
 1777
 1778impl SelectSyntaxNodeHistory {
 1779    pub fn try_clear(&mut self) {
 1780        if !self.disable_clearing {
 1781            self.stack.clear();
 1782        }
 1783    }
 1784
 1785    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1786        self.stack.push(selection);
 1787    }
 1788
 1789    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1790        self.stack.pop()
 1791    }
 1792}
 1793
 1794enum SelectSyntaxNodeScrollBehavior {
 1795    CursorTop,
 1796    FitSelection,
 1797    CursorBottom,
 1798}
 1799
 1800#[derive(Debug, Clone, Copy)]
 1801pub(crate) struct NavigationData {
 1802    cursor_anchor: Anchor,
 1803    cursor_position: Point,
 1804    scroll_anchor: ScrollAnchor,
 1805    scroll_top_row: u32,
 1806}
 1807
 1808#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1809pub enum GotoDefinitionKind {
 1810    Symbol,
 1811    Declaration,
 1812    Type,
 1813    Implementation,
 1814}
 1815
 1816pub enum FormatTarget {
 1817    Buffers(HashSet<Entity<Buffer>>),
 1818    Ranges(Vec<Range<MultiBufferPoint>>),
 1819}
 1820
 1821pub(crate) struct FocusedBlock {
 1822    id: BlockId,
 1823    focus_handle: WeakFocusHandle,
 1824}
 1825
 1826#[derive(Clone, Debug)]
 1827pub enum JumpData {
 1828    MultiBufferRow {
 1829        row: MultiBufferRow,
 1830        line_offset_from_top: u32,
 1831    },
 1832    MultiBufferPoint {
 1833        excerpt_id: ExcerptId,
 1834        position: Point,
 1835        anchor: text::Anchor,
 1836        line_offset_from_top: u32,
 1837    },
 1838}
 1839
 1840pub enum MultibufferSelectionMode {
 1841    First,
 1842    All,
 1843}
 1844
 1845#[derive(Clone, Copy, Debug, Default)]
 1846pub struct RewrapOptions {
 1847    pub override_language_settings: bool,
 1848    pub preserve_existing_whitespace: bool,
 1849}
 1850
 1851impl Editor {
 1852    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1853        let buffer = cx.new(|cx| Buffer::local("", cx));
 1854        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1855        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1856    }
 1857
 1858    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1859        let buffer = cx.new(|cx| Buffer::local("", cx));
 1860        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1861        Self::new(EditorMode::full(), buffer, None, window, cx)
 1862    }
 1863
 1864    pub fn auto_height(
 1865        min_lines: usize,
 1866        max_lines: usize,
 1867        window: &mut Window,
 1868        cx: &mut Context<Self>,
 1869    ) -> Self {
 1870        let buffer = cx.new(|cx| Buffer::local("", cx));
 1871        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1872        Self::new(
 1873            EditorMode::AutoHeight {
 1874                min_lines,
 1875                max_lines: Some(max_lines),
 1876            },
 1877            buffer,
 1878            None,
 1879            window,
 1880            cx,
 1881        )
 1882    }
 1883
 1884    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1885    /// The editor grows as tall as needed to fit its content.
 1886    pub fn auto_height_unbounded(
 1887        min_lines: usize,
 1888        window: &mut Window,
 1889        cx: &mut Context<Self>,
 1890    ) -> Self {
 1891        let buffer = cx.new(|cx| Buffer::local("", cx));
 1892        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1893        Self::new(
 1894            EditorMode::AutoHeight {
 1895                min_lines,
 1896                max_lines: None,
 1897            },
 1898            buffer,
 1899            None,
 1900            window,
 1901            cx,
 1902        )
 1903    }
 1904
 1905    pub fn for_buffer(
 1906        buffer: Entity<Buffer>,
 1907        project: Option<Entity<Project>>,
 1908        window: &mut Window,
 1909        cx: &mut Context<Self>,
 1910    ) -> Self {
 1911        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1912        Self::new(EditorMode::full(), buffer, project, window, cx)
 1913    }
 1914
 1915    pub fn for_multibuffer(
 1916        buffer: Entity<MultiBuffer>,
 1917        project: Option<Entity<Project>>,
 1918        window: &mut Window,
 1919        cx: &mut Context<Self>,
 1920    ) -> Self {
 1921        Self::new(EditorMode::full(), buffer, project, window, cx)
 1922    }
 1923
 1924    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1925        let mut clone = Self::new(
 1926            self.mode.clone(),
 1927            self.buffer.clone(),
 1928            self.project.clone(),
 1929            window,
 1930            cx,
 1931        );
 1932        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1933            let snapshot = display_map.snapshot(cx);
 1934            clone.display_map.update(cx, |display_map, cx| {
 1935                display_map.set_state(&snapshot, cx);
 1936            });
 1937            snapshot
 1938        });
 1939        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1940        clone.folds_did_change(cx);
 1941        clone.selections.clone_state(&self.selections);
 1942        clone
 1943            .scroll_manager
 1944            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1945        clone.searchable = self.searchable;
 1946        clone.read_only = self.read_only;
 1947        clone.buffers_with_disabled_indent_guides =
 1948            self.buffers_with_disabled_indent_guides.clone();
 1949        clone
 1950    }
 1951
 1952    pub fn new(
 1953        mode: EditorMode,
 1954        buffer: Entity<MultiBuffer>,
 1955        project: Option<Entity<Project>>,
 1956        window: &mut Window,
 1957        cx: &mut Context<Self>,
 1958    ) -> Self {
 1959        Editor::new_internal(mode, buffer, project, None, window, cx)
 1960    }
 1961
 1962    pub fn refresh_sticky_headers(
 1963        &mut self,
 1964        display_snapshot: &DisplaySnapshot,
 1965        cx: &mut Context<Editor>,
 1966    ) {
 1967        if !self.mode.is_full() {
 1968            return;
 1969        }
 1970        let multi_buffer = display_snapshot.buffer_snapshot();
 1971        let scroll_anchor = self
 1972            .scroll_manager
 1973            .native_anchor(display_snapshot, cx)
 1974            .anchor;
 1975        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 1976            return;
 1977        };
 1978        let buffer = buffer.clone();
 1979
 1980        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 1981        let max_row = buffer.max_point().row;
 1982        let start_row = buffer_visible_start.row.min(max_row);
 1983        let end_row = (buffer_visible_start.row + 10).min(max_row);
 1984
 1985        let syntax = self.style(cx).syntax.clone();
 1986        let background_task = cx.background_spawn(async move {
 1987            buffer
 1988                .outline_items_containing(
 1989                    Point::new(start_row, 0)..Point::new(end_row, 0),
 1990                    true,
 1991                    Some(syntax.as_ref()),
 1992                )
 1993                .into_iter()
 1994                .map(|outline_item| OutlineItem {
 1995                    depth: outline_item.depth,
 1996                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 1997                    source_range_for_text: Anchor::range_in_buffer(
 1998                        excerpt_id,
 1999                        outline_item.source_range_for_text,
 2000                    ),
 2001                    text: outline_item.text,
 2002                    highlight_ranges: outline_item.highlight_ranges,
 2003                    name_ranges: outline_item.name_ranges,
 2004                    body_range: outline_item
 2005                        .body_range
 2006                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2007                    annotation_range: outline_item
 2008                        .annotation_range
 2009                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2010                })
 2011                .collect()
 2012        });
 2013        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2014            let sticky_headers = background_task.await;
 2015            this.update(cx, |this, cx| {
 2016                this.sticky_headers = Some(sticky_headers);
 2017                cx.notify();
 2018            })
 2019            .ok();
 2020        });
 2021    }
 2022
 2023    fn new_internal(
 2024        mode: EditorMode,
 2025        multi_buffer: Entity<MultiBuffer>,
 2026        project: Option<Entity<Project>>,
 2027        display_map: Option<Entity<DisplayMap>>,
 2028        window: &mut Window,
 2029        cx: &mut Context<Self>,
 2030    ) -> Self {
 2031        debug_assert!(
 2032            display_map.is_none() || mode.is_minimap(),
 2033            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2034        );
 2035
 2036        let full_mode = mode.is_full();
 2037        let is_minimap = mode.is_minimap();
 2038        let diagnostics_max_severity = if full_mode {
 2039            EditorSettings::get_global(cx)
 2040                .diagnostics_max_severity
 2041                .unwrap_or(DiagnosticSeverity::Hint)
 2042        } else {
 2043            DiagnosticSeverity::Off
 2044        };
 2045        let style = window.text_style();
 2046        let font_size = style.font_size.to_pixels(window.rem_size());
 2047        let editor = cx.entity().downgrade();
 2048        let fold_placeholder = FoldPlaceholder {
 2049            constrain_width: false,
 2050            render: Arc::new(move |fold_id, fold_range, cx| {
 2051                let editor = editor.clone();
 2052                FoldPlaceholder::fold_element(fold_id, cx)
 2053                    .cursor_pointer()
 2054                    .child("")
 2055                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2056                    .on_click(move |_, _window, cx| {
 2057                        editor
 2058                            .update(cx, |editor, cx| {
 2059                                editor.unfold_ranges(
 2060                                    &[fold_range.start..fold_range.end],
 2061                                    true,
 2062                                    false,
 2063                                    cx,
 2064                                );
 2065                                cx.stop_propagation();
 2066                            })
 2067                            .ok();
 2068                    })
 2069                    .into_any()
 2070            }),
 2071            merge_adjacent: true,
 2072            ..FoldPlaceholder::default()
 2073        };
 2074        let display_map = display_map.unwrap_or_else(|| {
 2075            cx.new(|cx| {
 2076                DisplayMap::new(
 2077                    multi_buffer.clone(),
 2078                    style.font(),
 2079                    font_size,
 2080                    None,
 2081                    FILE_HEADER_HEIGHT,
 2082                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2083                    fold_placeholder,
 2084                    diagnostics_max_severity,
 2085                    cx,
 2086                )
 2087            })
 2088        });
 2089
 2090        let selections = SelectionsCollection::new();
 2091
 2092        let blink_manager = cx.new(|cx| {
 2093            let mut blink_manager = BlinkManager::new(
 2094                CURSOR_BLINK_INTERVAL,
 2095                |cx| EditorSettings::get_global(cx).cursor_blink,
 2096                cx,
 2097            );
 2098            if is_minimap {
 2099                blink_manager.disable(cx);
 2100            }
 2101            blink_manager
 2102        });
 2103
 2104        let soft_wrap_mode_override =
 2105            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2106
 2107        let mut project_subscriptions = Vec::new();
 2108        if full_mode && let Some(project) = project.as_ref() {
 2109            project_subscriptions.push(cx.subscribe_in(
 2110                project,
 2111                window,
 2112                |editor, _, event, window, cx| match event {
 2113                    project::Event::RefreshCodeLens => {
 2114                        // we always query lens with actions, without storing them, always refreshing them
 2115                    }
 2116                    project::Event::RefreshInlayHints {
 2117                        server_id,
 2118                        request_id,
 2119                    } => {
 2120                        editor.refresh_inlay_hints(
 2121                            InlayHintRefreshReason::RefreshRequested {
 2122                                server_id: *server_id,
 2123                                request_id: *request_id,
 2124                            },
 2125                            cx,
 2126                        );
 2127                    }
 2128                    project::Event::RefreshSemanticTokens {
 2129                        server_id,
 2130                        request_id,
 2131                    } => {
 2132                        editor.refresh_semantic_tokens(
 2133                            None,
 2134                            Some(RefreshForServer {
 2135                                server_id: *server_id,
 2136                                request_id: *request_id,
 2137                            }),
 2138                            cx,
 2139                        );
 2140                    }
 2141                    project::Event::LanguageServerRemoved(_) => {
 2142                        editor.registered_buffers.clear();
 2143                        editor.register_visible_buffers(cx);
 2144                        editor.invalidate_semantic_tokens(None);
 2145                        editor.refresh_runnables(None, window, cx);
 2146                        editor.update_lsp_data(None, window, cx);
 2147                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2148                    }
 2149                    project::Event::SnippetEdit(id, snippet_edits) => {
 2150                        // todo(lw): Non singletons
 2151                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2152                            let snapshot = buffer.read(cx).snapshot();
 2153                            let focus_handle = editor.focus_handle(cx);
 2154                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2155                                for (range, snippet) in snippet_edits {
 2156                                    let buffer_range =
 2157                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2158                                    editor
 2159                                        .insert_snippet(
 2160                                            &[MultiBufferOffset(buffer_range.start)
 2161                                                ..MultiBufferOffset(buffer_range.end)],
 2162                                            snippet.clone(),
 2163                                            window,
 2164                                            cx,
 2165                                        )
 2166                                        .ok();
 2167                                }
 2168                            }
 2169                        }
 2170                    }
 2171                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2172                        let buffer_id = *buffer_id;
 2173                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2174                            editor.register_buffer(buffer_id, cx);
 2175                            editor.refresh_runnables(Some(buffer_id), window, cx);
 2176                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2177                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2178                            refresh_linked_ranges(editor, window, cx);
 2179                            editor.refresh_code_actions(window, cx);
 2180                            editor.refresh_document_highlights(cx);
 2181                        }
 2182                    }
 2183
 2184                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2185                        let Some(workspace) = editor.workspace() else {
 2186                            return;
 2187                        };
 2188                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2189                        else {
 2190                            return;
 2191                        };
 2192
 2193                        if active_editor.entity_id() == cx.entity_id() {
 2194                            let entity_id = cx.entity_id();
 2195                            workspace.update(cx, |this, cx| {
 2196                                this.panes_mut()
 2197                                    .iter_mut()
 2198                                    .filter(|pane| pane.entity_id() != entity_id)
 2199                                    .for_each(|p| {
 2200                                        p.update(cx, |pane, _| {
 2201                                            pane.nav_history_mut().rename_item(
 2202                                                entity_id,
 2203                                                project_path.clone(),
 2204                                                abs_path.clone().into(),
 2205                                            );
 2206                                        })
 2207                                    });
 2208                            });
 2209
 2210                            Self::open_transaction_for_hidden_buffers(
 2211                                workspace,
 2212                                transaction.clone(),
 2213                                "Rename".to_string(),
 2214                                window,
 2215                                cx,
 2216                            );
 2217                        }
 2218                    }
 2219
 2220                    project::Event::WorkspaceEditApplied(transaction) => {
 2221                        let Some(workspace) = editor.workspace() else {
 2222                            return;
 2223                        };
 2224                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2225                        else {
 2226                            return;
 2227                        };
 2228
 2229                        if active_editor.entity_id() == cx.entity_id() {
 2230                            Self::open_transaction_for_hidden_buffers(
 2231                                workspace,
 2232                                transaction.clone(),
 2233                                "LSP Edit".to_string(),
 2234                                window,
 2235                                cx,
 2236                            );
 2237                        }
 2238                    }
 2239
 2240                    _ => {}
 2241                },
 2242            ));
 2243            if let Some(task_inventory) = project
 2244                .read(cx)
 2245                .task_store()
 2246                .read(cx)
 2247                .task_inventory()
 2248                .cloned()
 2249            {
 2250                project_subscriptions.push(cx.observe_in(
 2251                    &task_inventory,
 2252                    window,
 2253                    |editor, _, window, cx| {
 2254                        editor.refresh_runnables(None, window, cx);
 2255                    },
 2256                ));
 2257            };
 2258
 2259            project_subscriptions.push(cx.subscribe_in(
 2260                &project.read(cx).breakpoint_store(),
 2261                window,
 2262                |editor, _, event, window, cx| match event {
 2263                    BreakpointStoreEvent::ClearDebugLines => {
 2264                        editor.clear_row_highlights::<ActiveDebugLine>();
 2265                        editor.refresh_inline_values(cx);
 2266                    }
 2267                    BreakpointStoreEvent::SetDebugLine => {
 2268                        if editor.go_to_active_debug_line(window, cx) {
 2269                            cx.stop_propagation();
 2270                        }
 2271
 2272                        editor.refresh_inline_values(cx);
 2273                    }
 2274                    _ => {}
 2275                },
 2276            ));
 2277            let git_store = project.read(cx).git_store().clone();
 2278            let project = project.clone();
 2279            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2280                if let GitStoreEvent::RepositoryAdded = event {
 2281                    this.load_diff_task = Some(
 2282                        update_uncommitted_diff_for_buffer(
 2283                            cx.entity(),
 2284                            &project,
 2285                            this.buffer.read(cx).all_buffers(),
 2286                            this.buffer.clone(),
 2287                            cx,
 2288                        )
 2289                        .shared(),
 2290                    );
 2291                }
 2292            }));
 2293        }
 2294
 2295        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2296
 2297        let inlay_hint_settings =
 2298            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2299        let focus_handle = cx.focus_handle();
 2300        if !is_minimap {
 2301            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2302                .detach();
 2303            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2304                .detach();
 2305            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2306                .detach();
 2307            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2308                .detach();
 2309            cx.observe_pending_input(window, Self::observe_pending_input)
 2310                .detach();
 2311        }
 2312
 2313        let show_indent_guides =
 2314            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2315                Some(false)
 2316            } else {
 2317                None
 2318            };
 2319
 2320        let breakpoint_store = match (&mode, project.as_ref()) {
 2321            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2322            _ => None,
 2323        };
 2324
 2325        let mut code_action_providers = Vec::new();
 2326        let mut load_uncommitted_diff = None;
 2327        if let Some(project) = project.clone() {
 2328            load_uncommitted_diff = Some(
 2329                update_uncommitted_diff_for_buffer(
 2330                    cx.entity(),
 2331                    &project,
 2332                    multi_buffer.read(cx).all_buffers(),
 2333                    multi_buffer.clone(),
 2334                    cx,
 2335                )
 2336                .shared(),
 2337            );
 2338            code_action_providers.push(Rc::new(project) as Rc<_>);
 2339        }
 2340
 2341        let mut editor = Self {
 2342            focus_handle,
 2343            show_cursor_when_unfocused: false,
 2344            last_focused_descendant: None,
 2345            buffer: multi_buffer.clone(),
 2346            display_map: display_map.clone(),
 2347            placeholder_display_map: None,
 2348            selections,
 2349            scroll_manager: ScrollManager::new(cx),
 2350            columnar_selection_state: None,
 2351            add_selections_state: None,
 2352            select_next_state: None,
 2353            select_prev_state: None,
 2354            selection_history: SelectionHistory::default(),
 2355            defer_selection_effects: false,
 2356            deferred_selection_effects_state: None,
 2357            autoclose_regions: Vec::new(),
 2358            snippet_stack: InvalidationStack::default(),
 2359            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2360            ime_transaction: None,
 2361            active_diagnostics: ActiveDiagnostic::None,
 2362            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2363            inline_diagnostics_update: Task::ready(()),
 2364            inline_diagnostics: Vec::new(),
 2365            soft_wrap_mode_override,
 2366            diagnostics_max_severity,
 2367            hard_wrap: None,
 2368            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2369            semantics_provider: project
 2370                .as_ref()
 2371                .map(|project| Rc::new(project.downgrade()) as _),
 2372            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2373            project,
 2374            blink_manager: blink_manager.clone(),
 2375            show_local_selections: true,
 2376            show_scrollbars: ScrollbarAxes {
 2377                horizontal: full_mode,
 2378                vertical: full_mode,
 2379            },
 2380            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2381            offset_content: !matches!(mode, EditorMode::SingleLine),
 2382            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2383            show_gutter: full_mode,
 2384            show_line_numbers: (!full_mode).then_some(false),
 2385            use_relative_line_numbers: None,
 2386            disable_expand_excerpt_buttons: !full_mode,
 2387            delegate_expand_excerpts: false,
 2388            delegate_stage_and_restore: false,
 2389            delegate_open_excerpts: false,
 2390            enable_lsp_data: true,
 2391            enable_runnables: true,
 2392            show_git_diff_gutter: None,
 2393            show_code_actions: None,
 2394            show_runnables: None,
 2395            show_breakpoints: None,
 2396            show_diff_review_button: false,
 2397            show_wrap_guides: None,
 2398            show_indent_guides,
 2399            buffers_with_disabled_indent_guides: HashSet::default(),
 2400            highlight_order: 0,
 2401            highlighted_rows: HashMap::default(),
 2402            background_highlights: HashMap::default(),
 2403            gutter_highlights: HashMap::default(),
 2404            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2405            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2406            nav_history: None,
 2407            context_menu: RefCell::new(None),
 2408            context_menu_options: None,
 2409            mouse_context_menu: None,
 2410            completion_tasks: Vec::new(),
 2411            inline_blame_popover: None,
 2412            inline_blame_popover_show_task: None,
 2413            signature_help_state: SignatureHelpState::default(),
 2414            auto_signature_help: None,
 2415            find_all_references_task_sources: Vec::new(),
 2416            next_completion_id: 0,
 2417            next_inlay_id: 0,
 2418            code_action_providers,
 2419            available_code_actions: None,
 2420            code_actions_task: None,
 2421            quick_selection_highlight_task: None,
 2422            debounced_selection_highlight_task: None,
 2423            debounced_selection_highlight_complete: false,
 2424            document_highlights_task: None,
 2425            linked_editing_range_task: None,
 2426            pending_rename: None,
 2427            searchable: !is_minimap,
 2428            cursor_shape: EditorSettings::get_global(cx)
 2429                .cursor_shape
 2430                .unwrap_or_default(),
 2431            cursor_offset_on_selection: false,
 2432            current_line_highlight: None,
 2433            autoindent_mode: Some(AutoindentMode::EachLine),
 2434            collapse_matches: false,
 2435            workspace: None,
 2436            input_enabled: !is_minimap,
 2437            expects_character_input: !is_minimap,
 2438            use_modal_editing: full_mode,
 2439            read_only: is_minimap,
 2440            use_autoclose: true,
 2441            use_auto_surround: true,
 2442            auto_replace_emoji_shortcode: false,
 2443            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2444            leader_id: None,
 2445            remote_id: None,
 2446            hover_state: HoverState::default(),
 2447            pending_mouse_down: None,
 2448            prev_pressure_stage: None,
 2449            hovered_link_state: None,
 2450            edit_prediction_provider: None,
 2451            active_edit_prediction: None,
 2452            stale_edit_prediction_in_menu: None,
 2453            edit_prediction_preview: EditPredictionPreview::Inactive {
 2454                released_too_fast: false,
 2455            },
 2456            inline_diagnostics_enabled: full_mode,
 2457            diagnostics_enabled: full_mode,
 2458            word_completions_enabled: full_mode,
 2459            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2460            gutter_hovered: false,
 2461            pixel_position_of_newest_cursor: None,
 2462            last_bounds: None,
 2463            last_position_map: None,
 2464            expect_bounds_change: None,
 2465            gutter_dimensions: GutterDimensions::default(),
 2466            style: None,
 2467            show_cursor_names: false,
 2468            hovered_cursors: HashMap::default(),
 2469            next_editor_action_id: EditorActionId::default(),
 2470            editor_actions: Rc::default(),
 2471            edit_predictions_hidden_for_vim_mode: false,
 2472            show_edit_predictions_override: None,
 2473            show_completions_on_input_override: None,
 2474            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2475            edit_prediction_settings: EditPredictionSettings::Disabled,
 2476            edit_prediction_indent_conflict: false,
 2477            edit_prediction_requires_modifier_in_indent_conflict: true,
 2478            custom_context_menu: None,
 2479            show_git_blame_gutter: false,
 2480            show_git_blame_inline: false,
 2481            show_selection_menu: None,
 2482            show_git_blame_inline_delay_task: None,
 2483            git_blame_inline_enabled: full_mode
 2484                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2485            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2486            buffer_serialization: is_minimap.not().then(|| {
 2487                BufferSerialization::new(
 2488                    ProjectSettings::get_global(cx)
 2489                        .session
 2490                        .restore_unsaved_buffers,
 2491                )
 2492            }),
 2493            blame: None,
 2494            blame_subscription: None,
 2495
 2496            breakpoint_store,
 2497            gutter_breakpoint_indicator: (None, None),
 2498            gutter_diff_review_indicator: (None, None),
 2499            diff_review_drag_state: None,
 2500            diff_review_overlays: Vec::new(),
 2501            stored_review_comments: Vec::new(),
 2502            next_review_comment_id: 0,
 2503            hovered_diff_hunk_row: None,
 2504            _subscriptions: (!is_minimap)
 2505                .then(|| {
 2506                    vec![
 2507                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2508                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2509                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2510                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2511                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2512                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2513                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2514                        cx.observe_window_activation(window, |editor, window, cx| {
 2515                            let active = window.is_window_active();
 2516                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2517                                if active {
 2518                                    blink_manager.enable(cx);
 2519                                } else {
 2520                                    blink_manager.disable(cx);
 2521                                }
 2522                            });
 2523                            if active {
 2524                                editor.show_mouse_cursor(cx);
 2525                            }
 2526                        }),
 2527                    ]
 2528                })
 2529                .unwrap_or_default(),
 2530            runnables: RunnableData::new(),
 2531            pull_diagnostics_task: Task::ready(()),
 2532            colors: None,
 2533            refresh_colors_task: Task::ready(()),
 2534            use_document_folding_ranges: false,
 2535            refresh_folding_ranges_task: Task::ready(()),
 2536            inlay_hints: None,
 2537            next_color_inlay_id: 0,
 2538            post_scroll_update: Task::ready(()),
 2539            linked_edit_ranges: Default::default(),
 2540            in_project_search: false,
 2541            previous_search_ranges: None,
 2542            breadcrumb_header: None,
 2543            focused_block: None,
 2544            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2545            addons: HashMap::default(),
 2546            registered_buffers: HashMap::default(),
 2547            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2548            selection_mark_mode: false,
 2549            toggle_fold_multiple_buffers: Task::ready(()),
 2550            serialize_selections: Task::ready(()),
 2551            serialize_folds: Task::ready(()),
 2552            text_style_refinement: None,
 2553            load_diff_task: load_uncommitted_diff,
 2554            temporary_diff_override: false,
 2555            mouse_cursor_hidden: false,
 2556            minimap: None,
 2557            hide_mouse_mode: EditorSettings::get_global(cx)
 2558                .hide_mouse
 2559                .unwrap_or_default(),
 2560            change_list: ChangeList::new(),
 2561            mode,
 2562            selection_drag_state: SelectionDragState::None,
 2563            folding_newlines: Task::ready(()),
 2564            lookup_key: None,
 2565            select_next_is_case_sensitive: None,
 2566            on_local_selections_changed: None,
 2567            suppress_selection_callback: false,
 2568            applicable_language_settings: HashMap::default(),
 2569            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2570            accent_data: None,
 2571            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2572            number_deleted_lines: false,
 2573            refresh_matching_bracket_highlights_task: Task::ready(()),
 2574            refresh_document_symbols_task: Task::ready(()).shared(),
 2575            lsp_document_symbols: HashMap::default(),
 2576            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2577            outline_symbols_at_cursor: None,
 2578            sticky_headers_task: Task::ready(()),
 2579            sticky_headers: None,
 2580            colorize_brackets_task: Task::ready(()),
 2581        };
 2582
 2583        if is_minimap {
 2584            return editor;
 2585        }
 2586
 2587        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2588        editor.accent_data = editor.fetch_accent_data(cx);
 2589
 2590        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2591            editor
 2592                ._subscriptions
 2593                .push(cx.observe(breakpoints, |_, _, cx| {
 2594                    cx.notify();
 2595                }));
 2596        }
 2597        editor._subscriptions.extend(project_subscriptions);
 2598
 2599        editor._subscriptions.push(cx.subscribe_in(
 2600            &cx.entity(),
 2601            window,
 2602            |editor, _, e: &EditorEvent, window, cx| match e {
 2603                EditorEvent::ScrollPositionChanged { local, .. } => {
 2604                    if *local {
 2605                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2606                        editor.inline_blame_popover.take();
 2607                        let snapshot = editor.snapshot(window, cx);
 2608                        let new_anchor = editor
 2609                            .scroll_manager
 2610                            .native_anchor(&snapshot.display_snapshot, cx);
 2611                        editor.update_restoration_data(cx, move |data| {
 2612                            data.scroll_position = (
 2613                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2614                                new_anchor.offset,
 2615                            );
 2616                        });
 2617
 2618                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2619                            cx.background_executor()
 2620                                .timer(Duration::from_millis(50))
 2621                                .await;
 2622                            editor
 2623                                .update_in(cx, |editor, window, cx| {
 2624                                    editor.update_data_on_scroll(window, cx)
 2625                                })
 2626                                .ok();
 2627                        });
 2628                    }
 2629                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2630                }
 2631                EditorEvent::Edited { .. } => {
 2632                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2633                        .map(|vim_mode| vim_mode.0)
 2634                        .unwrap_or(false);
 2635                    if !vim_mode {
 2636                        let display_map = editor.display_snapshot(cx);
 2637                        let selections = editor.selections.all_adjusted_display(&display_map);
 2638                        let pop_state = editor
 2639                            .change_list
 2640                            .last()
 2641                            .map(|previous| {
 2642                                previous.len() == selections.len()
 2643                                    && previous.iter().enumerate().all(|(ix, p)| {
 2644                                        p.to_display_point(&display_map).row()
 2645                                            == selections[ix].head().row()
 2646                                    })
 2647                            })
 2648                            .unwrap_or(false);
 2649                        let new_positions = selections
 2650                            .into_iter()
 2651                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2652                            .collect();
 2653                        editor
 2654                            .change_list
 2655                            .push_to_change_list(pop_state, new_positions);
 2656                    }
 2657                }
 2658                _ => (),
 2659            },
 2660        ));
 2661
 2662        if let Some(dap_store) = editor
 2663            .project
 2664            .as_ref()
 2665            .map(|project| project.read(cx).dap_store())
 2666        {
 2667            let weak_editor = cx.weak_entity();
 2668
 2669            editor
 2670                ._subscriptions
 2671                .push(
 2672                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2673                        let session_entity = cx.entity();
 2674                        weak_editor
 2675                            .update(cx, |editor, cx| {
 2676                                editor._subscriptions.push(
 2677                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2678                                );
 2679                            })
 2680                            .ok();
 2681                    }),
 2682                );
 2683
 2684            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2685                editor
 2686                    ._subscriptions
 2687                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2688            }
 2689        }
 2690
 2691        // skip adding the initial selection to selection history
 2692        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2693        editor.end_selection(window, cx);
 2694        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2695
 2696        editor.scroll_manager.show_scrollbars(window, cx);
 2697        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2698
 2699        if full_mode {
 2700            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2701            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2702
 2703            if editor.git_blame_inline_enabled {
 2704                editor.start_git_blame_inline(false, window, cx);
 2705            }
 2706
 2707            editor.go_to_active_debug_line(window, cx);
 2708
 2709            editor.minimap =
 2710                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2711            editor.colors = Some(LspColorData::new(cx));
 2712            editor.use_document_folding_ranges = true;
 2713            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2714
 2715            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2716                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2717            }
 2718            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2719        }
 2720
 2721        editor
 2722    }
 2723
 2724    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2725        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2726    }
 2727
 2728    pub fn deploy_mouse_context_menu(
 2729        &mut self,
 2730        position: gpui::Point<Pixels>,
 2731        context_menu: Entity<ContextMenu>,
 2732        window: &mut Window,
 2733        cx: &mut Context<Self>,
 2734    ) {
 2735        self.mouse_context_menu = Some(MouseContextMenu::new(
 2736            self,
 2737            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2738            context_menu,
 2739            window,
 2740            cx,
 2741        ));
 2742    }
 2743
 2744    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2745        self.mouse_context_menu
 2746            .as_ref()
 2747            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2748    }
 2749
 2750    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2751        if self
 2752            .selections
 2753            .pending_anchor()
 2754            .is_some_and(|pending_selection| {
 2755                let snapshot = self.buffer().read(cx).snapshot(cx);
 2756                pending_selection.range().includes(range, &snapshot)
 2757            })
 2758        {
 2759            return true;
 2760        }
 2761
 2762        self.selections
 2763            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2764            .into_iter()
 2765            .any(|selection| {
 2766                // This is needed to cover a corner case, if we just check for an existing
 2767                // selection in the fold range, having a cursor at the start of the fold
 2768                // marks it as selected. Non-empty selections don't cause this.
 2769                let length = selection.end - selection.start;
 2770                length > 0
 2771            })
 2772    }
 2773
 2774    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2775        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2776    }
 2777
 2778    fn key_context_internal(
 2779        &self,
 2780        has_active_edit_prediction: bool,
 2781        window: &mut Window,
 2782        cx: &mut App,
 2783    ) -> KeyContext {
 2784        let mut key_context = KeyContext::new_with_defaults();
 2785        key_context.add("Editor");
 2786        let mode = match self.mode {
 2787            EditorMode::SingleLine => "single_line",
 2788            EditorMode::AutoHeight { .. } => "auto_height",
 2789            EditorMode::Minimap { .. } => "minimap",
 2790            EditorMode::Full { .. } => "full",
 2791        };
 2792
 2793        if EditorSettings::jupyter_enabled(cx) {
 2794            key_context.add("jupyter");
 2795        }
 2796
 2797        key_context.set("mode", mode);
 2798        if self.pending_rename.is_some() {
 2799            key_context.add("renaming");
 2800        }
 2801
 2802        if let Some(snippet_stack) = self.snippet_stack.last() {
 2803            key_context.add("in_snippet");
 2804
 2805            if snippet_stack.active_index > 0 {
 2806                key_context.add("has_previous_tabstop");
 2807            }
 2808
 2809            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2810                key_context.add("has_next_tabstop");
 2811            }
 2812        }
 2813
 2814        match self.context_menu.borrow().as_ref() {
 2815            Some(CodeContextMenu::Completions(menu)) => {
 2816                if menu.visible() {
 2817                    key_context.add("menu");
 2818                    key_context.add("showing_completions");
 2819                }
 2820            }
 2821            Some(CodeContextMenu::CodeActions(menu)) => {
 2822                if menu.visible() {
 2823                    key_context.add("menu");
 2824                    key_context.add("showing_code_actions")
 2825                }
 2826            }
 2827            None => {}
 2828        }
 2829
 2830        if self.signature_help_state.has_multiple_signatures() {
 2831            key_context.add("showing_signature_help");
 2832        }
 2833
 2834        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2835        if !self.focus_handle(cx).contains_focused(window, cx)
 2836            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2837        {
 2838            for addon in self.addons.values() {
 2839                addon.extend_key_context(&mut key_context, cx)
 2840            }
 2841        }
 2842
 2843        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2844            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2845                Some(
 2846                    file.full_path(cx)
 2847                        .extension()?
 2848                        .to_string_lossy()
 2849                        .to_lowercase(),
 2850                )
 2851            }) {
 2852                key_context.set("extension", extension);
 2853            }
 2854        } else {
 2855            key_context.add("multibuffer");
 2856        }
 2857
 2858        if has_active_edit_prediction {
 2859            if self.edit_prediction_in_conflict() {
 2860                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2861            } else {
 2862                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2863                key_context.add("copilot_suggestion");
 2864            }
 2865        }
 2866
 2867        if self.selection_mark_mode {
 2868            key_context.add("selection_mode");
 2869        }
 2870
 2871        let disjoint = self.selections.disjoint_anchors();
 2872        let snapshot = self.snapshot(window, cx);
 2873        let snapshot = snapshot.buffer_snapshot();
 2874        if self.mode == EditorMode::SingleLine
 2875            && let [selection] = disjoint
 2876            && selection.start == selection.end
 2877            && selection.end.to_offset(snapshot) == snapshot.len()
 2878        {
 2879            key_context.add("end_of_input");
 2880        }
 2881
 2882        if self.has_any_expanded_diff_hunks(cx) {
 2883            key_context.add("diffs_expanded");
 2884        }
 2885
 2886        key_context
 2887    }
 2888
 2889    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2890        self.last_bounds.as_ref()
 2891    }
 2892
 2893    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2894        if self.mouse_cursor_hidden {
 2895            self.mouse_cursor_hidden = false;
 2896            cx.notify();
 2897        }
 2898    }
 2899
 2900    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2901        let hide_mouse_cursor = match origin {
 2902            HideMouseCursorOrigin::TypingAction => {
 2903                matches!(
 2904                    self.hide_mouse_mode,
 2905                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2906                )
 2907            }
 2908            HideMouseCursorOrigin::MovementAction => {
 2909                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2910            }
 2911        };
 2912        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2913            self.mouse_cursor_hidden = hide_mouse_cursor;
 2914            cx.notify();
 2915        }
 2916    }
 2917
 2918    pub fn edit_prediction_in_conflict(&self) -> bool {
 2919        if !self.show_edit_predictions_in_menu() {
 2920            return false;
 2921        }
 2922
 2923        let showing_completions = self
 2924            .context_menu
 2925            .borrow()
 2926            .as_ref()
 2927            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2928
 2929        showing_completions
 2930            || self.edit_prediction_requires_modifier()
 2931            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2932            // bindings to insert tab characters.
 2933            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2934    }
 2935
 2936    pub fn accept_edit_prediction_keybind(
 2937        &self,
 2938        granularity: EditPredictionGranularity,
 2939        window: &mut Window,
 2940        cx: &mut App,
 2941    ) -> AcceptEditPredictionBinding {
 2942        let key_context = self.key_context_internal(true, window, cx);
 2943        let in_conflict = self.edit_prediction_in_conflict();
 2944
 2945        let bindings =
 2946            match granularity {
 2947                EditPredictionGranularity::Word => window
 2948                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2949                EditPredictionGranularity::Line => window
 2950                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2951                EditPredictionGranularity::Full => {
 2952                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2953                }
 2954            };
 2955
 2956        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2957            !in_conflict
 2958                || binding
 2959                    .keystrokes()
 2960                    .first()
 2961                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2962        }))
 2963    }
 2964
 2965    pub fn new_file(
 2966        workspace: &mut Workspace,
 2967        _: &workspace::NewFile,
 2968        window: &mut Window,
 2969        cx: &mut Context<Workspace>,
 2970    ) {
 2971        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 2972            "Failed to create buffer",
 2973            window,
 2974            cx,
 2975            |e, _, _| match e.error_code() {
 2976                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2977                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2978                e.error_tag("required").unwrap_or("the latest version")
 2979            )),
 2980                _ => None,
 2981            },
 2982        );
 2983    }
 2984
 2985    pub fn new_in_workspace(
 2986        workspace: &mut Workspace,
 2987        window: &mut Window,
 2988        cx: &mut Context<Workspace>,
 2989    ) -> Task<Result<Entity<Editor>>> {
 2990        let project = workspace.project().clone();
 2991        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 2992
 2993        cx.spawn_in(window, async move |workspace, cx| {
 2994            let buffer = create.await?;
 2995            workspace.update_in(cx, |workspace, window, cx| {
 2996                let editor =
 2997                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 2998                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 2999                editor
 3000            })
 3001        })
 3002    }
 3003
 3004    fn new_file_vertical(
 3005        workspace: &mut Workspace,
 3006        _: &workspace::NewFileSplitVertical,
 3007        window: &mut Window,
 3008        cx: &mut Context<Workspace>,
 3009    ) {
 3010        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3011    }
 3012
 3013    fn new_file_horizontal(
 3014        workspace: &mut Workspace,
 3015        _: &workspace::NewFileSplitHorizontal,
 3016        window: &mut Window,
 3017        cx: &mut Context<Workspace>,
 3018    ) {
 3019        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3020    }
 3021
 3022    fn new_file_split(
 3023        workspace: &mut Workspace,
 3024        action: &workspace::NewFileSplit,
 3025        window: &mut Window,
 3026        cx: &mut Context<Workspace>,
 3027    ) {
 3028        Self::new_file_in_direction(workspace, action.0, window, cx)
 3029    }
 3030
 3031    fn new_file_in_direction(
 3032        workspace: &mut Workspace,
 3033        direction: SplitDirection,
 3034        window: &mut Window,
 3035        cx: &mut Context<Workspace>,
 3036    ) {
 3037        let project = workspace.project().clone();
 3038        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3039
 3040        cx.spawn_in(window, async move |workspace, cx| {
 3041            let buffer = create.await?;
 3042            workspace.update_in(cx, move |workspace, window, cx| {
 3043                workspace.split_item(
 3044                    direction,
 3045                    Box::new(
 3046                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3047                    ),
 3048                    window,
 3049                    cx,
 3050                )
 3051            })?;
 3052            anyhow::Ok(())
 3053        })
 3054        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3055            match e.error_code() {
 3056                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3057                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3058                e.error_tag("required").unwrap_or("the latest version")
 3059            )),
 3060                _ => None,
 3061            }
 3062        });
 3063    }
 3064
 3065    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3066        self.leader_id
 3067    }
 3068
 3069    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3070        &self.buffer
 3071    }
 3072
 3073    pub fn project(&self) -> Option<&Entity<Project>> {
 3074        self.project.as_ref()
 3075    }
 3076
 3077    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3078        self.workspace.as_ref()?.0.upgrade()
 3079    }
 3080
 3081    /// Detaches a task and shows an error notification in the workspace if available,
 3082    /// otherwise just logs the error.
 3083    pub fn detach_and_notify_err<R, E>(
 3084        &self,
 3085        task: Task<Result<R, E>>,
 3086        window: &mut Window,
 3087        cx: &mut App,
 3088    ) where
 3089        E: std::fmt::Debug + std::fmt::Display + 'static,
 3090        R: 'static,
 3091    {
 3092        if let Some(workspace) = self.workspace() {
 3093            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3094        } else {
 3095            task.detach_and_log_err(cx);
 3096        }
 3097    }
 3098
 3099    /// Returns the workspace serialization ID if this editor should be serialized.
 3100    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3101        self.workspace
 3102            .as_ref()
 3103            .filter(|_| self.should_serialize_buffer())
 3104            .and_then(|workspace| workspace.1)
 3105    }
 3106
 3107    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3108        self.buffer().read(cx).title(cx)
 3109    }
 3110
 3111    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3112        let git_blame_gutter_max_author_length = self
 3113            .render_git_blame_gutter(cx)
 3114            .then(|| {
 3115                if let Some(blame) = self.blame.as_ref() {
 3116                    let max_author_length =
 3117                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3118                    Some(max_author_length)
 3119                } else {
 3120                    None
 3121                }
 3122            })
 3123            .flatten();
 3124
 3125        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3126
 3127        EditorSnapshot {
 3128            mode: self.mode.clone(),
 3129            show_gutter: self.show_gutter,
 3130            offset_content: self.offset_content,
 3131            show_line_numbers: self.show_line_numbers,
 3132            number_deleted_lines: self.number_deleted_lines,
 3133            show_git_diff_gutter: self.show_git_diff_gutter,
 3134            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3135            show_code_actions: self.show_code_actions,
 3136            show_runnables: self.show_runnables,
 3137            show_breakpoints: self.show_breakpoints,
 3138            git_blame_gutter_max_author_length,
 3139            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3140            display_snapshot,
 3141            placeholder_display_snapshot: self
 3142                .placeholder_display_map
 3143                .as_ref()
 3144                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3145            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3146            is_focused: self.focus_handle.is_focused(window),
 3147            current_line_highlight: self
 3148                .current_line_highlight
 3149                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3150            gutter_hovered: self.gutter_hovered,
 3151        }
 3152    }
 3153
 3154    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3155        self.buffer.read(cx).language_at(point, cx)
 3156    }
 3157
 3158    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3159        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3160    }
 3161
 3162    pub fn active_excerpt(
 3163        &self,
 3164        cx: &App,
 3165    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3166        self.buffer
 3167            .read(cx)
 3168            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3169    }
 3170
 3171    pub fn mode(&self) -> &EditorMode {
 3172        &self.mode
 3173    }
 3174
 3175    pub fn set_mode(&mut self, mode: EditorMode) {
 3176        self.mode = mode;
 3177    }
 3178
 3179    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3180        self.collaboration_hub.as_deref()
 3181    }
 3182
 3183    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3184        self.collaboration_hub = Some(hub);
 3185    }
 3186
 3187    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3188        self.in_project_search = in_project_search;
 3189    }
 3190
 3191    pub fn set_custom_context_menu(
 3192        &mut self,
 3193        f: impl 'static
 3194        + Fn(
 3195            &mut Self,
 3196            DisplayPoint,
 3197            &mut Window,
 3198            &mut Context<Self>,
 3199        ) -> Option<Entity<ui::ContextMenu>>,
 3200    ) {
 3201        self.custom_context_menu = Some(Box::new(f))
 3202    }
 3203
 3204    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3205        self.completion_provider = provider;
 3206    }
 3207
 3208    #[cfg(any(test, feature = "test-support"))]
 3209    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3210        self.completion_provider.clone()
 3211    }
 3212
 3213    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3214        self.semantics_provider.clone()
 3215    }
 3216
 3217    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3218        self.semantics_provider = provider;
 3219    }
 3220
 3221    pub fn set_edit_prediction_provider<T>(
 3222        &mut self,
 3223        provider: Option<Entity<T>>,
 3224        window: &mut Window,
 3225        cx: &mut Context<Self>,
 3226    ) where
 3227        T: EditPredictionDelegate,
 3228    {
 3229        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3230            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3231                if this.focus_handle.is_focused(window) {
 3232                    this.update_visible_edit_prediction(window, cx);
 3233                }
 3234            }),
 3235            provider: Arc::new(provider),
 3236        });
 3237        self.update_edit_prediction_settings(cx);
 3238        self.refresh_edit_prediction(false, false, window, cx);
 3239    }
 3240
 3241    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3242        self.placeholder_display_map
 3243            .as_ref()
 3244            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3245    }
 3246
 3247    pub fn set_placeholder_text(
 3248        &mut self,
 3249        placeholder_text: &str,
 3250        window: &mut Window,
 3251        cx: &mut Context<Self>,
 3252    ) {
 3253        let multibuffer = cx
 3254            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3255
 3256        let style = window.text_style();
 3257
 3258        self.placeholder_display_map = Some(cx.new(|cx| {
 3259            DisplayMap::new(
 3260                multibuffer,
 3261                style.font(),
 3262                style.font_size.to_pixels(window.rem_size()),
 3263                None,
 3264                FILE_HEADER_HEIGHT,
 3265                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3266                Default::default(),
 3267                DiagnosticSeverity::Off,
 3268                cx,
 3269            )
 3270        }));
 3271        cx.notify();
 3272    }
 3273
 3274    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3275        self.cursor_shape = cursor_shape;
 3276
 3277        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3278        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3279
 3280        cx.notify();
 3281    }
 3282
 3283    pub fn cursor_shape(&self) -> CursorShape {
 3284        self.cursor_shape
 3285    }
 3286
 3287    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3288        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3289    }
 3290
 3291    pub fn set_current_line_highlight(
 3292        &mut self,
 3293        current_line_highlight: Option<CurrentLineHighlight>,
 3294    ) {
 3295        self.current_line_highlight = current_line_highlight;
 3296    }
 3297
 3298    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3299        self.collapse_matches = collapse_matches;
 3300    }
 3301
 3302    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3303        if self.collapse_matches {
 3304            return range.start..range.start;
 3305        }
 3306        range.clone()
 3307    }
 3308
 3309    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3310        self.display_map.read(cx).clip_at_line_ends
 3311    }
 3312
 3313    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3314        if self.display_map.read(cx).clip_at_line_ends != clip {
 3315            self.display_map
 3316                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3317        }
 3318    }
 3319
 3320    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3321        self.input_enabled = input_enabled;
 3322    }
 3323
 3324    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3325        self.expects_character_input = expects_character_input;
 3326    }
 3327
 3328    pub fn set_edit_predictions_hidden_for_vim_mode(
 3329        &mut self,
 3330        hidden: bool,
 3331        window: &mut Window,
 3332        cx: &mut Context<Self>,
 3333    ) {
 3334        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3335            self.edit_predictions_hidden_for_vim_mode = hidden;
 3336            if hidden {
 3337                self.update_visible_edit_prediction(window, cx);
 3338            } else {
 3339                self.refresh_edit_prediction(true, false, window, cx);
 3340            }
 3341        }
 3342    }
 3343
 3344    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3345        self.menu_edit_predictions_policy = value;
 3346    }
 3347
 3348    pub fn set_autoindent(&mut self, autoindent: bool) {
 3349        if autoindent {
 3350            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3351        } else {
 3352            self.autoindent_mode = None;
 3353        }
 3354    }
 3355
 3356    pub fn capability(&self, cx: &App) -> Capability {
 3357        if self.read_only {
 3358            Capability::ReadOnly
 3359        } else {
 3360            self.buffer.read(cx).capability()
 3361        }
 3362    }
 3363
 3364    pub fn read_only(&self, cx: &App) -> bool {
 3365        self.read_only || self.buffer.read(cx).read_only()
 3366    }
 3367
 3368    pub fn set_read_only(&mut self, read_only: bool) {
 3369        self.read_only = read_only;
 3370    }
 3371
 3372    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3373        self.use_autoclose = autoclose;
 3374    }
 3375
 3376    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3377        self.use_auto_surround = auto_surround;
 3378    }
 3379
 3380    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3381        self.auto_replace_emoji_shortcode = auto_replace;
 3382    }
 3383
 3384    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3385        self.buffer_serialization = should_serialize.then(|| {
 3386            BufferSerialization::new(
 3387                ProjectSettings::get_global(cx)
 3388                    .session
 3389                    .restore_unsaved_buffers,
 3390            )
 3391        })
 3392    }
 3393
 3394    fn should_serialize_buffer(&self) -> bool {
 3395        self.buffer_serialization.is_some()
 3396    }
 3397
 3398    pub fn toggle_edit_predictions(
 3399        &mut self,
 3400        _: &ToggleEditPrediction,
 3401        window: &mut Window,
 3402        cx: &mut Context<Self>,
 3403    ) {
 3404        if self.show_edit_predictions_override.is_some() {
 3405            self.set_show_edit_predictions(None, window, cx);
 3406        } else {
 3407            let show_edit_predictions = !self.edit_predictions_enabled();
 3408            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3409        }
 3410    }
 3411
 3412    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3413        self.show_completions_on_input_override = show_completions_on_input;
 3414    }
 3415
 3416    pub fn set_show_edit_predictions(
 3417        &mut self,
 3418        show_edit_predictions: Option<bool>,
 3419        window: &mut Window,
 3420        cx: &mut Context<Self>,
 3421    ) {
 3422        self.show_edit_predictions_override = show_edit_predictions;
 3423        self.update_edit_prediction_settings(cx);
 3424
 3425        if let Some(false) = show_edit_predictions {
 3426            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3427        } else {
 3428            self.refresh_edit_prediction(false, true, window, cx);
 3429        }
 3430    }
 3431
 3432    fn edit_predictions_disabled_in_scope(
 3433        &self,
 3434        buffer: &Entity<Buffer>,
 3435        buffer_position: language::Anchor,
 3436        cx: &App,
 3437    ) -> bool {
 3438        let snapshot = buffer.read(cx).snapshot();
 3439        let settings = snapshot.settings_at(buffer_position, cx);
 3440
 3441        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3442            return false;
 3443        };
 3444
 3445        scope.override_name().is_some_and(|scope_name| {
 3446            settings
 3447                .edit_predictions_disabled_in
 3448                .iter()
 3449                .any(|s| s == scope_name)
 3450        })
 3451    }
 3452
 3453    pub fn set_use_modal_editing(&mut self, to: bool) {
 3454        self.use_modal_editing = to;
 3455    }
 3456
 3457    pub fn use_modal_editing(&self) -> bool {
 3458        self.use_modal_editing
 3459    }
 3460
 3461    fn selections_did_change(
 3462        &mut self,
 3463        local: bool,
 3464        old_cursor_position: &Anchor,
 3465        effects: SelectionEffects,
 3466        window: &mut Window,
 3467        cx: &mut Context<Self>,
 3468    ) {
 3469        window.invalidate_character_coordinates();
 3470
 3471        // Copy selections to primary selection buffer
 3472        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3473        if local {
 3474            let selections = self
 3475                .selections
 3476                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3477            let buffer_handle = self.buffer.read(cx).read(cx);
 3478
 3479            let mut text = String::new();
 3480            for (index, selection) in selections.iter().enumerate() {
 3481                let text_for_selection = buffer_handle
 3482                    .text_for_range(selection.start..selection.end)
 3483                    .collect::<String>();
 3484
 3485                text.push_str(&text_for_selection);
 3486                if index != selections.len() - 1 {
 3487                    text.push('\n');
 3488                }
 3489            }
 3490
 3491            if !text.is_empty() {
 3492                cx.write_to_primary(ClipboardItem::new_string(text));
 3493            }
 3494        }
 3495
 3496        let selection_anchors = self.selections.disjoint_anchors_arc();
 3497
 3498        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3499            self.buffer.update(cx, |buffer, cx| {
 3500                buffer.set_active_selections(
 3501                    &selection_anchors,
 3502                    self.selections.line_mode(),
 3503                    self.cursor_shape,
 3504                    cx,
 3505                )
 3506            });
 3507        }
 3508        let display_map = self
 3509            .display_map
 3510            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3511        let buffer = display_map.buffer_snapshot();
 3512        if self.selections.count() == 1 {
 3513            self.add_selections_state = None;
 3514        }
 3515        self.select_next_state = None;
 3516        self.select_prev_state = None;
 3517        self.select_syntax_node_history.try_clear();
 3518        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3519        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3520        self.take_rename(false, window, cx);
 3521
 3522        let newest_selection = self.selections.newest_anchor();
 3523        let new_cursor_position = newest_selection.head();
 3524        let selection_start = newest_selection.start;
 3525
 3526        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3527            self.push_to_nav_history(
 3528                *old_cursor_position,
 3529                Some(new_cursor_position.to_point(buffer)),
 3530                false,
 3531                effects.nav_history == Some(true),
 3532                cx,
 3533            );
 3534        }
 3535
 3536        if local {
 3537            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3538                self.register_buffer(buffer_id, cx);
 3539            }
 3540
 3541            let mut context_menu = self.context_menu.borrow_mut();
 3542            let completion_menu = match context_menu.as_ref() {
 3543                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3544                Some(CodeContextMenu::CodeActions(_)) => {
 3545                    *context_menu = None;
 3546                    None
 3547                }
 3548                None => None,
 3549            };
 3550            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3551            drop(context_menu);
 3552
 3553            if effects.completions
 3554                && let Some(completion_position) = completion_position
 3555            {
 3556                let start_offset = selection_start.to_offset(buffer);
 3557                let position_matches = start_offset == completion_position.to_offset(buffer);
 3558                let continue_showing = if let Some((snap, ..)) =
 3559                    buffer.point_to_buffer_offset(completion_position)
 3560                    && !snap.capability.editable()
 3561                {
 3562                    false
 3563                } else if position_matches {
 3564                    if self.snippet_stack.is_empty() {
 3565                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3566                            == Some(CharKind::Word)
 3567                    } else {
 3568                        // Snippet choices can be shown even when the cursor is in whitespace.
 3569                        // Dismissing the menu with actions like backspace is handled by
 3570                        // invalidation regions.
 3571                        true
 3572                    }
 3573                } else {
 3574                    false
 3575                };
 3576
 3577                if continue_showing {
 3578                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3579                } else {
 3580                    self.hide_context_menu(window, cx);
 3581                }
 3582            }
 3583
 3584            hide_hover(self, cx);
 3585
 3586            if old_cursor_position.to_display_point(&display_map).row()
 3587                != new_cursor_position.to_display_point(&display_map).row()
 3588            {
 3589                self.available_code_actions.take();
 3590            }
 3591            self.refresh_code_actions(window, cx);
 3592            self.refresh_document_highlights(cx);
 3593            refresh_linked_ranges(self, window, cx);
 3594
 3595            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3596            self.refresh_matching_bracket_highlights(&display_map, cx);
 3597            self.refresh_outline_symbols_at_cursor(cx);
 3598            self.update_visible_edit_prediction(window, cx);
 3599            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3600            self.inline_blame_popover.take();
 3601            if self.git_blame_inline_enabled {
 3602                self.start_inline_blame_timer(window, cx);
 3603            }
 3604        }
 3605
 3606        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3607
 3608        if local && !self.suppress_selection_callback {
 3609            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3610                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3611                callback(cursor_position, window, cx);
 3612            }
 3613        }
 3614
 3615        cx.emit(EditorEvent::SelectionsChanged { local });
 3616
 3617        let selections = &self.selections.disjoint_anchors_arc();
 3618        if selections.len() == 1 {
 3619            cx.emit(SearchEvent::ActiveMatchChanged)
 3620        }
 3621        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3622            let inmemory_selections = selections
 3623                .iter()
 3624                .map(|s| {
 3625                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3626                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3627                })
 3628                .collect();
 3629            self.update_restoration_data(cx, |data| {
 3630                data.selections = inmemory_selections;
 3631            });
 3632
 3633            if WorkspaceSettings::get(None, cx).restore_on_startup
 3634                != RestoreOnStartupBehavior::EmptyTab
 3635                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3636            {
 3637                let snapshot = self.buffer().read(cx).snapshot(cx);
 3638                let selections = selections.clone();
 3639                let background_executor = cx.background_executor().clone();
 3640                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3641                self.serialize_selections = cx.background_spawn(async move {
 3642                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3643                    let db_selections = selections
 3644                        .iter()
 3645                        .map(|selection| {
 3646                            (
 3647                                selection.start.to_offset(&snapshot).0,
 3648                                selection.end.to_offset(&snapshot).0,
 3649                            )
 3650                        })
 3651                        .collect();
 3652
 3653                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3654                        .await
 3655                        .with_context(|| {
 3656                            format!(
 3657                                "persisting editor selections for editor {editor_id}, \
 3658                                workspace {workspace_id:?}"
 3659                            )
 3660                        })
 3661                        .log_err();
 3662                });
 3663            }
 3664        }
 3665
 3666        cx.notify();
 3667    }
 3668
 3669    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3670        use text::ToOffset as _;
 3671        use text::ToPoint as _;
 3672
 3673        if self.mode.is_minimap()
 3674            || WorkspaceSettings::get(None, cx).restore_on_startup
 3675                == RestoreOnStartupBehavior::EmptyTab
 3676        {
 3677            return;
 3678        }
 3679
 3680        if !self.buffer().read(cx).is_singleton() {
 3681            return;
 3682        }
 3683
 3684        let display_snapshot = self
 3685            .display_map
 3686            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3687        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3688            return;
 3689        };
 3690        let inmemory_folds = display_snapshot
 3691            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3692            .map(|fold| {
 3693                fold.range.start.text_anchor.to_point(&snapshot)
 3694                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3695            })
 3696            .collect();
 3697        self.update_restoration_data(cx, |data| {
 3698            data.folds = inmemory_folds;
 3699        });
 3700
 3701        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3702            return;
 3703        };
 3704
 3705        // Get file path for path-based fold storage (survives tab close)
 3706        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3707            project::File::from_dyn(buffer.read(cx).file())
 3708                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3709        }) else {
 3710            return;
 3711        };
 3712
 3713        let background_executor = cx.background_executor().clone();
 3714        const FINGERPRINT_LEN: usize = 32;
 3715        let db_folds = display_snapshot
 3716            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3717            .map(|fold| {
 3718                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3719                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3720
 3721                // Extract fingerprints - content at fold boundaries for validation on restore
 3722                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3723                // content that might change independently.
 3724                // start_fp: first min(32, fold_len) bytes of fold content
 3725                // end_fp: last min(32, fold_len) bytes of fold content
 3726                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3727                let fold_len = end - start;
 3728                let start_fp_end = snapshot
 3729                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3730                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3731                let end_fp_start = snapshot
 3732                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3733                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3734
 3735                (start, end, start_fp, end_fp)
 3736            })
 3737            .collect::<Vec<_>>();
 3738        self.serialize_folds = cx.background_spawn(async move {
 3739            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3740            if db_folds.is_empty() {
 3741                // No folds - delete any persisted folds for this file
 3742                DB.delete_file_folds(workspace_id, file_path)
 3743                    .await
 3744                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3745                    .log_err();
 3746            } else {
 3747                DB.save_file_folds(workspace_id, file_path, db_folds)
 3748                    .await
 3749                    .with_context(|| {
 3750                        format!("persisting file folds for workspace {workspace_id:?}")
 3751                    })
 3752                    .log_err();
 3753            }
 3754        });
 3755    }
 3756
 3757    pub fn sync_selections(
 3758        &mut self,
 3759        other: Entity<Editor>,
 3760        cx: &mut Context<Self>,
 3761    ) -> gpui::Subscription {
 3762        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3763        if !other_selections.is_empty() {
 3764            self.selections
 3765                .change_with(&self.display_snapshot(cx), |selections| {
 3766                    selections.select_anchors(other_selections);
 3767                });
 3768        }
 3769
 3770        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3771            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3772                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3773                if other_selections.is_empty() {
 3774                    return;
 3775                }
 3776                let snapshot = this.display_snapshot(cx);
 3777                this.selections.change_with(&snapshot, |selections| {
 3778                    selections.select_anchors(other_selections);
 3779                });
 3780            }
 3781        });
 3782
 3783        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3784            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3785                let these_selections = this.selections.disjoint_anchors().to_vec();
 3786                if these_selections.is_empty() {
 3787                    return;
 3788                }
 3789                other.update(cx, |other_editor, cx| {
 3790                    let snapshot = other_editor.display_snapshot(cx);
 3791                    other_editor
 3792                        .selections
 3793                        .change_with(&snapshot, |selections| {
 3794                            selections.select_anchors(these_selections);
 3795                        })
 3796                });
 3797            }
 3798        });
 3799
 3800        Subscription::join(other_subscription, this_subscription)
 3801    }
 3802
 3803    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3804        if self.buffer().read(cx).is_singleton() {
 3805            return;
 3806        }
 3807        let snapshot = self.buffer.read(cx).snapshot(cx);
 3808        let buffer_ids: HashSet<BufferId> = self
 3809            .selections
 3810            .disjoint_anchor_ranges()
 3811            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3812            .collect();
 3813        for buffer_id in buffer_ids {
 3814            self.unfold_buffer(buffer_id, cx);
 3815        }
 3816    }
 3817
 3818    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3819    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3820    /// effects of selection change occur at the end of the transaction.
 3821    pub fn change_selections<R>(
 3822        &mut self,
 3823        effects: SelectionEffects,
 3824        window: &mut Window,
 3825        cx: &mut Context<Self>,
 3826        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3827    ) -> R {
 3828        let snapshot = self.display_snapshot(cx);
 3829        if let Some(state) = &mut self.deferred_selection_effects_state {
 3830            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3831            state.effects.completions = effects.completions;
 3832            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3833            let (changed, result) = self.selections.change_with(&snapshot, change);
 3834            state.changed |= changed;
 3835            return result;
 3836        }
 3837        let mut state = DeferredSelectionEffectsState {
 3838            changed: false,
 3839            effects,
 3840            old_cursor_position: self.selections.newest_anchor().head(),
 3841            history_entry: SelectionHistoryEntry {
 3842                selections: self.selections.disjoint_anchors_arc(),
 3843                select_next_state: self.select_next_state.clone(),
 3844                select_prev_state: self.select_prev_state.clone(),
 3845                add_selections_state: self.add_selections_state.clone(),
 3846            },
 3847        };
 3848        let (changed, result) = self.selections.change_with(&snapshot, change);
 3849        state.changed = state.changed || changed;
 3850        if self.defer_selection_effects {
 3851            self.deferred_selection_effects_state = Some(state);
 3852        } else {
 3853            self.apply_selection_effects(state, window, cx);
 3854        }
 3855        result
 3856    }
 3857
 3858    /// Defers the effects of selection change, so that the effects of multiple calls to
 3859    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3860    /// to selection history and the state of popovers based on selection position aren't
 3861    /// erroneously updated.
 3862    pub fn with_selection_effects_deferred<R>(
 3863        &mut self,
 3864        window: &mut Window,
 3865        cx: &mut Context<Self>,
 3866        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3867    ) -> R {
 3868        let already_deferred = self.defer_selection_effects;
 3869        self.defer_selection_effects = true;
 3870        let result = update(self, window, cx);
 3871        if !already_deferred {
 3872            self.defer_selection_effects = false;
 3873            if let Some(state) = self.deferred_selection_effects_state.take() {
 3874                self.apply_selection_effects(state, window, cx);
 3875            }
 3876        }
 3877        result
 3878    }
 3879
 3880    fn apply_selection_effects(
 3881        &mut self,
 3882        state: DeferredSelectionEffectsState,
 3883        window: &mut Window,
 3884        cx: &mut Context<Self>,
 3885    ) {
 3886        if state.changed {
 3887            self.selection_history.push(state.history_entry);
 3888
 3889            if let Some(autoscroll) = state.effects.scroll {
 3890                self.request_autoscroll(autoscroll, cx);
 3891            }
 3892
 3893            let old_cursor_position = &state.old_cursor_position;
 3894
 3895            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3896
 3897            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3898                self.show_signature_help_auto(window, cx);
 3899            }
 3900        }
 3901    }
 3902
 3903    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3904    where
 3905        I: IntoIterator<Item = (Range<S>, T)>,
 3906        S: ToOffset,
 3907        T: Into<Arc<str>>,
 3908    {
 3909        if self.read_only(cx) {
 3910            return;
 3911        }
 3912
 3913        self.buffer
 3914            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3915    }
 3916
 3917    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3918    where
 3919        I: IntoIterator<Item = (Range<S>, T)>,
 3920        S: ToOffset,
 3921        T: Into<Arc<str>>,
 3922    {
 3923        if self.read_only(cx) {
 3924            return;
 3925        }
 3926
 3927        self.buffer.update(cx, |buffer, cx| {
 3928            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3929        });
 3930    }
 3931
 3932    pub fn edit_with_block_indent<I, S, T>(
 3933        &mut self,
 3934        edits: I,
 3935        original_indent_columns: Vec<Option<u32>>,
 3936        cx: &mut Context<Self>,
 3937    ) where
 3938        I: IntoIterator<Item = (Range<S>, T)>,
 3939        S: ToOffset,
 3940        T: Into<Arc<str>>,
 3941    {
 3942        if self.read_only(cx) {
 3943            return;
 3944        }
 3945
 3946        self.buffer.update(cx, |buffer, cx| {
 3947            buffer.edit(
 3948                edits,
 3949                Some(AutoindentMode::Block {
 3950                    original_indent_columns,
 3951                }),
 3952                cx,
 3953            )
 3954        });
 3955    }
 3956
 3957    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3958        self.hide_context_menu(window, cx);
 3959
 3960        match phase {
 3961            SelectPhase::Begin {
 3962                position,
 3963                add,
 3964                click_count,
 3965            } => self.begin_selection(position, add, click_count, window, cx),
 3966            SelectPhase::BeginColumnar {
 3967                position,
 3968                goal_column,
 3969                reset,
 3970                mode,
 3971            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3972            SelectPhase::Extend {
 3973                position,
 3974                click_count,
 3975            } => self.extend_selection(position, click_count, window, cx),
 3976            SelectPhase::Update {
 3977                position,
 3978                goal_column,
 3979                scroll_delta,
 3980            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3981            SelectPhase::End => self.end_selection(window, cx),
 3982        }
 3983    }
 3984
 3985    fn extend_selection(
 3986        &mut self,
 3987        position: DisplayPoint,
 3988        click_count: usize,
 3989        window: &mut Window,
 3990        cx: &mut Context<Self>,
 3991    ) {
 3992        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3993        let tail = self
 3994            .selections
 3995            .newest::<MultiBufferOffset>(&display_map)
 3996            .tail();
 3997        let click_count = click_count.max(match self.selections.select_mode() {
 3998            SelectMode::Character => 1,
 3999            SelectMode::Word(_) => 2,
 4000            SelectMode::Line(_) => 3,
 4001            SelectMode::All => 4,
 4002        });
 4003        self.begin_selection(position, false, click_count, window, cx);
 4004
 4005        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4006
 4007        let current_selection = match self.selections.select_mode() {
 4008            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4009            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4010        };
 4011
 4012        let mut pending_selection = self
 4013            .selections
 4014            .pending_anchor()
 4015            .cloned()
 4016            .expect("extend_selection not called with pending selection");
 4017
 4018        if pending_selection
 4019            .start
 4020            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4021            == Ordering::Greater
 4022        {
 4023            pending_selection.start = current_selection.start;
 4024        }
 4025        if pending_selection
 4026            .end
 4027            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4028            == Ordering::Less
 4029        {
 4030            pending_selection.end = current_selection.end;
 4031            pending_selection.reversed = true;
 4032        }
 4033
 4034        let mut pending_mode = self.selections.pending_mode().unwrap();
 4035        match &mut pending_mode {
 4036            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4037            _ => {}
 4038        }
 4039
 4040        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4041            SelectionEffects::scroll(Autoscroll::fit())
 4042        } else {
 4043            SelectionEffects::no_scroll()
 4044        };
 4045
 4046        self.change_selections(effects, window, cx, |s| {
 4047            s.set_pending(pending_selection.clone(), pending_mode);
 4048            s.set_is_extending(true);
 4049        });
 4050    }
 4051
 4052    fn begin_selection(
 4053        &mut self,
 4054        position: DisplayPoint,
 4055        add: bool,
 4056        click_count: usize,
 4057        window: &mut Window,
 4058        cx: &mut Context<Self>,
 4059    ) {
 4060        if !self.focus_handle.is_focused(window) {
 4061            self.last_focused_descendant = None;
 4062            window.focus(&self.focus_handle, cx);
 4063        }
 4064
 4065        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4066        let buffer = display_map.buffer_snapshot();
 4067        let position = display_map.clip_point(position, Bias::Left);
 4068
 4069        let start;
 4070        let end;
 4071        let mode;
 4072        let mut auto_scroll;
 4073        match click_count {
 4074            1 => {
 4075                start = buffer.anchor_before(position.to_point(&display_map));
 4076                end = start;
 4077                mode = SelectMode::Character;
 4078                auto_scroll = true;
 4079            }
 4080            2 => {
 4081                let position = display_map
 4082                    .clip_point(position, Bias::Left)
 4083                    .to_offset(&display_map, Bias::Left);
 4084                let (range, _) = buffer.surrounding_word(position, None);
 4085                start = buffer.anchor_before(range.start);
 4086                end = buffer.anchor_before(range.end);
 4087                mode = SelectMode::Word(start..end);
 4088                auto_scroll = true;
 4089            }
 4090            3 => {
 4091                let position = display_map
 4092                    .clip_point(position, Bias::Left)
 4093                    .to_point(&display_map);
 4094                let line_start = display_map.prev_line_boundary(position).0;
 4095                let next_line_start = buffer.clip_point(
 4096                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4097                    Bias::Left,
 4098                );
 4099                start = buffer.anchor_before(line_start);
 4100                end = buffer.anchor_before(next_line_start);
 4101                mode = SelectMode::Line(start..end);
 4102                auto_scroll = true;
 4103            }
 4104            _ => {
 4105                start = buffer.anchor_before(MultiBufferOffset(0));
 4106                end = buffer.anchor_before(buffer.len());
 4107                mode = SelectMode::All;
 4108                auto_scroll = false;
 4109            }
 4110        }
 4111        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4112
 4113        let point_to_delete: Option<usize> = {
 4114            let selected_points: Vec<Selection<Point>> =
 4115                self.selections.disjoint_in_range(start..end, &display_map);
 4116
 4117            if !add || click_count > 1 {
 4118                None
 4119            } else if !selected_points.is_empty() {
 4120                Some(selected_points[0].id)
 4121            } else {
 4122                let clicked_point_already_selected =
 4123                    self.selections.disjoint_anchors().iter().find(|selection| {
 4124                        selection.start.to_point(buffer) == start.to_point(buffer)
 4125                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4126                    });
 4127
 4128                clicked_point_already_selected.map(|selection| selection.id)
 4129            }
 4130        };
 4131
 4132        let selections_count = self.selections.count();
 4133        let effects = if auto_scroll {
 4134            SelectionEffects::default()
 4135        } else {
 4136            SelectionEffects::no_scroll()
 4137        };
 4138
 4139        self.change_selections(effects, window, cx, |s| {
 4140            if let Some(point_to_delete) = point_to_delete {
 4141                s.delete(point_to_delete);
 4142
 4143                if selections_count == 1 {
 4144                    s.set_pending_anchor_range(start..end, mode);
 4145                }
 4146            } else {
 4147                if !add {
 4148                    s.clear_disjoint();
 4149                }
 4150
 4151                s.set_pending_anchor_range(start..end, mode);
 4152            }
 4153        });
 4154    }
 4155
 4156    fn begin_columnar_selection(
 4157        &mut self,
 4158        position: DisplayPoint,
 4159        goal_column: u32,
 4160        reset: bool,
 4161        mode: ColumnarMode,
 4162        window: &mut Window,
 4163        cx: &mut Context<Self>,
 4164    ) {
 4165        if !self.focus_handle.is_focused(window) {
 4166            self.last_focused_descendant = None;
 4167            window.focus(&self.focus_handle, cx);
 4168        }
 4169
 4170        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4171
 4172        if reset {
 4173            let pointer_position = display_map
 4174                .buffer_snapshot()
 4175                .anchor_before(position.to_point(&display_map));
 4176
 4177            self.change_selections(
 4178                SelectionEffects::scroll(Autoscroll::newest()),
 4179                window,
 4180                cx,
 4181                |s| {
 4182                    s.clear_disjoint();
 4183                    s.set_pending_anchor_range(
 4184                        pointer_position..pointer_position,
 4185                        SelectMode::Character,
 4186                    );
 4187                },
 4188            );
 4189        };
 4190
 4191        let tail = self.selections.newest::<Point>(&display_map).tail();
 4192        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4193        self.columnar_selection_state = match mode {
 4194            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4195                selection_tail: selection_anchor,
 4196                display_point: if reset {
 4197                    if position.column() != goal_column {
 4198                        Some(DisplayPoint::new(position.row(), goal_column))
 4199                    } else {
 4200                        None
 4201                    }
 4202                } else {
 4203                    None
 4204                },
 4205            }),
 4206            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4207                selection_tail: selection_anchor,
 4208            }),
 4209        };
 4210
 4211        if !reset {
 4212            self.select_columns(position, goal_column, &display_map, window, cx);
 4213        }
 4214    }
 4215
 4216    fn update_selection(
 4217        &mut self,
 4218        position: DisplayPoint,
 4219        goal_column: u32,
 4220        scroll_delta: gpui::Point<f32>,
 4221        window: &mut Window,
 4222        cx: &mut Context<Self>,
 4223    ) {
 4224        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4225
 4226        if self.columnar_selection_state.is_some() {
 4227            self.select_columns(position, goal_column, &display_map, window, cx);
 4228        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4229            let buffer = display_map.buffer_snapshot();
 4230            let head;
 4231            let tail;
 4232            let mode = self.selections.pending_mode().unwrap();
 4233            match &mode {
 4234                SelectMode::Character => {
 4235                    head = position.to_point(&display_map);
 4236                    tail = pending.tail().to_point(buffer);
 4237                }
 4238                SelectMode::Word(original_range) => {
 4239                    let offset = display_map
 4240                        .clip_point(position, Bias::Left)
 4241                        .to_offset(&display_map, Bias::Left);
 4242                    let original_range = original_range.to_offset(buffer);
 4243
 4244                    let head_offset = if buffer.is_inside_word(offset, None)
 4245                        || original_range.contains(&offset)
 4246                    {
 4247                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4248                        if word_range.start < original_range.start {
 4249                            word_range.start
 4250                        } else {
 4251                            word_range.end
 4252                        }
 4253                    } else {
 4254                        offset
 4255                    };
 4256
 4257                    head = head_offset.to_point(buffer);
 4258                    if head_offset <= original_range.start {
 4259                        tail = original_range.end.to_point(buffer);
 4260                    } else {
 4261                        tail = original_range.start.to_point(buffer);
 4262                    }
 4263                }
 4264                SelectMode::Line(original_range) => {
 4265                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4266
 4267                    let position = display_map
 4268                        .clip_point(position, Bias::Left)
 4269                        .to_point(&display_map);
 4270                    let line_start = display_map.prev_line_boundary(position).0;
 4271                    let next_line_start = buffer.clip_point(
 4272                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4273                        Bias::Left,
 4274                    );
 4275
 4276                    if line_start < original_range.start {
 4277                        head = line_start
 4278                    } else {
 4279                        head = next_line_start
 4280                    }
 4281
 4282                    if head <= original_range.start {
 4283                        tail = original_range.end;
 4284                    } else {
 4285                        tail = original_range.start;
 4286                    }
 4287                }
 4288                SelectMode::All => {
 4289                    return;
 4290                }
 4291            };
 4292
 4293            if head < tail {
 4294                pending.start = buffer.anchor_before(head);
 4295                pending.end = buffer.anchor_before(tail);
 4296                pending.reversed = true;
 4297            } else {
 4298                pending.start = buffer.anchor_before(tail);
 4299                pending.end = buffer.anchor_before(head);
 4300                pending.reversed = false;
 4301            }
 4302
 4303            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4304                s.set_pending(pending.clone(), mode);
 4305            });
 4306        } else {
 4307            log::error!("update_selection dispatched with no pending selection");
 4308            return;
 4309        }
 4310
 4311        self.apply_scroll_delta(scroll_delta, window, cx);
 4312        cx.notify();
 4313    }
 4314
 4315    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4316        self.columnar_selection_state.take();
 4317        if let Some(pending_mode) = self.selections.pending_mode() {
 4318            let selections = self
 4319                .selections
 4320                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4321            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4322                s.select(selections);
 4323                s.clear_pending();
 4324                if s.is_extending() {
 4325                    s.set_is_extending(false);
 4326                } else {
 4327                    s.set_select_mode(pending_mode);
 4328                }
 4329            });
 4330        }
 4331    }
 4332
 4333    fn select_columns(
 4334        &mut self,
 4335        head: DisplayPoint,
 4336        goal_column: u32,
 4337        display_map: &DisplaySnapshot,
 4338        window: &mut Window,
 4339        cx: &mut Context<Self>,
 4340    ) {
 4341        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4342            return;
 4343        };
 4344
 4345        let tail = match columnar_state {
 4346            ColumnarSelectionState::FromMouse {
 4347                selection_tail,
 4348                display_point,
 4349            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4350            ColumnarSelectionState::FromSelection { selection_tail } => {
 4351                selection_tail.to_display_point(display_map)
 4352            }
 4353        };
 4354
 4355        let start_row = cmp::min(tail.row(), head.row());
 4356        let end_row = cmp::max(tail.row(), head.row());
 4357        let start_column = cmp::min(tail.column(), goal_column);
 4358        let end_column = cmp::max(tail.column(), goal_column);
 4359        let reversed = start_column < tail.column();
 4360
 4361        let selection_ranges = (start_row.0..=end_row.0)
 4362            .map(DisplayRow)
 4363            .filter_map(|row| {
 4364                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4365                    || start_column <= display_map.line_len(row))
 4366                    && !display_map.is_block_line(row)
 4367                {
 4368                    let start = display_map
 4369                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4370                        .to_point(display_map);
 4371                    let end = display_map
 4372                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4373                        .to_point(display_map);
 4374                    if reversed {
 4375                        Some(end..start)
 4376                    } else {
 4377                        Some(start..end)
 4378                    }
 4379                } else {
 4380                    None
 4381                }
 4382            })
 4383            .collect::<Vec<_>>();
 4384        if selection_ranges.is_empty() {
 4385            return;
 4386        }
 4387
 4388        let ranges = match columnar_state {
 4389            ColumnarSelectionState::FromMouse { .. } => {
 4390                let mut non_empty_ranges = selection_ranges
 4391                    .iter()
 4392                    .filter(|selection_range| selection_range.start != selection_range.end)
 4393                    .peekable();
 4394                if non_empty_ranges.peek().is_some() {
 4395                    non_empty_ranges.cloned().collect()
 4396                } else {
 4397                    selection_ranges
 4398                }
 4399            }
 4400            _ => selection_ranges,
 4401        };
 4402
 4403        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4404            s.select_ranges(ranges);
 4405        });
 4406        cx.notify();
 4407    }
 4408
 4409    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4410        self.selections
 4411            .all_adjusted(snapshot)
 4412            .iter()
 4413            .any(|selection| !selection.is_empty())
 4414    }
 4415
 4416    pub fn has_pending_nonempty_selection(&self) -> bool {
 4417        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4418            Some(Selection { start, end, .. }) => start != end,
 4419            None => false,
 4420        };
 4421
 4422        pending_nonempty_selection
 4423            || (self.columnar_selection_state.is_some()
 4424                && self.selections.disjoint_anchors().len() > 1)
 4425    }
 4426
 4427    pub fn has_pending_selection(&self) -> bool {
 4428        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4429    }
 4430
 4431    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4432        self.selection_mark_mode = false;
 4433        self.selection_drag_state = SelectionDragState::None;
 4434
 4435        if self.dismiss_menus_and_popups(true, window, cx) {
 4436            cx.notify();
 4437            return;
 4438        }
 4439        if self.clear_expanded_diff_hunks(cx) {
 4440            cx.notify();
 4441            return;
 4442        }
 4443        if self.show_git_blame_gutter {
 4444            self.show_git_blame_gutter = false;
 4445            cx.notify();
 4446            return;
 4447        }
 4448
 4449        if self.mode.is_full()
 4450            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4451        {
 4452            cx.notify();
 4453            return;
 4454        }
 4455
 4456        cx.propagate();
 4457    }
 4458
 4459    pub fn dismiss_menus_and_popups(
 4460        &mut self,
 4461        is_user_requested: bool,
 4462        window: &mut Window,
 4463        cx: &mut Context<Self>,
 4464    ) -> bool {
 4465        let mut dismissed = false;
 4466
 4467        dismissed |= self.take_rename(false, window, cx).is_some();
 4468        dismissed |= self.hide_blame_popover(true, cx);
 4469        dismissed |= hide_hover(self, cx);
 4470        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4471        dismissed |= self.hide_context_menu(window, cx).is_some();
 4472        dismissed |= self.mouse_context_menu.take().is_some();
 4473        dismissed |= is_user_requested
 4474            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4475        dismissed |= self.snippet_stack.pop().is_some();
 4476        if self.diff_review_drag_state.is_some() {
 4477            self.cancel_diff_review_drag(cx);
 4478            dismissed = true;
 4479        }
 4480        if !self.diff_review_overlays.is_empty() {
 4481            self.dismiss_all_diff_review_overlays(cx);
 4482            dismissed = true;
 4483        }
 4484
 4485        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4486            self.dismiss_diagnostics(cx);
 4487            dismissed = true;
 4488        }
 4489
 4490        dismissed
 4491    }
 4492
 4493    fn linked_editing_ranges_for(
 4494        &self,
 4495        selection: Range<text::Anchor>,
 4496        cx: &App,
 4497    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4498        if self.linked_edit_ranges.is_empty() {
 4499            return None;
 4500        }
 4501        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4502            selection.end.buffer_id.and_then(|end_buffer_id| {
 4503                if selection.start.buffer_id != Some(end_buffer_id) {
 4504                    return None;
 4505                }
 4506                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4507                let snapshot = buffer.read(cx).snapshot();
 4508                self.linked_edit_ranges
 4509                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4510                    .map(|ranges| (ranges, snapshot, buffer))
 4511            })?;
 4512        use text::ToOffset as TO;
 4513        // find offset from the start of current range to current cursor position
 4514        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4515
 4516        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4517        let start_difference = start_offset - start_byte_offset;
 4518        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4519        let end_difference = end_offset - start_byte_offset;
 4520
 4521        // Current range has associated linked ranges.
 4522        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4523        for range in linked_ranges.iter() {
 4524            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4525            let end_offset = start_offset + end_difference;
 4526            let start_offset = start_offset + start_difference;
 4527            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4528                continue;
 4529            }
 4530            if self.selections.disjoint_anchor_ranges().any(|s| {
 4531                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4532                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4533                {
 4534                    return false;
 4535                }
 4536                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4537                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4538            }) {
 4539                continue;
 4540            }
 4541            let start = buffer_snapshot.anchor_after(start_offset);
 4542            let end = buffer_snapshot.anchor_after(end_offset);
 4543            linked_edits
 4544                .entry(buffer.clone())
 4545                .or_default()
 4546                .push(start..end);
 4547        }
 4548        Some(linked_edits)
 4549    }
 4550
 4551    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4552        let text: Arc<str> = text.into();
 4553
 4554        if self.read_only(cx) {
 4555            return;
 4556        }
 4557
 4558        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4559
 4560        self.unfold_buffers_with_selections(cx);
 4561
 4562        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4563        let mut bracket_inserted = false;
 4564        let mut edits = Vec::new();
 4565        let mut linked_edits = LinkedEdits::new();
 4566        let mut new_selections = Vec::with_capacity(selections.len());
 4567        let mut new_autoclose_regions = Vec::new();
 4568        let snapshot = self.buffer.read(cx).read(cx);
 4569        let mut clear_linked_edit_ranges = false;
 4570        let mut all_selections_read_only = true;
 4571        let mut has_adjacent_edits = false;
 4572        let mut in_adjacent_group = false;
 4573
 4574        let mut regions = self
 4575            .selections_with_autoclose_regions(selections, &snapshot)
 4576            .peekable();
 4577
 4578        while let Some((selection, autoclose_region)) = regions.next() {
 4579            if snapshot
 4580                .point_to_buffer_point(selection.head())
 4581                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4582            {
 4583                continue;
 4584            }
 4585            if snapshot
 4586                .point_to_buffer_point(selection.tail())
 4587                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4588            {
 4589                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4590                continue;
 4591            }
 4592            all_selections_read_only = false;
 4593
 4594            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4595                // Determine if the inserted text matches the opening or closing
 4596                // bracket of any of this language's bracket pairs.
 4597                let mut bracket_pair = None;
 4598                let mut is_bracket_pair_start = false;
 4599                let mut is_bracket_pair_end = false;
 4600                if !text.is_empty() {
 4601                    let mut bracket_pair_matching_end = None;
 4602                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4603                    //  and they are removing the character that triggered IME popup.
 4604                    for (pair, enabled) in scope.brackets() {
 4605                        if !pair.close && !pair.surround {
 4606                            continue;
 4607                        }
 4608
 4609                        if enabled && pair.start.ends_with(text.as_ref()) {
 4610                            let prefix_len = pair.start.len() - text.len();
 4611                            let preceding_text_matches_prefix = prefix_len == 0
 4612                                || (selection.start.column >= (prefix_len as u32)
 4613                                    && snapshot.contains_str_at(
 4614                                        Point::new(
 4615                                            selection.start.row,
 4616                                            selection.start.column - (prefix_len as u32),
 4617                                        ),
 4618                                        &pair.start[..prefix_len],
 4619                                    ));
 4620                            if preceding_text_matches_prefix {
 4621                                bracket_pair = Some(pair.clone());
 4622                                is_bracket_pair_start = true;
 4623                                break;
 4624                            }
 4625                        }
 4626                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4627                        {
 4628                            // take first bracket pair matching end, but don't break in case a later bracket
 4629                            // pair matches start
 4630                            bracket_pair_matching_end = Some(pair.clone());
 4631                        }
 4632                    }
 4633                    if let Some(end) = bracket_pair_matching_end
 4634                        && bracket_pair.is_none()
 4635                    {
 4636                        bracket_pair = Some(end);
 4637                        is_bracket_pair_end = true;
 4638                    }
 4639                }
 4640
 4641                if let Some(bracket_pair) = bracket_pair {
 4642                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4643                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4644                    let auto_surround =
 4645                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4646                    if selection.is_empty() {
 4647                        if is_bracket_pair_start {
 4648                            // If the inserted text is a suffix of an opening bracket and the
 4649                            // selection is preceded by the rest of the opening bracket, then
 4650                            // insert the closing bracket.
 4651                            let following_text_allows_autoclose = snapshot
 4652                                .chars_at(selection.start)
 4653                                .next()
 4654                                .is_none_or(|c| scope.should_autoclose_before(c));
 4655
 4656                            let preceding_text_allows_autoclose = selection.start.column == 0
 4657                                || snapshot
 4658                                    .reversed_chars_at(selection.start)
 4659                                    .next()
 4660                                    .is_none_or(|c| {
 4661                                        bracket_pair.start != bracket_pair.end
 4662                                            || !snapshot
 4663                                                .char_classifier_at(selection.start)
 4664                                                .is_word(c)
 4665                                    });
 4666
 4667                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4668                                && bracket_pair.start.len() == 1
 4669                            {
 4670                                let target = bracket_pair.start.chars().next().unwrap();
 4671                                let mut byte_offset = 0u32;
 4672                                let current_line_count = snapshot
 4673                                    .reversed_chars_at(selection.start)
 4674                                    .take_while(|&c| c != '\n')
 4675                                    .filter(|c| {
 4676                                        byte_offset += c.len_utf8() as u32;
 4677                                        if *c != target {
 4678                                            return false;
 4679                                        }
 4680
 4681                                        let point = Point::new(
 4682                                            selection.start.row,
 4683                                            selection.start.column.saturating_sub(byte_offset),
 4684                                        );
 4685
 4686                                        let is_enabled = snapshot
 4687                                            .language_scope_at(point)
 4688                                            .and_then(|scope| {
 4689                                                scope
 4690                                                    .brackets()
 4691                                                    .find(|(pair, _)| {
 4692                                                        pair.start == bracket_pair.start
 4693                                                    })
 4694                                                    .map(|(_, enabled)| enabled)
 4695                                            })
 4696                                            .unwrap_or(true);
 4697
 4698                                        let is_delimiter = snapshot
 4699                                            .language_scope_at(Point::new(
 4700                                                point.row,
 4701                                                point.column + 1,
 4702                                            ))
 4703                                            .and_then(|scope| {
 4704                                                scope
 4705                                                    .brackets()
 4706                                                    .find(|(pair, _)| {
 4707                                                        pair.start == bracket_pair.start
 4708                                                    })
 4709                                                    .map(|(_, enabled)| !enabled)
 4710                                            })
 4711                                            .unwrap_or(false);
 4712
 4713                                        is_enabled && !is_delimiter
 4714                                    })
 4715                                    .count();
 4716                                current_line_count % 2 == 1
 4717                            } else {
 4718                                false
 4719                            };
 4720
 4721                            if autoclose
 4722                                && bracket_pair.close
 4723                                && following_text_allows_autoclose
 4724                                && preceding_text_allows_autoclose
 4725                                && !is_closing_quote
 4726                            {
 4727                                let anchor = snapshot.anchor_before(selection.end);
 4728                                new_selections.push((selection.map(|_| anchor), text.len()));
 4729                                new_autoclose_regions.push((
 4730                                    anchor,
 4731                                    text.len(),
 4732                                    selection.id,
 4733                                    bracket_pair.clone(),
 4734                                ));
 4735                                edits.push((
 4736                                    selection.range(),
 4737                                    format!("{}{}", text, bracket_pair.end).into(),
 4738                                ));
 4739                                bracket_inserted = true;
 4740                                continue;
 4741                            }
 4742                        }
 4743
 4744                        if let Some(region) = autoclose_region {
 4745                            // If the selection is followed by an auto-inserted closing bracket,
 4746                            // then don't insert that closing bracket again; just move the selection
 4747                            // past the closing bracket.
 4748                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4749                                && text.as_ref() == region.pair.end.as_str()
 4750                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4751                            if should_skip {
 4752                                let anchor = snapshot.anchor_after(selection.end);
 4753                                new_selections
 4754                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4755                                continue;
 4756                            }
 4757                        }
 4758
 4759                        let always_treat_brackets_as_autoclosed = snapshot
 4760                            .language_settings_at(selection.start, cx)
 4761                            .always_treat_brackets_as_autoclosed;
 4762                        if always_treat_brackets_as_autoclosed
 4763                            && is_bracket_pair_end
 4764                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4765                        {
 4766                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4767                            // and the inserted text is a closing bracket and the selection is followed
 4768                            // by the closing bracket then move the selection past the closing bracket.
 4769                            let anchor = snapshot.anchor_after(selection.end);
 4770                            new_selections.push((selection.map(|_| anchor), text.len()));
 4771                            continue;
 4772                        }
 4773                    }
 4774                    // If an opening bracket is 1 character long and is typed while
 4775                    // text is selected, then surround that text with the bracket pair.
 4776                    else if auto_surround
 4777                        && bracket_pair.surround
 4778                        && is_bracket_pair_start
 4779                        && bracket_pair.start.chars().count() == 1
 4780                    {
 4781                        edits.push((selection.start..selection.start, text.clone()));
 4782                        edits.push((
 4783                            selection.end..selection.end,
 4784                            bracket_pair.end.as_str().into(),
 4785                        ));
 4786                        bracket_inserted = true;
 4787                        new_selections.push((
 4788                            Selection {
 4789                                id: selection.id,
 4790                                start: snapshot.anchor_after(selection.start),
 4791                                end: snapshot.anchor_before(selection.end),
 4792                                reversed: selection.reversed,
 4793                                goal: selection.goal,
 4794                            },
 4795                            0,
 4796                        ));
 4797                        continue;
 4798                    }
 4799                }
 4800            }
 4801
 4802            if self.auto_replace_emoji_shortcode
 4803                && selection.is_empty()
 4804                && text.as_ref().ends_with(':')
 4805                && let Some(possible_emoji_short_code) =
 4806                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4807                && !possible_emoji_short_code.is_empty()
 4808                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4809            {
 4810                let emoji_shortcode_start = Point::new(
 4811                    selection.start.row,
 4812                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4813                );
 4814
 4815                // Remove shortcode from buffer
 4816                edits.push((
 4817                    emoji_shortcode_start..selection.start,
 4818                    "".to_string().into(),
 4819                ));
 4820                new_selections.push((
 4821                    Selection {
 4822                        id: selection.id,
 4823                        start: snapshot.anchor_after(emoji_shortcode_start),
 4824                        end: snapshot.anchor_before(selection.start),
 4825                        reversed: selection.reversed,
 4826                        goal: selection.goal,
 4827                    },
 4828                    0,
 4829                ));
 4830
 4831                // Insert emoji
 4832                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4833                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4834                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4835
 4836                continue;
 4837            }
 4838
 4839            let next_is_adjacent = regions
 4840                .peek()
 4841                .is_some_and(|(next, _)| selection.end == next.start);
 4842
 4843            // If not handling any auto-close operation, then just replace the selected
 4844            // text with the given input and move the selection to the end of the
 4845            // newly inserted text.
 4846            let anchor = if in_adjacent_group || next_is_adjacent {
 4847                // After edits the right bias would shift those anchor to the next visible fragment
 4848                // but we want to resolve to the previous one
 4849                snapshot.anchor_before(selection.end)
 4850            } else {
 4851                snapshot.anchor_after(selection.end)
 4852            };
 4853
 4854            if !self.linked_edit_ranges.is_empty() {
 4855                let start_anchor = snapshot.anchor_before(selection.start);
 4856
 4857                let is_word_char = text.chars().next().is_none_or(|char| {
 4858                    let classifier = snapshot
 4859                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4860                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4861                    classifier.is_word(char)
 4862                });
 4863                let is_dot = text.as_ref() == ".";
 4864                let should_apply_linked_edit = is_word_char || is_dot;
 4865
 4866                if should_apply_linked_edit {
 4867                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4868                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4869                } else {
 4870                    clear_linked_edit_ranges = true;
 4871                }
 4872            }
 4873
 4874            new_selections.push((selection.map(|_| anchor), 0));
 4875            edits.push((selection.start..selection.end, text.clone()));
 4876
 4877            has_adjacent_edits |= next_is_adjacent;
 4878            in_adjacent_group = next_is_adjacent;
 4879        }
 4880
 4881        if all_selections_read_only {
 4882            return;
 4883        }
 4884
 4885        drop(regions);
 4886        drop(snapshot);
 4887
 4888        self.transact(window, cx, |this, window, cx| {
 4889            if clear_linked_edit_ranges {
 4890                this.linked_edit_ranges.clear();
 4891            }
 4892            let initial_buffer_versions =
 4893                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4894
 4895            this.buffer.update(cx, |buffer, cx| {
 4896                if has_adjacent_edits {
 4897                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4898                } else {
 4899                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4900                }
 4901            });
 4902            linked_edits.apply(cx);
 4903            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4904            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4905            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4906            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4907                new_anchor_selections,
 4908                &map,
 4909            )
 4910            .zip(new_selection_deltas)
 4911            .map(|(selection, delta)| Selection {
 4912                id: selection.id,
 4913                start: selection.start + delta,
 4914                end: selection.end + delta,
 4915                reversed: selection.reversed,
 4916                goal: SelectionGoal::None,
 4917            })
 4918            .collect::<Vec<_>>();
 4919
 4920            let mut i = 0;
 4921            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4922                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4923                let start = map.buffer_snapshot().anchor_before(position);
 4924                let end = map.buffer_snapshot().anchor_after(position);
 4925                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4926                    match existing_state
 4927                        .range
 4928                        .start
 4929                        .cmp(&start, map.buffer_snapshot())
 4930                    {
 4931                        Ordering::Less => i += 1,
 4932                        Ordering::Greater => break,
 4933                        Ordering::Equal => {
 4934                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4935                                Ordering::Less => i += 1,
 4936                                Ordering::Equal => break,
 4937                                Ordering::Greater => break,
 4938                            }
 4939                        }
 4940                    }
 4941                }
 4942                this.autoclose_regions.insert(
 4943                    i,
 4944                    AutocloseRegion {
 4945                        selection_id,
 4946                        range: start..end,
 4947                        pair,
 4948                    },
 4949                );
 4950            }
 4951
 4952            let had_active_edit_prediction = this.has_active_edit_prediction();
 4953            this.change_selections(
 4954                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4955                window,
 4956                cx,
 4957                |s| s.select(new_selections),
 4958            );
 4959
 4960            if !bracket_inserted
 4961                && let Some(on_type_format_task) =
 4962                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4963            {
 4964                on_type_format_task.detach_and_log_err(cx);
 4965            }
 4966
 4967            let editor_settings = EditorSettings::get_global(cx);
 4968            if bracket_inserted
 4969                && (editor_settings.auto_signature_help
 4970                    || editor_settings.show_signature_help_after_edits)
 4971            {
 4972                this.show_signature_help(&ShowSignatureHelp, window, cx);
 4973            }
 4974
 4975            let trigger_in_words =
 4976                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 4977            if this.hard_wrap.is_some() {
 4978                let latest: Range<Point> = this.selections.newest(&map).range();
 4979                if latest.is_empty()
 4980                    && this
 4981                        .buffer()
 4982                        .read(cx)
 4983                        .snapshot(cx)
 4984                        .line_len(MultiBufferRow(latest.start.row))
 4985                        == latest.start.column
 4986                {
 4987                    this.rewrap_impl(
 4988                        RewrapOptions {
 4989                            override_language_settings: true,
 4990                            preserve_existing_whitespace: true,
 4991                        },
 4992                        cx,
 4993                    )
 4994                }
 4995            }
 4996            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 4997            refresh_linked_ranges(this, window, cx);
 4998            this.refresh_edit_prediction(true, false, window, cx);
 4999            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5000        });
 5001    }
 5002
 5003    fn find_possible_emoji_shortcode_at_position(
 5004        snapshot: &MultiBufferSnapshot,
 5005        position: Point,
 5006    ) -> Option<String> {
 5007        let mut chars = Vec::new();
 5008        let mut found_colon = false;
 5009        for char in snapshot.reversed_chars_at(position).take(100) {
 5010            // Found a possible emoji shortcode in the middle of the buffer
 5011            if found_colon {
 5012                if char.is_whitespace() {
 5013                    chars.reverse();
 5014                    return Some(chars.iter().collect());
 5015                }
 5016                // If the previous character is not a whitespace, we are in the middle of a word
 5017                // and we only want to complete the shortcode if the word is made up of other emojis
 5018                let mut containing_word = String::new();
 5019                for ch in snapshot
 5020                    .reversed_chars_at(position)
 5021                    .skip(chars.len() + 1)
 5022                    .take(100)
 5023                {
 5024                    if ch.is_whitespace() {
 5025                        break;
 5026                    }
 5027                    containing_word.push(ch);
 5028                }
 5029                let containing_word = containing_word.chars().rev().collect::<String>();
 5030                if util::word_consists_of_emojis(containing_word.as_str()) {
 5031                    chars.reverse();
 5032                    return Some(chars.iter().collect());
 5033                }
 5034            }
 5035
 5036            if char.is_whitespace() || !char.is_ascii() {
 5037                return None;
 5038            }
 5039            if char == ':' {
 5040                found_colon = true;
 5041            } else {
 5042                chars.push(char);
 5043            }
 5044        }
 5045        // Found a possible emoji shortcode at the beginning of the buffer
 5046        chars.reverse();
 5047        Some(chars.iter().collect())
 5048    }
 5049
 5050    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5051        if self.read_only(cx) {
 5052            return;
 5053        }
 5054
 5055        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5056        self.transact(window, cx, |this, window, cx| {
 5057            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5058                let selections = this
 5059                    .selections
 5060                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5061                let multi_buffer = this.buffer.read(cx);
 5062                let buffer = multi_buffer.snapshot(cx);
 5063                selections
 5064                    .iter()
 5065                    .map(|selection| {
 5066                        let start_point = selection.start.to_point(&buffer);
 5067                        let mut existing_indent =
 5068                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5069                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5070                        let start = selection.start;
 5071                        let end = selection.end;
 5072                        let selection_is_empty = start == end;
 5073                        let language_scope = buffer.language_scope_at(start);
 5074                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5075                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5076                                &buffer,
 5077                                start..end,
 5078                                language,
 5079                            )
 5080                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5081                                    &buffer,
 5082                                    start..end,
 5083                                );
 5084
 5085                            let mut newline_config = NewlineConfig::Newline {
 5086                                additional_indent: IndentSize::spaces(0),
 5087                                extra_line_additional_indent: if needs_extra_newline {
 5088                                    Some(IndentSize::spaces(0))
 5089                                } else {
 5090                                    None
 5091                                },
 5092                                prevent_auto_indent: false,
 5093                            };
 5094
 5095                            let comment_delimiter = maybe!({
 5096                                if !selection_is_empty {
 5097                                    return None;
 5098                                }
 5099
 5100                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5101                                    return None;
 5102                                }
 5103
 5104                                return comment_delimiter_for_newline(
 5105                                    &start_point,
 5106                                    &buffer,
 5107                                    language,
 5108                                );
 5109                            });
 5110
 5111                            let doc_delimiter = maybe!({
 5112                                if !selection_is_empty {
 5113                                    return None;
 5114                                }
 5115
 5116                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5117                                    return None;
 5118                                }
 5119
 5120                                return documentation_delimiter_for_newline(
 5121                                    &start_point,
 5122                                    &buffer,
 5123                                    language,
 5124                                    &mut newline_config,
 5125                                );
 5126                            });
 5127
 5128                            let list_delimiter = maybe!({
 5129                                if !selection_is_empty {
 5130                                    return None;
 5131                                }
 5132
 5133                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5134                                    return None;
 5135                                }
 5136
 5137                                return list_delimiter_for_newline(
 5138                                    &start_point,
 5139                                    &buffer,
 5140                                    language,
 5141                                    &mut newline_config,
 5142                                );
 5143                            });
 5144
 5145                            (
 5146                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5147                                newline_config,
 5148                            )
 5149                        } else {
 5150                            (
 5151                                None,
 5152                                NewlineConfig::Newline {
 5153                                    additional_indent: IndentSize::spaces(0),
 5154                                    extra_line_additional_indent: None,
 5155                                    prevent_auto_indent: false,
 5156                                },
 5157                            )
 5158                        };
 5159
 5160                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5161                            NewlineConfig::ClearCurrentLine => {
 5162                                let row_start =
 5163                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5164                                (row_start, String::new(), false)
 5165                            }
 5166                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5167                                let row_start =
 5168                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5169                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5170                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5171                                let reduced_indent =
 5172                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5173                                let mut new_text = String::new();
 5174                                new_text.extend(reduced_indent.chars());
 5175                                new_text.push_str(continuation);
 5176                                (row_start, new_text, true)
 5177                            }
 5178                            NewlineConfig::Newline {
 5179                                additional_indent,
 5180                                extra_line_additional_indent,
 5181                                prevent_auto_indent,
 5182                            } => {
 5183                                let auto_indent_mode =
 5184                                    buffer.language_settings_at(start, cx).auto_indent;
 5185                                let preserve_indent =
 5186                                    auto_indent_mode != language::AutoIndentMode::None;
 5187                                let apply_syntax_indent =
 5188                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5189                                let capacity_for_delimiter =
 5190                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5191                                let existing_indent_len = if preserve_indent {
 5192                                    existing_indent.len as usize
 5193                                } else {
 5194                                    0
 5195                                };
 5196                                let extra_line_len = extra_line_additional_indent
 5197                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5198                                    .unwrap_or(0);
 5199                                let mut new_text = String::with_capacity(
 5200                                    1 + capacity_for_delimiter
 5201                                        + existing_indent_len
 5202                                        + additional_indent.len as usize
 5203                                        + extra_line_len,
 5204                                );
 5205                                new_text.push('\n');
 5206                                if preserve_indent {
 5207                                    new_text.extend(existing_indent.chars());
 5208                                }
 5209                                new_text.extend(additional_indent.chars());
 5210                                if let Some(delimiter) = &delimiter {
 5211                                    new_text.push_str(delimiter);
 5212                                }
 5213                                if let Some(extra_indent) = extra_line_additional_indent {
 5214                                    new_text.push('\n');
 5215                                    if preserve_indent {
 5216                                        new_text.extend(existing_indent.chars());
 5217                                    }
 5218                                    new_text.extend(extra_indent.chars());
 5219                                }
 5220                                (
 5221                                    start,
 5222                                    new_text,
 5223                                    *prevent_auto_indent || !apply_syntax_indent,
 5224                                )
 5225                            }
 5226                        };
 5227
 5228                        let anchor = buffer.anchor_after(end);
 5229                        let new_selection = selection.map(|_| anchor);
 5230                        (
 5231                            ((edit_start..end, new_text), prevent_auto_indent),
 5232                            (newline_config.has_extra_line(), new_selection),
 5233                        )
 5234                    })
 5235                    .unzip()
 5236            };
 5237
 5238            let mut auto_indent_edits = Vec::new();
 5239            let mut edits = Vec::new();
 5240            for (edit, prevent_auto_indent) in edits_with_flags {
 5241                if prevent_auto_indent {
 5242                    edits.push(edit);
 5243                } else {
 5244                    auto_indent_edits.push(edit);
 5245                }
 5246            }
 5247            if !edits.is_empty() {
 5248                this.edit(edits, cx);
 5249            }
 5250            if !auto_indent_edits.is_empty() {
 5251                this.edit_with_autoindent(auto_indent_edits, cx);
 5252            }
 5253
 5254            let buffer = this.buffer.read(cx).snapshot(cx);
 5255            let new_selections = selection_info
 5256                .into_iter()
 5257                .map(|(extra_newline_inserted, new_selection)| {
 5258                    let mut cursor = new_selection.end.to_point(&buffer);
 5259                    if extra_newline_inserted {
 5260                        cursor.row -= 1;
 5261                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5262                    }
 5263                    new_selection.map(|_| cursor)
 5264                })
 5265                .collect();
 5266
 5267            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5268            this.refresh_edit_prediction(true, false, window, cx);
 5269            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5270                task.detach_and_log_err(cx);
 5271            }
 5272        });
 5273    }
 5274
 5275    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5276        if self.read_only(cx) {
 5277            return;
 5278        }
 5279
 5280        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5281
 5282        let buffer = self.buffer.read(cx);
 5283        let snapshot = buffer.snapshot(cx);
 5284
 5285        let mut edits = Vec::new();
 5286        let mut rows = Vec::new();
 5287
 5288        for (rows_inserted, selection) in self
 5289            .selections
 5290            .all_adjusted(&self.display_snapshot(cx))
 5291            .into_iter()
 5292            .enumerate()
 5293        {
 5294            let cursor = selection.head();
 5295            let row = cursor.row;
 5296
 5297            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5298
 5299            let newline = "\n".to_string();
 5300            edits.push((start_of_line..start_of_line, newline));
 5301
 5302            rows.push(row + rows_inserted as u32);
 5303        }
 5304
 5305        self.transact(window, cx, |editor, window, cx| {
 5306            editor.edit(edits, cx);
 5307
 5308            editor.change_selections(Default::default(), window, cx, |s| {
 5309                let mut index = 0;
 5310                s.move_cursors_with(&mut |map, _, _| {
 5311                    let row = rows[index];
 5312                    index += 1;
 5313
 5314                    let point = Point::new(row, 0);
 5315                    let boundary = map.next_line_boundary(point).1;
 5316                    let clipped = map.clip_point(boundary, Bias::Left);
 5317
 5318                    (clipped, SelectionGoal::None)
 5319                });
 5320            });
 5321
 5322            let mut indent_edits = Vec::new();
 5323            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5324            for row in rows {
 5325                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5326                for (row, indent) in indents {
 5327                    if indent.len == 0 {
 5328                        continue;
 5329                    }
 5330
 5331                    let text = match indent.kind {
 5332                        IndentKind::Space => " ".repeat(indent.len as usize),
 5333                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5334                    };
 5335                    let point = Point::new(row.0, 0);
 5336                    indent_edits.push((point..point, text));
 5337                }
 5338            }
 5339            editor.edit(indent_edits, cx);
 5340            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5341                format.detach_and_log_err(cx);
 5342            }
 5343        });
 5344    }
 5345
 5346    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5347        if self.read_only(cx) {
 5348            return;
 5349        }
 5350
 5351        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5352
 5353        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5354        let mut rows = Vec::new();
 5355        let mut rows_inserted = 0;
 5356
 5357        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5358            let cursor = selection.head();
 5359            let row = cursor.row;
 5360
 5361            let point = Point::new(row, 0);
 5362            let Some((buffer_handle, buffer_point, _)) =
 5363                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5364            else {
 5365                continue;
 5366            };
 5367
 5368            buffer_edits
 5369                .entry(buffer_handle.entity_id())
 5370                .or_insert_with(|| (buffer_handle, Vec::new()))
 5371                .1
 5372                .push(buffer_point);
 5373
 5374            rows_inserted += 1;
 5375            rows.push(row + rows_inserted);
 5376        }
 5377
 5378        self.transact(window, cx, |editor, window, cx| {
 5379            for (_, (buffer_handle, points)) in &buffer_edits {
 5380                buffer_handle.update(cx, |buffer, cx| {
 5381                    let edits: Vec<_> = points
 5382                        .iter()
 5383                        .map(|point| {
 5384                            let target = Point::new(point.row + 1, 0);
 5385                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5386                            (start_of_line..start_of_line, "\n")
 5387                        })
 5388                        .collect();
 5389                    buffer.edit(edits, None, cx);
 5390                });
 5391            }
 5392
 5393            editor.change_selections(Default::default(), window, cx, |s| {
 5394                let mut index = 0;
 5395                s.move_cursors_with(&mut |map, _, _| {
 5396                    let row = rows[index];
 5397                    index += 1;
 5398
 5399                    let point = Point::new(row, 0);
 5400                    let boundary = map.next_line_boundary(point).1;
 5401                    let clipped = map.clip_point(boundary, Bias::Left);
 5402
 5403                    (clipped, SelectionGoal::None)
 5404                });
 5405            });
 5406
 5407            let mut indent_edits = Vec::new();
 5408            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5409            for row in rows {
 5410                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5411                for (row, indent) in indents {
 5412                    if indent.len == 0 {
 5413                        continue;
 5414                    }
 5415
 5416                    let text = match indent.kind {
 5417                        IndentKind::Space => " ".repeat(indent.len as usize),
 5418                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5419                    };
 5420                    let point = Point::new(row.0, 0);
 5421                    indent_edits.push((point..point, text));
 5422                }
 5423            }
 5424            editor.edit(indent_edits, cx);
 5425            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5426                format.detach_and_log_err(cx);
 5427            }
 5428        });
 5429    }
 5430
 5431    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5432        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5433            original_indent_columns: Vec::new(),
 5434        });
 5435        self.replace_selections(text, autoindent, window, cx, false);
 5436    }
 5437
 5438    /// Replaces the editor's selections with the provided `text`, applying the
 5439    /// given `autoindent_mode` (`None` will skip autoindentation).
 5440    ///
 5441    /// Early returns if the editor is in read-only mode, without applying any
 5442    /// edits.
 5443    fn replace_selections(
 5444        &mut self,
 5445        text: &str,
 5446        autoindent_mode: Option<AutoindentMode>,
 5447        window: &mut Window,
 5448        cx: &mut Context<Self>,
 5449        apply_linked_edits: bool,
 5450    ) {
 5451        if self.read_only(cx) {
 5452            return;
 5453        }
 5454
 5455        let text: Arc<str> = text.into();
 5456        self.transact(window, cx, |this, window, cx| {
 5457            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5458            let linked_edits = if apply_linked_edits {
 5459                this.linked_edits_for_selections(text.clone(), cx)
 5460            } else {
 5461                LinkedEdits::new()
 5462            };
 5463
 5464            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5465                let anchors = {
 5466                    let snapshot = buffer.read(cx);
 5467                    old_selections
 5468                        .iter()
 5469                        .map(|s| {
 5470                            let anchor = snapshot.anchor_after(s.head());
 5471                            s.map(|_| anchor)
 5472                        })
 5473                        .collect::<Vec<_>>()
 5474                };
 5475                buffer.edit(
 5476                    old_selections
 5477                        .iter()
 5478                        .map(|s| (s.start..s.end, text.clone())),
 5479                    autoindent_mode,
 5480                    cx,
 5481                );
 5482                anchors
 5483            });
 5484
 5485            linked_edits.apply(cx);
 5486
 5487            this.change_selections(Default::default(), window, cx, |s| {
 5488                s.select_anchors(selection_anchors);
 5489            });
 5490
 5491            if apply_linked_edits {
 5492                refresh_linked_ranges(this, window, cx);
 5493            }
 5494
 5495            cx.notify();
 5496        });
 5497    }
 5498
 5499    /// Collects linked edits for the current selections, pairing each linked
 5500    /// range with `text`.
 5501    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5502        let mut linked_edits = LinkedEdits::new();
 5503        if !self.linked_edit_ranges.is_empty() {
 5504            for selection in self.selections.disjoint_anchors() {
 5505                let start = selection.start.text_anchor;
 5506                let end = selection.end.text_anchor;
 5507                linked_edits.push(self, start..end, text.clone(), cx);
 5508            }
 5509        }
 5510        linked_edits
 5511    }
 5512
 5513    /// Deletes the content covered by the current selections and applies
 5514    /// linked edits.
 5515    pub fn delete_selections_with_linked_edits(
 5516        &mut self,
 5517        window: &mut Window,
 5518        cx: &mut Context<Self>,
 5519    ) {
 5520        self.replace_selections("", None, window, cx, true);
 5521    }
 5522
 5523    #[cfg(any(test, feature = "test-support"))]
 5524    pub fn set_linked_edit_ranges_for_testing(
 5525        &mut self,
 5526        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5527        cx: &mut Context<Self>,
 5528    ) -> Option<()> {
 5529        let Some((buffer, _)) = self
 5530            .buffer
 5531            .read(cx)
 5532            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5533        else {
 5534            return None;
 5535        };
 5536        let buffer = buffer.read(cx);
 5537        let buffer_id = buffer.remote_id();
 5538        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5539        for (base_range, linked_ranges_points) in ranges {
 5540            let base_anchor =
 5541                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5542            let linked_anchors = linked_ranges_points
 5543                .into_iter()
 5544                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5545                .collect();
 5546            linked_ranges.push((base_anchor, linked_anchors));
 5547        }
 5548        let mut map = HashMap::default();
 5549        map.insert(buffer_id, linked_ranges);
 5550        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5551        Some(())
 5552    }
 5553
 5554    fn trigger_completion_on_input(
 5555        &mut self,
 5556        text: &str,
 5557        trigger_in_words: bool,
 5558        window: &mut Window,
 5559        cx: &mut Context<Self>,
 5560    ) {
 5561        let completions_source = self
 5562            .context_menu
 5563            .borrow()
 5564            .as_ref()
 5565            .and_then(|menu| match menu {
 5566                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5567                CodeContextMenu::CodeActions(_) => None,
 5568            });
 5569
 5570        match completions_source {
 5571            Some(CompletionsMenuSource::Words { .. }) => {
 5572                self.open_or_update_completions_menu(
 5573                    Some(CompletionsMenuSource::Words {
 5574                        ignore_threshold: false,
 5575                    }),
 5576                    None,
 5577                    trigger_in_words,
 5578                    window,
 5579                    cx,
 5580                );
 5581            }
 5582            _ => self.open_or_update_completions_menu(
 5583                None,
 5584                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5585                true,
 5586                window,
 5587                cx,
 5588            ),
 5589        }
 5590    }
 5591
 5592    /// If any empty selections is touching the start of its innermost containing autoclose
 5593    /// region, expand it to select the brackets.
 5594    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5595        let selections = self
 5596            .selections
 5597            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5598        let buffer = self.buffer.read(cx).read(cx);
 5599        let new_selections = self
 5600            .selections_with_autoclose_regions(selections, &buffer)
 5601            .map(|(mut selection, region)| {
 5602                if !selection.is_empty() {
 5603                    return selection;
 5604                }
 5605
 5606                if let Some(region) = region {
 5607                    let mut range = region.range.to_offset(&buffer);
 5608                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5609                        range.start -= region.pair.start.len();
 5610                        if buffer.contains_str_at(range.start, &region.pair.start)
 5611                            && buffer.contains_str_at(range.end, &region.pair.end)
 5612                        {
 5613                            range.end += region.pair.end.len();
 5614                            selection.start = range.start;
 5615                            selection.end = range.end;
 5616
 5617                            return selection;
 5618                        }
 5619                    }
 5620                }
 5621
 5622                let always_treat_brackets_as_autoclosed = buffer
 5623                    .language_settings_at(selection.start, cx)
 5624                    .always_treat_brackets_as_autoclosed;
 5625
 5626                if !always_treat_brackets_as_autoclosed {
 5627                    return selection;
 5628                }
 5629
 5630                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5631                    for (pair, enabled) in scope.brackets() {
 5632                        if !enabled || !pair.close {
 5633                            continue;
 5634                        }
 5635
 5636                        if buffer.contains_str_at(selection.start, &pair.end) {
 5637                            let pair_start_len = pair.start.len();
 5638                            if buffer.contains_str_at(
 5639                                selection.start.saturating_sub_usize(pair_start_len),
 5640                                &pair.start,
 5641                            ) {
 5642                                selection.start -= pair_start_len;
 5643                                selection.end += pair.end.len();
 5644
 5645                                return selection;
 5646                            }
 5647                        }
 5648                    }
 5649                }
 5650
 5651                selection
 5652            })
 5653            .collect();
 5654
 5655        drop(buffer);
 5656        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5657            selections.select(new_selections)
 5658        });
 5659    }
 5660
 5661    /// Iterate the given selections, and for each one, find the smallest surrounding
 5662    /// autoclose region. This uses the ordering of the selections and the autoclose
 5663    /// regions to avoid repeated comparisons.
 5664    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5665        &'a self,
 5666        selections: impl IntoIterator<Item = Selection<D>>,
 5667        buffer: &'a MultiBufferSnapshot,
 5668    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5669        let mut i = 0;
 5670        let mut regions = self.autoclose_regions.as_slice();
 5671        selections.into_iter().map(move |selection| {
 5672            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5673
 5674            let mut enclosing = None;
 5675            while let Some(pair_state) = regions.get(i) {
 5676                if pair_state.range.end.to_offset(buffer) < range.start {
 5677                    regions = &regions[i + 1..];
 5678                    i = 0;
 5679                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5680                    break;
 5681                } else {
 5682                    if pair_state.selection_id == selection.id {
 5683                        enclosing = Some(pair_state);
 5684                    }
 5685                    i += 1;
 5686                }
 5687            }
 5688
 5689            (selection, enclosing)
 5690        })
 5691    }
 5692
 5693    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5694    fn invalidate_autoclose_regions(
 5695        &mut self,
 5696        mut selections: &[Selection<Anchor>],
 5697        buffer: &MultiBufferSnapshot,
 5698    ) {
 5699        self.autoclose_regions.retain(|state| {
 5700            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5701                return false;
 5702            }
 5703
 5704            let mut i = 0;
 5705            while let Some(selection) = selections.get(i) {
 5706                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5707                    selections = &selections[1..];
 5708                    continue;
 5709                }
 5710                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5711                    break;
 5712                }
 5713                if selection.id == state.selection_id {
 5714                    return true;
 5715                } else {
 5716                    i += 1;
 5717                }
 5718            }
 5719            false
 5720        });
 5721    }
 5722
 5723    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5724        let offset = position.to_offset(buffer);
 5725        let (word_range, kind) =
 5726            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5727        if offset > word_range.start && kind == Some(CharKind::Word) {
 5728            Some(
 5729                buffer
 5730                    .text_for_range(word_range.start..offset)
 5731                    .collect::<String>(),
 5732            )
 5733        } else {
 5734            None
 5735        }
 5736    }
 5737
 5738    pub fn visible_excerpts(
 5739        &self,
 5740        lsp_related_only: bool,
 5741        cx: &mut Context<Editor>,
 5742    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5743        let project = self.project().cloned();
 5744        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5745        let multi_buffer = self.buffer().read(cx);
 5746        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5747        multi_buffer_snapshot
 5748            .range_to_buffer_ranges(
 5749                self.multi_buffer_visible_range(&display_snapshot, cx)
 5750                    .to_inclusive(),
 5751            )
 5752            .into_iter()
 5753            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5754            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5755                if !lsp_related_only {
 5756                    return Some((
 5757                        excerpt_id,
 5758                        (
 5759                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5760                            buffer.version().clone(),
 5761                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5762                        ),
 5763                    ));
 5764                }
 5765
 5766                let project = project.as_ref()?.read(cx);
 5767                let buffer_file = project::File::from_dyn(buffer.file())?;
 5768                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5769                let worktree_entry = buffer_worktree
 5770                    .read(cx)
 5771                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5772                if worktree_entry.is_ignored {
 5773                    None
 5774                } else {
 5775                    Some((
 5776                        excerpt_id,
 5777                        (
 5778                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5779                            buffer.version().clone(),
 5780                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5781                        ),
 5782                    ))
 5783                }
 5784            })
 5785            .collect()
 5786    }
 5787
 5788    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5789        TextLayoutDetails {
 5790            text_system: window.text_system().clone(),
 5791            editor_style: self.style.clone().unwrap(),
 5792            rem_size: window.rem_size(),
 5793            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5794            visible_rows: self.visible_line_count(),
 5795            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5796        }
 5797    }
 5798
 5799    fn trigger_on_type_formatting(
 5800        &self,
 5801        input: String,
 5802        window: &mut Window,
 5803        cx: &mut Context<Self>,
 5804    ) -> Option<Task<Result<()>>> {
 5805        if input.chars().count() != 1 {
 5806            return None;
 5807        }
 5808
 5809        let project = self.project()?;
 5810        let position = self.selections.newest_anchor().head();
 5811        let (buffer, buffer_position) = self
 5812            .buffer
 5813            .read(cx)
 5814            .text_anchor_for_position(position, cx)?;
 5815
 5816        let settings = language_settings::language_settings(
 5817            buffer
 5818                .read(cx)
 5819                .language_at(buffer_position)
 5820                .map(|l| l.name()),
 5821            buffer.read(cx).file(),
 5822            cx,
 5823        );
 5824        if !settings.use_on_type_format {
 5825            return None;
 5826        }
 5827
 5828        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5829        // hence we do LSP request & edit on host side only — add formats to host's history.
 5830        let push_to_lsp_host_history = true;
 5831        // If this is not the host, append its history with new edits.
 5832        let push_to_client_history = project.read(cx).is_via_collab();
 5833
 5834        let on_type_formatting = project.update(cx, |project, cx| {
 5835            project.on_type_format(
 5836                buffer.clone(),
 5837                buffer_position,
 5838                input,
 5839                push_to_lsp_host_history,
 5840                cx,
 5841            )
 5842        });
 5843        Some(cx.spawn_in(window, async move |editor, cx| {
 5844            if let Some(transaction) = on_type_formatting.await? {
 5845                if push_to_client_history {
 5846                    buffer.update(cx, |buffer, _| {
 5847                        buffer.push_transaction(transaction, Instant::now());
 5848                        buffer.finalize_last_transaction();
 5849                    });
 5850                }
 5851                editor.update(cx, |editor, cx| {
 5852                    editor.refresh_document_highlights(cx);
 5853                })?;
 5854            }
 5855            Ok(())
 5856        }))
 5857    }
 5858
 5859    pub fn show_word_completions(
 5860        &mut self,
 5861        _: &ShowWordCompletions,
 5862        window: &mut Window,
 5863        cx: &mut Context<Self>,
 5864    ) {
 5865        self.open_or_update_completions_menu(
 5866            Some(CompletionsMenuSource::Words {
 5867                ignore_threshold: true,
 5868            }),
 5869            None,
 5870            false,
 5871            window,
 5872            cx,
 5873        );
 5874    }
 5875
 5876    pub fn show_completions(
 5877        &mut self,
 5878        _: &ShowCompletions,
 5879        window: &mut Window,
 5880        cx: &mut Context<Self>,
 5881    ) {
 5882        self.open_or_update_completions_menu(None, None, false, window, cx);
 5883    }
 5884
 5885    fn open_or_update_completions_menu(
 5886        &mut self,
 5887        requested_source: Option<CompletionsMenuSource>,
 5888        trigger: Option<String>,
 5889        trigger_in_words: bool,
 5890        window: &mut Window,
 5891        cx: &mut Context<Self>,
 5892    ) {
 5893        if self.pending_rename.is_some() {
 5894            return;
 5895        }
 5896
 5897        let completions_source = self
 5898            .context_menu
 5899            .borrow()
 5900            .as_ref()
 5901            .and_then(|menu| match menu {
 5902                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5903                CodeContextMenu::CodeActions(_) => None,
 5904            });
 5905
 5906        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5907
 5908        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5909        // inserted and selected. To handle that case, the start of the selection is used so that
 5910        // the menu starts with all choices.
 5911        let position = self
 5912            .selections
 5913            .newest_anchor()
 5914            .start
 5915            .bias_right(&multibuffer_snapshot);
 5916        if position.diff_base_anchor.is_some() {
 5917            return;
 5918        }
 5919        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5920        let Some(buffer) = buffer_position
 5921            .text_anchor
 5922            .buffer_id
 5923            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5924        else {
 5925            return;
 5926        };
 5927        let buffer_snapshot = buffer.read(cx).snapshot();
 5928
 5929        let menu_is_open = matches!(
 5930            self.context_menu.borrow().as_ref(),
 5931            Some(CodeContextMenu::Completions(_))
 5932        );
 5933
 5934        let language = buffer_snapshot
 5935            .language_at(buffer_position.text_anchor)
 5936            .map(|language| language.name());
 5937
 5938        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5939        let completion_settings = language_settings.completions.clone();
 5940
 5941        let show_completions_on_input = self
 5942            .show_completions_on_input_override
 5943            .unwrap_or(language_settings.show_completions_on_input);
 5944        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5945            return;
 5946        }
 5947
 5948        let query: Option<Arc<String>> =
 5949            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5950                .map(|query| query.into());
 5951
 5952        drop(multibuffer_snapshot);
 5953
 5954        // Hide the current completions menu when query is empty. Without this, cached
 5955        // completions from before the trigger char may be reused (#32774).
 5956        if query.is_none() && menu_is_open {
 5957            self.hide_context_menu(window, cx);
 5958        }
 5959
 5960        let mut ignore_word_threshold = false;
 5961        let provider = match requested_source {
 5962            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5963            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5964                ignore_word_threshold = ignore_threshold;
 5965                None
 5966            }
 5967            Some(CompletionsMenuSource::SnippetChoices)
 5968            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5969                log::error!("bug: SnippetChoices requested_source is not handled");
 5970                None
 5971            }
 5972        };
 5973
 5974        let sort_completions = provider
 5975            .as_ref()
 5976            .is_some_and(|provider| provider.sort_completions());
 5977
 5978        let filter_completions = provider
 5979            .as_ref()
 5980            .is_none_or(|provider| provider.filter_completions());
 5981
 5982        let was_snippets_only = matches!(
 5983            completions_source,
 5984            Some(CompletionsMenuSource::SnippetsOnly)
 5985        );
 5986
 5987        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5988            if filter_completions {
 5989                menu.filter(
 5990                    query.clone().unwrap_or_default(),
 5991                    buffer_position.text_anchor,
 5992                    &buffer,
 5993                    provider.clone(),
 5994                    window,
 5995                    cx,
 5996                );
 5997            }
 5998            // When `is_incomplete` is false, no need to re-query completions when the current query
 5999            // is a suffix of the initial query.
 6000            let was_complete = !menu.is_incomplete;
 6001            if was_complete && !was_snippets_only {
 6002                // If the new query is a suffix of the old query (typing more characters) and
 6003                // the previous result was complete, the existing completions can be filtered.
 6004                //
 6005                // Note that snippet completions are always complete.
 6006                let query_matches = match (&menu.initial_query, &query) {
 6007                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6008                    (None, _) => true,
 6009                    _ => false,
 6010                };
 6011                if query_matches {
 6012                    let position_matches = if menu.initial_position == position {
 6013                        true
 6014                    } else {
 6015                        let snapshot = self.buffer.read(cx).read(cx);
 6016                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6017                    };
 6018                    if position_matches {
 6019                        return;
 6020                    }
 6021                }
 6022            }
 6023        };
 6024
 6025        let Anchor {
 6026            excerpt_id: buffer_excerpt_id,
 6027            text_anchor: buffer_position,
 6028            ..
 6029        } = buffer_position;
 6030
 6031        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6032            buffer_snapshot.surrounding_word(buffer_position, None)
 6033        {
 6034            let word_to_exclude = buffer_snapshot
 6035                .text_for_range(word_range.clone())
 6036                .collect::<String>();
 6037            (
 6038                buffer_snapshot.anchor_before(word_range.start)
 6039                    ..buffer_snapshot.anchor_after(buffer_position),
 6040                Some(word_to_exclude),
 6041            )
 6042        } else {
 6043            (buffer_position..buffer_position, None)
 6044        };
 6045
 6046        let show_completion_documentation = buffer_snapshot
 6047            .settings_at(buffer_position, cx)
 6048            .show_completion_documentation;
 6049
 6050        // The document can be large, so stay in reasonable bounds when searching for words,
 6051        // otherwise completion pop-up might be slow to appear.
 6052        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6053        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6054        let min_word_search = buffer_snapshot.clip_point(
 6055            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6056            Bias::Left,
 6057        );
 6058        let max_word_search = buffer_snapshot.clip_point(
 6059            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6060            Bias::Right,
 6061        );
 6062        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6063            ..buffer_snapshot.point_to_offset(max_word_search);
 6064
 6065        let skip_digits = query
 6066            .as_ref()
 6067            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6068
 6069        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6070            trigger.as_ref().is_none_or(|trigger| {
 6071                provider.is_completion_trigger(
 6072                    &buffer,
 6073                    position.text_anchor,
 6074                    trigger,
 6075                    trigger_in_words,
 6076                    cx,
 6077                )
 6078            })
 6079        });
 6080
 6081        let provider_responses = if let Some(provider) = &provider
 6082            && load_provider_completions
 6083        {
 6084            let trigger_character =
 6085                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6086            let completion_context = CompletionContext {
 6087                trigger_kind: match &trigger_character {
 6088                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6089                    None => CompletionTriggerKind::INVOKED,
 6090                },
 6091                trigger_character,
 6092            };
 6093
 6094            provider.completions(
 6095                buffer_excerpt_id,
 6096                &buffer,
 6097                buffer_position,
 6098                completion_context,
 6099                window,
 6100                cx,
 6101            )
 6102        } else {
 6103            Task::ready(Ok(Vec::new()))
 6104        };
 6105
 6106        let load_word_completions = if !self.word_completions_enabled {
 6107            false
 6108        } else if requested_source
 6109            == Some(CompletionsMenuSource::Words {
 6110                ignore_threshold: true,
 6111            })
 6112        {
 6113            true
 6114        } else {
 6115            load_provider_completions
 6116                && completion_settings.words != WordsCompletionMode::Disabled
 6117                && (ignore_word_threshold || {
 6118                    let words_min_length = completion_settings.words_min_length;
 6119                    // check whether word has at least `words_min_length` characters
 6120                    let query_chars = query.iter().flat_map(|q| q.chars());
 6121                    query_chars.take(words_min_length).count() == words_min_length
 6122                })
 6123        };
 6124
 6125        let mut words = if load_word_completions {
 6126            cx.background_spawn({
 6127                let buffer_snapshot = buffer_snapshot.clone();
 6128                async move {
 6129                    buffer_snapshot.words_in_range(WordsQuery {
 6130                        fuzzy_contents: None,
 6131                        range: word_search_range,
 6132                        skip_digits,
 6133                    })
 6134                }
 6135            })
 6136        } else {
 6137            Task::ready(BTreeMap::default())
 6138        };
 6139
 6140        let snippets = if let Some(provider) = &provider
 6141            && provider.show_snippets()
 6142            && let Some(project) = self.project()
 6143        {
 6144            let char_classifier = buffer_snapshot
 6145                .char_classifier_at(buffer_position)
 6146                .scope_context(Some(CharScopeContext::Completion));
 6147            project.update(cx, |project, cx| {
 6148                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6149            })
 6150        } else {
 6151            Task::ready(Ok(CompletionResponse {
 6152                completions: Vec::new(),
 6153                display_options: Default::default(),
 6154                is_incomplete: false,
 6155            }))
 6156        };
 6157
 6158        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6159
 6160        let id = post_inc(&mut self.next_completion_id);
 6161        let task = cx.spawn_in(window, async move |editor, cx| {
 6162            let Ok(()) = editor.update(cx, |this, _| {
 6163                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6164            }) else {
 6165                return;
 6166            };
 6167
 6168            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6169            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6170            let mut completions = Vec::new();
 6171            let mut is_incomplete = false;
 6172            let mut display_options: Option<CompletionDisplayOptions> = None;
 6173            if let Some(provider_responses) = provider_responses.await.log_err()
 6174                && !provider_responses.is_empty()
 6175            {
 6176                for response in provider_responses {
 6177                    completions.extend(response.completions);
 6178                    is_incomplete = is_incomplete || response.is_incomplete;
 6179                    match display_options.as_mut() {
 6180                        None => {
 6181                            display_options = Some(response.display_options);
 6182                        }
 6183                        Some(options) => options.merge(&response.display_options),
 6184                    }
 6185                }
 6186                if completion_settings.words == WordsCompletionMode::Fallback {
 6187                    words = Task::ready(BTreeMap::default());
 6188                }
 6189            }
 6190            let display_options = display_options.unwrap_or_default();
 6191
 6192            let mut words = words.await;
 6193            if let Some(word_to_exclude) = &word_to_exclude {
 6194                words.remove(word_to_exclude);
 6195            }
 6196            for lsp_completion in &completions {
 6197                words.remove(&lsp_completion.new_text);
 6198            }
 6199            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6200                replace_range: word_replace_range.clone(),
 6201                new_text: word.clone(),
 6202                label: CodeLabel::plain(word, None),
 6203                match_start: None,
 6204                snippet_deduplication_key: None,
 6205                icon_path: None,
 6206                documentation: None,
 6207                source: CompletionSource::BufferWord {
 6208                    word_range,
 6209                    resolved: false,
 6210                },
 6211                insert_text_mode: Some(InsertTextMode::AS_IS),
 6212                confirm: None,
 6213            }));
 6214
 6215            completions.extend(
 6216                snippets
 6217                    .await
 6218                    .into_iter()
 6219                    .flat_map(|response| response.completions),
 6220            );
 6221
 6222            let menu = if completions.is_empty() {
 6223                None
 6224            } else {
 6225                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6226                    let languages = editor
 6227                        .workspace
 6228                        .as_ref()
 6229                        .and_then(|(workspace, _)| workspace.upgrade())
 6230                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6231                    let menu = CompletionsMenu::new(
 6232                        id,
 6233                        requested_source.unwrap_or(if load_provider_completions {
 6234                            CompletionsMenuSource::Normal
 6235                        } else {
 6236                            CompletionsMenuSource::SnippetsOnly
 6237                        }),
 6238                        sort_completions,
 6239                        show_completion_documentation,
 6240                        position,
 6241                        query.clone(),
 6242                        is_incomplete,
 6243                        buffer.clone(),
 6244                        completions.into(),
 6245                        editor
 6246                            .context_menu()
 6247                            .borrow_mut()
 6248                            .as_ref()
 6249                            .map(|menu| menu.primary_scroll_handle()),
 6250                        display_options,
 6251                        snippet_sort_order,
 6252                        languages,
 6253                        language,
 6254                        cx,
 6255                    );
 6256
 6257                    let query = if filter_completions { query } else { None };
 6258                    let matches_task = menu.do_async_filtering(
 6259                        query.unwrap_or_default(),
 6260                        buffer_position,
 6261                        &buffer,
 6262                        cx,
 6263                    );
 6264                    (menu, matches_task)
 6265                }) else {
 6266                    return;
 6267                };
 6268
 6269                let matches = matches_task.await;
 6270
 6271                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6272                    // Newer menu already set, so exit.
 6273                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6274                        editor.context_menu.borrow().as_ref()
 6275                        && prev_menu.id > id
 6276                    {
 6277                        return;
 6278                    };
 6279
 6280                    // Only valid to take prev_menu because either the new menu is immediately set
 6281                    // below, or the menu is hidden.
 6282                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6283                        editor.context_menu.borrow_mut().take()
 6284                    {
 6285                        let position_matches =
 6286                            if prev_menu.initial_position == menu.initial_position {
 6287                                true
 6288                            } else {
 6289                                let snapshot = editor.buffer.read(cx).read(cx);
 6290                                prev_menu.initial_position.to_offset(&snapshot)
 6291                                    == menu.initial_position.to_offset(&snapshot)
 6292                            };
 6293                        if position_matches {
 6294                            // Preserve markdown cache before `set_filter_results` because it will
 6295                            // try to populate the documentation cache.
 6296                            menu.preserve_markdown_cache(prev_menu);
 6297                        }
 6298                    };
 6299
 6300                    menu.set_filter_results(matches, provider, window, cx);
 6301                }) else {
 6302                    return;
 6303                };
 6304
 6305                menu.visible().then_some(menu)
 6306            };
 6307
 6308            editor
 6309                .update_in(cx, |editor, window, cx| {
 6310                    if editor.focus_handle.is_focused(window)
 6311                        && let Some(menu) = menu
 6312                    {
 6313                        *editor.context_menu.borrow_mut() =
 6314                            Some(CodeContextMenu::Completions(menu));
 6315
 6316                        crate::hover_popover::hide_hover(editor, cx);
 6317                        if editor.show_edit_predictions_in_menu() {
 6318                            editor.update_visible_edit_prediction(window, cx);
 6319                        } else {
 6320                            editor
 6321                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6322                        }
 6323
 6324                        cx.notify();
 6325                        return;
 6326                    }
 6327
 6328                    if editor.completion_tasks.len() <= 1 {
 6329                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6330                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6331                        // If it was already hidden and we don't show edit predictions in the menu,
 6332                        // we should also show the edit prediction when available.
 6333                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6334                            editor.update_visible_edit_prediction(window, cx);
 6335                        }
 6336                    }
 6337                })
 6338                .ok();
 6339        });
 6340
 6341        self.completion_tasks.push((id, task));
 6342    }
 6343
 6344    #[cfg(any(test, feature = "test-support"))]
 6345    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6346        let menu = self.context_menu.borrow();
 6347        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6348            let completions = menu.completions.borrow();
 6349            Some(completions.to_vec())
 6350        } else {
 6351            None
 6352        }
 6353    }
 6354
 6355    pub fn with_completions_menu_matching_id<R>(
 6356        &self,
 6357        id: CompletionId,
 6358        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6359    ) -> R {
 6360        let mut context_menu = self.context_menu.borrow_mut();
 6361        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6362            return f(None);
 6363        };
 6364        if completions_menu.id != id {
 6365            return f(None);
 6366        }
 6367        f(Some(completions_menu))
 6368    }
 6369
 6370    pub fn confirm_completion(
 6371        &mut self,
 6372        action: &ConfirmCompletion,
 6373        window: &mut Window,
 6374        cx: &mut Context<Self>,
 6375    ) -> Option<Task<Result<()>>> {
 6376        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6377        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6378    }
 6379
 6380    pub fn confirm_completion_insert(
 6381        &mut self,
 6382        _: &ConfirmCompletionInsert,
 6383        window: &mut Window,
 6384        cx: &mut Context<Self>,
 6385    ) -> Option<Task<Result<()>>> {
 6386        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6387        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6388    }
 6389
 6390    pub fn confirm_completion_replace(
 6391        &mut self,
 6392        _: &ConfirmCompletionReplace,
 6393        window: &mut Window,
 6394        cx: &mut Context<Self>,
 6395    ) -> Option<Task<Result<()>>> {
 6396        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6397        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6398    }
 6399
 6400    pub fn compose_completion(
 6401        &mut self,
 6402        action: &ComposeCompletion,
 6403        window: &mut Window,
 6404        cx: &mut Context<Self>,
 6405    ) -> Option<Task<Result<()>>> {
 6406        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6407        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6408    }
 6409
 6410    fn do_completion(
 6411        &mut self,
 6412        item_ix: Option<usize>,
 6413        intent: CompletionIntent,
 6414        window: &mut Window,
 6415        cx: &mut Context<Editor>,
 6416    ) -> Option<Task<Result<()>>> {
 6417        use language::ToOffset as _;
 6418
 6419        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6420        else {
 6421            return None;
 6422        };
 6423
 6424        let candidate_id = {
 6425            let entries = completions_menu.entries.borrow();
 6426            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6427            if self.show_edit_predictions_in_menu() {
 6428                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6429            }
 6430            mat.candidate_id
 6431        };
 6432
 6433        let completion = completions_menu
 6434            .completions
 6435            .borrow()
 6436            .get(candidate_id)?
 6437            .clone();
 6438        cx.stop_propagation();
 6439
 6440        let buffer_handle = completions_menu.buffer.clone();
 6441
 6442        let CompletionEdit {
 6443            new_text,
 6444            snippet,
 6445            replace_range,
 6446        } = process_completion_for_edit(
 6447            &completion,
 6448            intent,
 6449            &buffer_handle,
 6450            &completions_menu.initial_position.text_anchor,
 6451            cx,
 6452        );
 6453
 6454        let buffer = buffer_handle.read(cx);
 6455        let snapshot = self.buffer.read(cx).snapshot(cx);
 6456        let newest_anchor = self.selections.newest_anchor();
 6457        let replace_range_multibuffer = {
 6458            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6459            excerpt.map_range_from_buffer(replace_range.clone())
 6460        };
 6461        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6462            return None;
 6463        }
 6464
 6465        let old_text = buffer
 6466            .text_for_range(replace_range.clone())
 6467            .collect::<String>();
 6468        let lookbehind = newest_anchor
 6469            .start
 6470            .text_anchor
 6471            .to_offset(buffer)
 6472            .saturating_sub(replace_range.start.0);
 6473        let lookahead = replace_range
 6474            .end
 6475            .0
 6476            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6477        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6478        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6479
 6480        let selections = self
 6481            .selections
 6482            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6483        let mut ranges = Vec::new();
 6484        let mut all_commit_ranges = Vec::new();
 6485        let mut linked_edits = LinkedEdits::new();
 6486
 6487        let text: Arc<str> = new_text.clone().into();
 6488        for selection in &selections {
 6489            let range = if selection.id == newest_anchor.id {
 6490                replace_range_multibuffer.clone()
 6491            } else {
 6492                let mut range = selection.range();
 6493
 6494                // if prefix is present, don't duplicate it
 6495                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6496                    range.start = range.start.saturating_sub_usize(lookbehind);
 6497
 6498                    // if suffix is also present, mimic the newest cursor and replace it
 6499                    if selection.id != newest_anchor.id
 6500                        && snapshot.contains_str_at(range.end, suffix)
 6501                    {
 6502                        range.end += lookahead;
 6503                    }
 6504                }
 6505                range
 6506            };
 6507
 6508            ranges.push(range.clone());
 6509
 6510            let start_anchor = snapshot.anchor_before(range.start);
 6511            let end_anchor = snapshot.anchor_after(range.end);
 6512            let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6513            all_commit_ranges.push(anchor_range.clone());
 6514
 6515            if !self.linked_edit_ranges.is_empty() {
 6516                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6517            }
 6518        }
 6519
 6520        let common_prefix_len = old_text
 6521            .chars()
 6522            .zip(new_text.chars())
 6523            .take_while(|(a, b)| a == b)
 6524            .map(|(a, _)| a.len_utf8())
 6525            .sum::<usize>();
 6526
 6527        cx.emit(EditorEvent::InputHandled {
 6528            utf16_range_to_replace: None,
 6529            text: new_text[common_prefix_len..].into(),
 6530        });
 6531
 6532        self.transact(window, cx, |editor, window, cx| {
 6533            if let Some(mut snippet) = snippet {
 6534                snippet.text = new_text.to_string();
 6535                editor
 6536                    .insert_snippet(&ranges, snippet, window, cx)
 6537                    .log_err();
 6538            } else {
 6539                editor.buffer.update(cx, |multi_buffer, cx| {
 6540                    let auto_indent = match completion.insert_text_mode {
 6541                        Some(InsertTextMode::AS_IS) => None,
 6542                        _ => editor.autoindent_mode.clone(),
 6543                    };
 6544                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6545                    multi_buffer.edit(edits, auto_indent, cx);
 6546                });
 6547            }
 6548            linked_edits.apply(cx);
 6549            editor.refresh_edit_prediction(true, false, window, cx);
 6550        });
 6551        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6552
 6553        let show_new_completions_on_confirm = completion
 6554            .confirm
 6555            .as_ref()
 6556            .is_some_and(|confirm| confirm(intent, window, cx));
 6557        if show_new_completions_on_confirm {
 6558            self.open_or_update_completions_menu(None, None, false, window, cx);
 6559        }
 6560
 6561        let provider = self.completion_provider.as_ref()?;
 6562
 6563        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6564        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6565            let CompletionSource::Lsp {
 6566                lsp_completion,
 6567                server_id,
 6568                ..
 6569            } = &completion.source
 6570            else {
 6571                return None;
 6572            };
 6573            let lsp_command = lsp_completion.command.as_ref()?;
 6574            let available_commands = lsp_store
 6575                .read(cx)
 6576                .lsp_server_capabilities
 6577                .get(server_id)
 6578                .and_then(|server_capabilities| {
 6579                    server_capabilities
 6580                        .execute_command_provider
 6581                        .as_ref()
 6582                        .map(|options| options.commands.as_slice())
 6583                })?;
 6584            if available_commands.contains(&lsp_command.command) {
 6585                Some(CodeAction {
 6586                    server_id: *server_id,
 6587                    range: language::Anchor::MIN..language::Anchor::MIN,
 6588                    lsp_action: LspAction::Command(lsp_command.clone()),
 6589                    resolved: false,
 6590                })
 6591            } else {
 6592                None
 6593            }
 6594        });
 6595
 6596        drop(completion);
 6597        let apply_edits = provider.apply_additional_edits_for_completion(
 6598            buffer_handle.clone(),
 6599            completions_menu.completions.clone(),
 6600            candidate_id,
 6601            true,
 6602            all_commit_ranges,
 6603            cx,
 6604        );
 6605
 6606        let editor_settings = EditorSettings::get_global(cx);
 6607        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6608            // After the code completion is finished, users often want to know what signatures are needed.
 6609            // so we should automatically call signature_help
 6610            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6611        }
 6612
 6613        Some(cx.spawn_in(window, async move |editor, cx| {
 6614            apply_edits.await?;
 6615
 6616            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6617                let title = command.lsp_action.title().to_owned();
 6618                let project_transaction = lsp_store
 6619                    .update(cx, |lsp_store, cx| {
 6620                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6621                    })
 6622                    .await
 6623                    .context("applying post-completion command")?;
 6624                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6625                    Self::open_project_transaction(
 6626                        &editor,
 6627                        workspace.downgrade(),
 6628                        project_transaction,
 6629                        title,
 6630                        cx,
 6631                    )
 6632                    .await?;
 6633                }
 6634            }
 6635
 6636            Ok(())
 6637        }))
 6638    }
 6639
 6640    pub fn toggle_code_actions(
 6641        &mut self,
 6642        action: &ToggleCodeActions,
 6643        window: &mut Window,
 6644        cx: &mut Context<Self>,
 6645    ) {
 6646        let quick_launch = action.quick_launch;
 6647        let mut context_menu = self.context_menu.borrow_mut();
 6648        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6649            if code_actions.deployed_from == action.deployed_from {
 6650                // Toggle if we're selecting the same one
 6651                *context_menu = None;
 6652                cx.notify();
 6653                return;
 6654            } else {
 6655                // Otherwise, clear it and start a new one
 6656                *context_menu = None;
 6657                cx.notify();
 6658            }
 6659        }
 6660        drop(context_menu);
 6661        let snapshot = self.snapshot(window, cx);
 6662        let deployed_from = action.deployed_from.clone();
 6663        let action = action.clone();
 6664        self.completion_tasks.clear();
 6665        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6666
 6667        let multibuffer_point = match &action.deployed_from {
 6668            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6669                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6670            }
 6671            _ => self
 6672                .selections
 6673                .newest::<Point>(&snapshot.display_snapshot)
 6674                .head(),
 6675        };
 6676        let Some((buffer, buffer_row)) = snapshot
 6677            .buffer_snapshot()
 6678            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6679            .and_then(|(buffer_snapshot, range)| {
 6680                self.buffer()
 6681                    .read(cx)
 6682                    .buffer(buffer_snapshot.remote_id())
 6683                    .map(|buffer| (buffer, range.start.row))
 6684            })
 6685        else {
 6686            return;
 6687        };
 6688        let buffer_id = buffer.read(cx).remote_id();
 6689        let tasks = self
 6690            .runnables
 6691            .runnables((buffer_id, buffer_row))
 6692            .map(|t| Arc::new(t.to_owned()));
 6693
 6694        if !self.focus_handle.is_focused(window) {
 6695            return;
 6696        }
 6697        let project = self.project.clone();
 6698
 6699        let code_actions_task = match deployed_from {
 6700            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6701            _ => self.code_actions(buffer_row, window, cx),
 6702        };
 6703
 6704        let runnable_task = match deployed_from {
 6705            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6706            _ => {
 6707                let mut task_context_task = Task::ready(None);
 6708                if let Some(tasks) = &tasks
 6709                    && let Some(project) = project
 6710                {
 6711                    task_context_task =
 6712                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6713                }
 6714
 6715                cx.spawn_in(window, {
 6716                    let buffer = buffer.clone();
 6717                    async move |editor, cx| {
 6718                        let task_context = task_context_task.await;
 6719
 6720                        let resolved_tasks =
 6721                            tasks
 6722                                .zip(task_context.clone())
 6723                                .map(|(tasks, task_context)| ResolvedTasks {
 6724                                    templates: tasks.resolve(&task_context).collect(),
 6725                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6726                                        multibuffer_point.row,
 6727                                        tasks.column,
 6728                                    )),
 6729                                });
 6730                        let debug_scenarios = editor
 6731                            .update(cx, |editor, cx| {
 6732                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6733                            })?
 6734                            .await;
 6735                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6736                    }
 6737                })
 6738            }
 6739        };
 6740
 6741        cx.spawn_in(window, async move |editor, cx| {
 6742            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6743            let code_actions = code_actions_task.await;
 6744            let spawn_straight_away = quick_launch
 6745                && resolved_tasks
 6746                    .as_ref()
 6747                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6748                && code_actions
 6749                    .as_ref()
 6750                    .is_none_or(|actions| actions.is_empty())
 6751                && debug_scenarios.is_empty();
 6752
 6753            editor.update_in(cx, |editor, window, cx| {
 6754                crate::hover_popover::hide_hover(editor, cx);
 6755                let actions = CodeActionContents::new(
 6756                    resolved_tasks,
 6757                    code_actions,
 6758                    debug_scenarios,
 6759                    task_context.unwrap_or_default(),
 6760                );
 6761
 6762                // Don't show the menu if there are no actions available
 6763                if actions.is_empty() {
 6764                    cx.notify();
 6765                    return Task::ready(Ok(()));
 6766                }
 6767
 6768                *editor.context_menu.borrow_mut() =
 6769                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6770                        buffer,
 6771                        actions,
 6772                        selected_item: Default::default(),
 6773                        scroll_handle: UniformListScrollHandle::default(),
 6774                        deployed_from,
 6775                    }));
 6776                cx.notify();
 6777                if spawn_straight_away
 6778                    && let Some(task) = editor.confirm_code_action(
 6779                        &ConfirmCodeAction { item_ix: Some(0) },
 6780                        window,
 6781                        cx,
 6782                    )
 6783                {
 6784                    return task;
 6785                }
 6786
 6787                Task::ready(Ok(()))
 6788            })
 6789        })
 6790        .detach_and_log_err(cx);
 6791    }
 6792
 6793    fn debug_scenarios(
 6794        &mut self,
 6795        resolved_tasks: &Option<ResolvedTasks>,
 6796        buffer: &Entity<Buffer>,
 6797        cx: &mut App,
 6798    ) -> Task<Vec<task::DebugScenario>> {
 6799        maybe!({
 6800            let project = self.project()?;
 6801            let dap_store = project.read(cx).dap_store();
 6802            let mut scenarios = vec![];
 6803            let resolved_tasks = resolved_tasks.as_ref()?;
 6804            let buffer = buffer.read(cx);
 6805            let language = buffer.language()?;
 6806            let file = buffer.file();
 6807            let debug_adapter = language_settings(language.name().into(), file, cx)
 6808                .debuggers
 6809                .first()
 6810                .map(SharedString::from)
 6811                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6812
 6813            dap_store.update(cx, |dap_store, cx| {
 6814                for (_, task) in &resolved_tasks.templates {
 6815                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6816                        task.original_task().clone(),
 6817                        debug_adapter.clone().into(),
 6818                        task.display_label().to_owned().into(),
 6819                        cx,
 6820                    );
 6821                    scenarios.push(maybe_scenario);
 6822                }
 6823            });
 6824            Some(cx.background_spawn(async move {
 6825                futures::future::join_all(scenarios)
 6826                    .await
 6827                    .into_iter()
 6828                    .flatten()
 6829                    .collect::<Vec<_>>()
 6830            }))
 6831        })
 6832        .unwrap_or_else(|| Task::ready(vec![]))
 6833    }
 6834
 6835    fn code_actions(
 6836        &mut self,
 6837        buffer_row: u32,
 6838        window: &mut Window,
 6839        cx: &mut Context<Self>,
 6840    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6841        let mut task = self.code_actions_task.take();
 6842        cx.spawn_in(window, async move |editor, cx| {
 6843            while let Some(prev_task) = task {
 6844                prev_task.await.log_err();
 6845                task = editor
 6846                    .update(cx, |this, _| this.code_actions_task.take())
 6847                    .ok()?;
 6848            }
 6849
 6850            editor
 6851                .update(cx, |editor, cx| {
 6852                    editor
 6853                        .available_code_actions
 6854                        .clone()
 6855                        .and_then(|(location, code_actions)| {
 6856                            let snapshot = location.buffer.read(cx).snapshot();
 6857                            let point_range = location.range.to_point(&snapshot);
 6858                            let point_range = point_range.start.row..=point_range.end.row;
 6859                            if point_range.contains(&buffer_row) {
 6860                                Some(code_actions)
 6861                            } else {
 6862                                None
 6863                            }
 6864                        })
 6865                })
 6866                .ok()
 6867                .flatten()
 6868        })
 6869    }
 6870
 6871    pub fn confirm_code_action(
 6872        &mut self,
 6873        action: &ConfirmCodeAction,
 6874        window: &mut Window,
 6875        cx: &mut Context<Self>,
 6876    ) -> Option<Task<Result<()>>> {
 6877        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6878
 6879        let actions_menu =
 6880            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6881                menu
 6882            } else {
 6883                return None;
 6884            };
 6885
 6886        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6887        let action = actions_menu.actions.get(action_ix)?;
 6888        let title = action.label();
 6889        let buffer = actions_menu.buffer;
 6890        let workspace = self.workspace()?;
 6891
 6892        match action {
 6893            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6894                workspace.update(cx, |workspace, cx| {
 6895                    workspace.schedule_resolved_task(
 6896                        task_source_kind,
 6897                        resolved_task,
 6898                        false,
 6899                        window,
 6900                        cx,
 6901                    );
 6902
 6903                    Some(Task::ready(Ok(())))
 6904                })
 6905            }
 6906            CodeActionsItem::CodeAction {
 6907                excerpt_id,
 6908                action,
 6909                provider,
 6910            } => {
 6911                let apply_code_action =
 6912                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6913                let workspace = workspace.downgrade();
 6914                Some(cx.spawn_in(window, async move |editor, cx| {
 6915                    let project_transaction = apply_code_action.await?;
 6916                    Self::open_project_transaction(
 6917                        &editor,
 6918                        workspace,
 6919                        project_transaction,
 6920                        title,
 6921                        cx,
 6922                    )
 6923                    .await
 6924                }))
 6925            }
 6926            CodeActionsItem::DebugScenario(scenario) => {
 6927                let context = actions_menu.actions.context.into();
 6928
 6929                workspace.update(cx, |workspace, cx| {
 6930                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6931                    workspace.start_debug_session(
 6932                        scenario,
 6933                        context,
 6934                        Some(buffer),
 6935                        None,
 6936                        window,
 6937                        cx,
 6938                    );
 6939                });
 6940                Some(Task::ready(Ok(())))
 6941            }
 6942        }
 6943    }
 6944
 6945    fn open_transaction_for_hidden_buffers(
 6946        workspace: Entity<Workspace>,
 6947        transaction: ProjectTransaction,
 6948        title: String,
 6949        window: &mut Window,
 6950        cx: &mut Context<Self>,
 6951    ) {
 6952        if transaction.0.is_empty() {
 6953            return;
 6954        }
 6955
 6956        let edited_buffers_already_open = {
 6957            let other_editors: Vec<Entity<Editor>> = workspace
 6958                .read(cx)
 6959                .panes()
 6960                .iter()
 6961                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6962                .filter(|editor| editor.entity_id() != cx.entity_id())
 6963                .collect();
 6964
 6965            transaction.0.keys().all(|buffer| {
 6966                other_editors.iter().any(|editor| {
 6967                    let multi_buffer = editor.read(cx).buffer();
 6968                    multi_buffer.read(cx).is_singleton()
 6969                        && multi_buffer
 6970                            .read(cx)
 6971                            .as_singleton()
 6972                            .map_or(false, |singleton| {
 6973                                singleton.entity_id() == buffer.entity_id()
 6974                            })
 6975                })
 6976            })
 6977        };
 6978        if !edited_buffers_already_open {
 6979            let workspace = workspace.downgrade();
 6980            cx.defer_in(window, move |_, window, cx| {
 6981                cx.spawn_in(window, async move |editor, cx| {
 6982                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6983                        .await
 6984                        .ok()
 6985                })
 6986                .detach();
 6987            });
 6988        }
 6989    }
 6990
 6991    pub async fn open_project_transaction(
 6992        editor: &WeakEntity<Editor>,
 6993        workspace: WeakEntity<Workspace>,
 6994        transaction: ProjectTransaction,
 6995        title: String,
 6996        cx: &mut AsyncWindowContext,
 6997    ) -> Result<()> {
 6998        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6999        cx.update(|_, cx| {
 7000            entries.sort_unstable_by_key(|(buffer, _)| {
 7001                buffer.read(cx).file().map(|f| f.path().clone())
 7002            });
 7003        })?;
 7004        if entries.is_empty() {
 7005            return Ok(());
 7006        }
 7007
 7008        // If the project transaction's edits are all contained within this editor, then
 7009        // avoid opening a new editor to display them.
 7010
 7011        if let [(buffer, transaction)] = &*entries {
 7012            let excerpt = editor.update(cx, |editor, cx| {
 7013                editor
 7014                    .buffer()
 7015                    .read(cx)
 7016                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7017            })?;
 7018            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7019                && excerpted_buffer == *buffer
 7020            {
 7021                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7022                    let excerpt_range = excerpt_range.to_offset(buffer);
 7023                    buffer
 7024                        .edited_ranges_for_transaction::<usize>(transaction)
 7025                        .all(|range| {
 7026                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7027                        })
 7028                });
 7029
 7030                if all_edits_within_excerpt {
 7031                    return Ok(());
 7032                }
 7033            }
 7034        }
 7035
 7036        let mut ranges_to_highlight = Vec::new();
 7037        let excerpt_buffer = cx.new(|cx| {
 7038            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7039            for (buffer_handle, transaction) in &entries {
 7040                let edited_ranges = buffer_handle
 7041                    .read(cx)
 7042                    .edited_ranges_for_transaction::<Point>(transaction)
 7043                    .collect::<Vec<_>>();
 7044                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7045                    PathKey::for_buffer(buffer_handle, cx),
 7046                    buffer_handle.clone(),
 7047                    edited_ranges,
 7048                    multibuffer_context_lines(cx),
 7049                    cx,
 7050                );
 7051
 7052                ranges_to_highlight.extend(ranges);
 7053            }
 7054            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7055            multibuffer
 7056        });
 7057
 7058        workspace.update_in(cx, |workspace, window, cx| {
 7059            let project = workspace.project().clone();
 7060            let editor =
 7061                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7062            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7063            editor.update(cx, |editor, cx| {
 7064                editor.highlight_background(
 7065                    HighlightKey::Editor,
 7066                    &ranges_to_highlight,
 7067                    |_, theme| theme.colors().editor_highlighted_line_background,
 7068                    cx,
 7069                );
 7070            });
 7071        })?;
 7072
 7073        Ok(())
 7074    }
 7075
 7076    pub fn clear_code_action_providers(&mut self) {
 7077        self.code_action_providers.clear();
 7078        self.available_code_actions.take();
 7079    }
 7080
 7081    pub fn add_code_action_provider(
 7082        &mut self,
 7083        provider: Rc<dyn CodeActionProvider>,
 7084        window: &mut Window,
 7085        cx: &mut Context<Self>,
 7086    ) {
 7087        if self
 7088            .code_action_providers
 7089            .iter()
 7090            .any(|existing_provider| existing_provider.id() == provider.id())
 7091        {
 7092            return;
 7093        }
 7094
 7095        self.code_action_providers.push(provider);
 7096        self.refresh_code_actions(window, cx);
 7097    }
 7098
 7099    pub fn remove_code_action_provider(
 7100        &mut self,
 7101        id: Arc<str>,
 7102        window: &mut Window,
 7103        cx: &mut Context<Self>,
 7104    ) {
 7105        self.code_action_providers
 7106            .retain(|provider| provider.id() != id);
 7107        self.refresh_code_actions(window, cx);
 7108    }
 7109
 7110    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7111        !self.code_action_providers.is_empty()
 7112            && EditorSettings::get_global(cx).toolbar.code_actions
 7113    }
 7114
 7115    pub fn has_available_code_actions(&self) -> bool {
 7116        self.available_code_actions
 7117            .as_ref()
 7118            .is_some_and(|(_, actions)| !actions.is_empty())
 7119    }
 7120
 7121    fn render_inline_code_actions(
 7122        &self,
 7123        icon_size: ui::IconSize,
 7124        display_row: DisplayRow,
 7125        is_active: bool,
 7126        cx: &mut Context<Self>,
 7127    ) -> AnyElement {
 7128        let show_tooltip = !self.context_menu_visible();
 7129        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7130            .icon_size(icon_size)
 7131            .shape(ui::IconButtonShape::Square)
 7132            .icon_color(ui::Color::Hidden)
 7133            .toggle_state(is_active)
 7134            .when(show_tooltip, |this| {
 7135                this.tooltip({
 7136                    let focus_handle = self.focus_handle.clone();
 7137                    move |_window, cx| {
 7138                        Tooltip::for_action_in(
 7139                            "Toggle Code Actions",
 7140                            &ToggleCodeActions {
 7141                                deployed_from: None,
 7142                                quick_launch: false,
 7143                            },
 7144                            &focus_handle,
 7145                            cx,
 7146                        )
 7147                    }
 7148                })
 7149            })
 7150            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7151                window.focus(&editor.focus_handle(cx), cx);
 7152                editor.toggle_code_actions(
 7153                    &crate::actions::ToggleCodeActions {
 7154                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7155                            display_row,
 7156                        )),
 7157                        quick_launch: false,
 7158                    },
 7159                    window,
 7160                    cx,
 7161                );
 7162            }))
 7163            .into_any_element()
 7164    }
 7165
 7166    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7167        &self.context_menu
 7168    }
 7169
 7170    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7171        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7172            cx.background_executor()
 7173                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7174                .await;
 7175
 7176            let (start_buffer, start, _, end, newest_selection) = this
 7177                .update(cx, |this, cx| {
 7178                    let newest_selection = this.selections.newest_anchor().clone();
 7179                    if newest_selection.head().diff_base_anchor.is_some() {
 7180                        return None;
 7181                    }
 7182                    let display_snapshot = this.display_snapshot(cx);
 7183                    let newest_selection_adjusted =
 7184                        this.selections.newest_adjusted(&display_snapshot);
 7185                    let buffer = this.buffer.read(cx);
 7186
 7187                    let (start_buffer, start) =
 7188                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7189                    let (end_buffer, end) =
 7190                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7191
 7192                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7193                })?
 7194                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7195                .context(
 7196                    "Expected selection to lie in a single buffer when refreshing code actions",
 7197                )?;
 7198            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7199                let providers = this.code_action_providers.clone();
 7200                let tasks = this
 7201                    .code_action_providers
 7202                    .iter()
 7203                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7204                    .collect::<Vec<_>>();
 7205                (providers, tasks)
 7206            })?;
 7207
 7208            let mut actions = Vec::new();
 7209            for (provider, provider_actions) in
 7210                providers.into_iter().zip(future::join_all(tasks).await)
 7211            {
 7212                if let Some(provider_actions) = provider_actions.log_err() {
 7213                    actions.extend(provider_actions.into_iter().map(|action| {
 7214                        AvailableCodeAction {
 7215                            excerpt_id: newest_selection.start.excerpt_id,
 7216                            action,
 7217                            provider: provider.clone(),
 7218                        }
 7219                    }));
 7220                }
 7221            }
 7222
 7223            this.update(cx, |this, cx| {
 7224                this.available_code_actions = if actions.is_empty() {
 7225                    None
 7226                } else {
 7227                    Some((
 7228                        Location {
 7229                            buffer: start_buffer,
 7230                            range: start..end,
 7231                        },
 7232                        actions.into(),
 7233                    ))
 7234                };
 7235                cx.notify();
 7236            })
 7237        }));
 7238    }
 7239
 7240    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7241        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7242            self.show_git_blame_inline = false;
 7243
 7244            self.show_git_blame_inline_delay_task =
 7245                Some(cx.spawn_in(window, async move |this, cx| {
 7246                    cx.background_executor().timer(delay).await;
 7247
 7248                    this.update(cx, |this, cx| {
 7249                        this.show_git_blame_inline = true;
 7250                        cx.notify();
 7251                    })
 7252                    .log_err();
 7253                }));
 7254        }
 7255    }
 7256
 7257    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7258        let snapshot = self.snapshot(window, cx);
 7259        let cursor = self
 7260            .selections
 7261            .newest::<Point>(&snapshot.display_snapshot)
 7262            .head();
 7263        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7264        else {
 7265            return;
 7266        };
 7267
 7268        if self.blame.is_none() {
 7269            self.start_git_blame(true, window, cx);
 7270        }
 7271        let Some(blame) = self.blame.as_ref() else {
 7272            return;
 7273        };
 7274
 7275        let row_info = RowInfo {
 7276            buffer_id: Some(buffer.remote_id()),
 7277            buffer_row: Some(point.row),
 7278            ..Default::default()
 7279        };
 7280        let Some((buffer, blame_entry)) = blame
 7281            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7282            .flatten()
 7283        else {
 7284            return;
 7285        };
 7286
 7287        let anchor = self.selections.newest_anchor().head();
 7288        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7289        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7290            self.show_blame_popover(
 7291                buffer,
 7292                &blame_entry,
 7293                position + last_bounds.origin,
 7294                true,
 7295                cx,
 7296            );
 7297        };
 7298    }
 7299
 7300    fn show_blame_popover(
 7301        &mut self,
 7302        buffer: BufferId,
 7303        blame_entry: &BlameEntry,
 7304        position: gpui::Point<Pixels>,
 7305        ignore_timeout: bool,
 7306        cx: &mut Context<Self>,
 7307    ) {
 7308        if let Some(state) = &mut self.inline_blame_popover {
 7309            state.hide_task.take();
 7310        } else {
 7311            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7312            let blame_entry = blame_entry.clone();
 7313            let show_task = cx.spawn(async move |editor, cx| {
 7314                if !ignore_timeout {
 7315                    cx.background_executor()
 7316                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7317                        .await;
 7318                }
 7319                editor
 7320                    .update(cx, |editor, cx| {
 7321                        editor.inline_blame_popover_show_task.take();
 7322                        let Some(blame) = editor.blame.as_ref() else {
 7323                            return;
 7324                        };
 7325                        let blame = blame.read(cx);
 7326                        let details = blame.details_for_entry(buffer, &blame_entry);
 7327                        let markdown = cx.new(|cx| {
 7328                            Markdown::new(
 7329                                details
 7330                                    .as_ref()
 7331                                    .map(|message| message.message.clone())
 7332                                    .unwrap_or_default(),
 7333                                None,
 7334                                None,
 7335                                cx,
 7336                            )
 7337                        });
 7338                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7339                            position,
 7340                            hide_task: None,
 7341                            popover_bounds: None,
 7342                            popover_state: InlineBlamePopoverState {
 7343                                scroll_handle: ScrollHandle::new(),
 7344                                commit_message: details,
 7345                                markdown,
 7346                            },
 7347                            keyboard_grace: ignore_timeout,
 7348                        });
 7349                        cx.notify();
 7350                    })
 7351                    .ok();
 7352            });
 7353            self.inline_blame_popover_show_task = Some(show_task);
 7354        }
 7355    }
 7356
 7357    pub fn has_mouse_context_menu(&self) -> bool {
 7358        self.mouse_context_menu.is_some()
 7359    }
 7360
 7361    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7362        self.inline_blame_popover_show_task.take();
 7363        if let Some(state) = &mut self.inline_blame_popover {
 7364            let hide_task = cx.spawn(async move |editor, cx| {
 7365                if !ignore_timeout {
 7366                    cx.background_executor()
 7367                        .timer(std::time::Duration::from_millis(100))
 7368                        .await;
 7369                }
 7370                editor
 7371                    .update(cx, |editor, cx| {
 7372                        editor.inline_blame_popover.take();
 7373                        cx.notify();
 7374                    })
 7375                    .ok();
 7376            });
 7377            state.hide_task = Some(hide_task);
 7378            true
 7379        } else {
 7380            false
 7381        }
 7382    }
 7383
 7384    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7385        if self.pending_rename.is_some() {
 7386            return None;
 7387        }
 7388
 7389        let provider = self.semantics_provider.clone()?;
 7390        let buffer = self.buffer.read(cx);
 7391        let newest_selection = self.selections.newest_anchor().clone();
 7392        let cursor_position = newest_selection.head();
 7393        let (cursor_buffer, cursor_buffer_position) =
 7394            buffer.text_anchor_for_position(cursor_position, cx)?;
 7395        let (tail_buffer, tail_buffer_position) =
 7396            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7397        if cursor_buffer != tail_buffer {
 7398            return None;
 7399        }
 7400
 7401        let snapshot = cursor_buffer.read(cx).snapshot();
 7402        let word_ranges = cx.background_spawn(async move {
 7403            // this might look odd to put on the background thread, but
 7404            // `surrounding_word` can be quite expensive as it calls into
 7405            // tree-sitter language scopes
 7406            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7407            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7408            (start_word_range, end_word_range)
 7409        });
 7410
 7411        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7412        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7413            let (start_word_range, end_word_range) = word_ranges.await;
 7414            if start_word_range != end_word_range {
 7415                this.update(cx, |this, cx| {
 7416                    this.document_highlights_task.take();
 7417                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7418                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7419                })
 7420                .ok();
 7421                return;
 7422            }
 7423            cx.background_executor()
 7424                .timer(Duration::from_millis(debounce))
 7425                .await;
 7426
 7427            let highlights = if let Some(highlights) = cx.update(|cx| {
 7428                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7429            }) {
 7430                highlights.await.log_err()
 7431            } else {
 7432                None
 7433            };
 7434
 7435            if let Some(highlights) = highlights {
 7436                this.update(cx, |this, cx| {
 7437                    if this.pending_rename.is_some() {
 7438                        return;
 7439                    }
 7440
 7441                    let buffer = this.buffer.read(cx);
 7442                    if buffer
 7443                        .text_anchor_for_position(cursor_position, cx)
 7444                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7445                    {
 7446                        return;
 7447                    }
 7448
 7449                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7450                    let mut write_ranges = Vec::new();
 7451                    let mut read_ranges = Vec::new();
 7452                    for highlight in highlights {
 7453                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7454                        for (excerpt_id, _, excerpt_range) in
 7455                            buffer.excerpts_for_buffer(buffer_id, cx)
 7456                        {
 7457                            let start = highlight
 7458                                .range
 7459                                .start
 7460                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7461                            let end = highlight
 7462                                .range
 7463                                .end
 7464                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7465                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7466                                continue;
 7467                            }
 7468
 7469                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7470                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7471                                write_ranges.push(range);
 7472                            } else {
 7473                                read_ranges.push(range);
 7474                            }
 7475                        }
 7476                    }
 7477
 7478                    this.highlight_background(
 7479                        HighlightKey::DocumentHighlightRead,
 7480                        &read_ranges,
 7481                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7482                        cx,
 7483                    );
 7484                    this.highlight_background(
 7485                        HighlightKey::DocumentHighlightWrite,
 7486                        &write_ranges,
 7487                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7488                        cx,
 7489                    );
 7490                    cx.notify();
 7491                })
 7492                .log_err();
 7493            }
 7494        }));
 7495        None
 7496    }
 7497
 7498    fn prepare_highlight_query_from_selection(
 7499        &mut self,
 7500        snapshot: &DisplaySnapshot,
 7501        cx: &mut Context<Editor>,
 7502    ) -> Option<(String, Range<Anchor>)> {
 7503        if matches!(self.mode, EditorMode::SingleLine) {
 7504            return None;
 7505        }
 7506        if !EditorSettings::get_global(cx).selection_highlight {
 7507            return None;
 7508        }
 7509        if self.selections.count() != 1 || self.selections.line_mode() {
 7510            return None;
 7511        }
 7512        let selection = self.selections.newest::<Point>(&snapshot);
 7513        // If the selection spans multiple rows OR it is empty
 7514        if selection.start.row != selection.end.row
 7515            || selection.start.column == selection.end.column
 7516        {
 7517            return None;
 7518        }
 7519        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7520        let query = snapshot
 7521            .buffer_snapshot()
 7522            .text_for_range(selection_anchor_range.clone())
 7523            .collect::<String>();
 7524        if query.trim().is_empty() {
 7525            return None;
 7526        }
 7527        Some((query, selection_anchor_range))
 7528    }
 7529
 7530    #[ztracing::instrument(skip_all)]
 7531    fn update_selection_occurrence_highlights(
 7532        &mut self,
 7533        multi_buffer_snapshot: MultiBufferSnapshot,
 7534        query_text: String,
 7535        query_range: Range<Anchor>,
 7536        multi_buffer_range_to_query: Range<Point>,
 7537        use_debounce: bool,
 7538        window: &mut Window,
 7539        cx: &mut Context<Editor>,
 7540    ) -> Task<()> {
 7541        cx.spawn_in(window, async move |editor, cx| {
 7542            if use_debounce {
 7543                cx.background_executor()
 7544                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7545                    .await;
 7546            }
 7547            let match_task = cx.background_spawn(async move {
 7548                let buffer_ranges = multi_buffer_snapshot
 7549                    .range_to_buffer_ranges(
 7550                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7551                    )
 7552                    .into_iter()
 7553                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7554                let mut match_ranges = Vec::new();
 7555                let Ok(regex) = project::search::SearchQuery::text(
 7556                    query_text,
 7557                    false,
 7558                    false,
 7559                    false,
 7560                    Default::default(),
 7561                    Default::default(),
 7562                    false,
 7563                    None,
 7564                ) else {
 7565                    return Vec::default();
 7566                };
 7567                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7568                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7569                    match_ranges.extend(
 7570                        regex
 7571                            .search(
 7572                                buffer_snapshot,
 7573                                Some(search_range.start.0..search_range.end.0),
 7574                            )
 7575                            .await
 7576                            .into_iter()
 7577                            .filter_map(|match_range| {
 7578                                let match_start = buffer_snapshot
 7579                                    .anchor_after(search_range.start + match_range.start);
 7580                                let match_end = buffer_snapshot
 7581                                    .anchor_before(search_range.start + match_range.end);
 7582                                let match_anchor_range =
 7583                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7584                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7585                            }),
 7586                    );
 7587                }
 7588                match_ranges
 7589            });
 7590            let match_ranges = match_task.await;
 7591            editor
 7592                .update_in(cx, |editor, _, cx| {
 7593                    if use_debounce {
 7594                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7595                        editor.debounced_selection_highlight_complete = true;
 7596                    } else if editor.debounced_selection_highlight_complete {
 7597                        return;
 7598                    }
 7599                    if !match_ranges.is_empty() {
 7600                        editor.highlight_background(
 7601                            HighlightKey::SelectedTextHighlight,
 7602                            &match_ranges,
 7603                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7604                            cx,
 7605                        )
 7606                    }
 7607                })
 7608                .log_err();
 7609        })
 7610    }
 7611
 7612    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7613        struct NewlineFold;
 7614        let type_id = std::any::TypeId::of::<NewlineFold>();
 7615        if !self.mode.is_single_line() {
 7616            return;
 7617        }
 7618        let snapshot = self.snapshot(window, cx);
 7619        if snapshot.buffer_snapshot().max_point().row == 0 {
 7620            return;
 7621        }
 7622        let task = cx.background_spawn(async move {
 7623            let new_newlines = snapshot
 7624                .buffer_chars_at(MultiBufferOffset(0))
 7625                .filter_map(|(c, i)| {
 7626                    if c == '\n' {
 7627                        Some(
 7628                            snapshot.buffer_snapshot().anchor_after(i)
 7629                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7630                        )
 7631                    } else {
 7632                        None
 7633                    }
 7634                })
 7635                .collect::<Vec<_>>();
 7636            let existing_newlines = snapshot
 7637                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7638                .filter_map(|fold| {
 7639                    if fold.placeholder.type_tag == Some(type_id) {
 7640                        Some(fold.range.start..fold.range.end)
 7641                    } else {
 7642                        None
 7643                    }
 7644                })
 7645                .collect::<Vec<_>>();
 7646
 7647            (new_newlines, existing_newlines)
 7648        });
 7649        self.folding_newlines = cx.spawn(async move |this, cx| {
 7650            let (new_newlines, existing_newlines) = task.await;
 7651            if new_newlines == existing_newlines {
 7652                return;
 7653            }
 7654            let placeholder = FoldPlaceholder {
 7655                render: Arc::new(move |_, _, cx| {
 7656                    div()
 7657                        .bg(cx.theme().status().hint_background)
 7658                        .border_b_1()
 7659                        .size_full()
 7660                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7661                        .border_color(cx.theme().status().hint)
 7662                        .child("\\n")
 7663                        .into_any()
 7664                }),
 7665                constrain_width: false,
 7666                merge_adjacent: false,
 7667                type_tag: Some(type_id),
 7668                collapsed_text: None,
 7669            };
 7670            let creases = new_newlines
 7671                .into_iter()
 7672                .map(|range| Crease::simple(range, placeholder.clone()))
 7673                .collect();
 7674            this.update(cx, |this, cx| {
 7675                this.display_map.update(cx, |display_map, cx| {
 7676                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7677                    display_map.fold(creases, cx);
 7678                });
 7679            })
 7680            .ok();
 7681        });
 7682    }
 7683
 7684    #[ztracing::instrument(skip_all)]
 7685    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7686        if !self.lsp_data_enabled() {
 7687            return;
 7688        }
 7689        let cursor = self.selections.newest_anchor().head();
 7690        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7691
 7692        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7693            self.outline_symbols_at_cursor =
 7694                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7695            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7696            cx.notify();
 7697        } else {
 7698            let syntax = cx.theme().syntax().clone();
 7699            let background_task = cx.background_spawn(async move {
 7700                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7701            });
 7702            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7703                cx.spawn(async move |this, cx| {
 7704                    let symbols = background_task.await;
 7705                    this.update(cx, |this, cx| {
 7706                        this.outline_symbols_at_cursor = symbols;
 7707                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7708                        cx.notify();
 7709                    })
 7710                    .ok();
 7711                });
 7712        }
 7713    }
 7714
 7715    #[ztracing::instrument(skip_all)]
 7716    fn refresh_selected_text_highlights(
 7717        &mut self,
 7718        snapshot: &DisplaySnapshot,
 7719        on_buffer_edit: bool,
 7720        window: &mut Window,
 7721        cx: &mut Context<Editor>,
 7722    ) {
 7723        let Some((query_text, query_range)) =
 7724            self.prepare_highlight_query_from_selection(snapshot, cx)
 7725        else {
 7726            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7727            self.quick_selection_highlight_task.take();
 7728            self.debounced_selection_highlight_task.take();
 7729            self.debounced_selection_highlight_complete = false;
 7730            return;
 7731        };
 7732        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7733        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7734        let query_changed = self
 7735            .quick_selection_highlight_task
 7736            .as_ref()
 7737            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7738        if query_changed {
 7739            self.debounced_selection_highlight_complete = false;
 7740        }
 7741        if on_buffer_edit || query_changed {
 7742            self.quick_selection_highlight_task = Some((
 7743                query_range.clone(),
 7744                self.update_selection_occurrence_highlights(
 7745                    snapshot.buffer.clone(),
 7746                    query_text.clone(),
 7747                    query_range.clone(),
 7748                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7749                    false,
 7750                    window,
 7751                    cx,
 7752                ),
 7753            ));
 7754        }
 7755        if on_buffer_edit
 7756            || self
 7757                .debounced_selection_highlight_task
 7758                .as_ref()
 7759                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7760        {
 7761            let multi_buffer_start = multi_buffer_snapshot
 7762                .anchor_before(MultiBufferOffset(0))
 7763                .to_point(&multi_buffer_snapshot);
 7764            let multi_buffer_end = multi_buffer_snapshot
 7765                .anchor_after(multi_buffer_snapshot.len())
 7766                .to_point(&multi_buffer_snapshot);
 7767            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7768            self.debounced_selection_highlight_task = Some((
 7769                query_range.clone(),
 7770                self.update_selection_occurrence_highlights(
 7771                    snapshot.buffer.clone(),
 7772                    query_text,
 7773                    query_range,
 7774                    multi_buffer_full_range,
 7775                    true,
 7776                    window,
 7777                    cx,
 7778                ),
 7779            ));
 7780        }
 7781    }
 7782
 7783    pub fn multi_buffer_visible_range(
 7784        &self,
 7785        display_snapshot: &DisplaySnapshot,
 7786        cx: &App,
 7787    ) -> Range<Point> {
 7788        let visible_start = self
 7789            .scroll_manager
 7790            .native_anchor(display_snapshot, cx)
 7791            .anchor
 7792            .to_point(display_snapshot.buffer_snapshot())
 7793            .to_display_point(display_snapshot);
 7794
 7795        let mut target_end = visible_start;
 7796        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7797
 7798        visible_start.to_point(display_snapshot)
 7799            ..display_snapshot
 7800                .clip_point(target_end, Bias::Right)
 7801                .to_point(display_snapshot)
 7802    }
 7803
 7804    pub fn refresh_edit_prediction(
 7805        &mut self,
 7806        debounce: bool,
 7807        user_requested: bool,
 7808        window: &mut Window,
 7809        cx: &mut Context<Self>,
 7810    ) -> Option<()> {
 7811        if self.leader_id.is_some() {
 7812            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7813            return None;
 7814        }
 7815
 7816        let cursor = self.selections.newest_anchor().head();
 7817        let (buffer, cursor_buffer_position) =
 7818            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7819
 7820        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7821            return None;
 7822        }
 7823
 7824        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7825            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7826            return None;
 7827        }
 7828
 7829        self.update_visible_edit_prediction(window, cx);
 7830
 7831        if !user_requested
 7832            && (!self.should_show_edit_predictions()
 7833                || !self.is_focused(window)
 7834                || buffer.read(cx).is_empty())
 7835        {
 7836            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7837            return None;
 7838        }
 7839
 7840        self.edit_prediction_provider()?
 7841            .refresh(buffer, cursor_buffer_position, debounce, cx);
 7842        Some(())
 7843    }
 7844
 7845    fn show_edit_predictions_in_menu(&self) -> bool {
 7846        match self.edit_prediction_settings {
 7847            EditPredictionSettings::Disabled => false,
 7848            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7849        }
 7850    }
 7851
 7852    pub fn edit_predictions_enabled(&self) -> bool {
 7853        match self.edit_prediction_settings {
 7854            EditPredictionSettings::Disabled => false,
 7855            EditPredictionSettings::Enabled { .. } => true,
 7856        }
 7857    }
 7858
 7859    fn edit_prediction_requires_modifier(&self) -> bool {
 7860        match self.edit_prediction_settings {
 7861            EditPredictionSettings::Disabled => false,
 7862            EditPredictionSettings::Enabled {
 7863                preview_requires_modifier,
 7864                ..
 7865            } => preview_requires_modifier,
 7866        }
 7867    }
 7868
 7869    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7870        if self.edit_prediction_provider.is_none() {
 7871            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7872            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7873            return;
 7874        }
 7875
 7876        let selection = self.selections.newest_anchor();
 7877        let cursor = selection.head();
 7878
 7879        if let Some((buffer, cursor_buffer_position)) =
 7880            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7881        {
 7882            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7883                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7884                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7885                return;
 7886            }
 7887            self.edit_prediction_settings =
 7888                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7889        }
 7890    }
 7891
 7892    fn edit_prediction_settings_at_position(
 7893        &self,
 7894        buffer: &Entity<Buffer>,
 7895        buffer_position: language::Anchor,
 7896        cx: &App,
 7897    ) -> EditPredictionSettings {
 7898        if !self.mode.is_full()
 7899            || !self.show_edit_predictions_override.unwrap_or(true)
 7900            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7901        {
 7902            return EditPredictionSettings::Disabled;
 7903        }
 7904
 7905        let buffer = buffer.read(cx);
 7906
 7907        let file = buffer.file();
 7908
 7909        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7910            return EditPredictionSettings::Disabled;
 7911        };
 7912
 7913        let by_provider = matches!(
 7914            self.menu_edit_predictions_policy,
 7915            MenuEditPredictionsPolicy::ByProvider
 7916        );
 7917
 7918        let show_in_menu = by_provider
 7919            && self
 7920                .edit_prediction_provider
 7921                .as_ref()
 7922                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7923
 7924        let preview_requires_modifier =
 7925            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7926
 7927        EditPredictionSettings::Enabled {
 7928            show_in_menu,
 7929            preview_requires_modifier,
 7930        }
 7931    }
 7932
 7933    fn should_show_edit_predictions(&self) -> bool {
 7934        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7935    }
 7936
 7937    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7938        matches!(
 7939            self.edit_prediction_preview,
 7940            EditPredictionPreview::Active { .. }
 7941        )
 7942    }
 7943
 7944    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7945        let cursor = self.selections.newest_anchor().head();
 7946        if let Some((buffer, cursor_position)) =
 7947            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7948        {
 7949            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7950        } else {
 7951            false
 7952        }
 7953    }
 7954
 7955    pub fn supports_minimap(&self, cx: &App) -> bool {
 7956        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7957    }
 7958
 7959    fn edit_predictions_enabled_in_buffer(
 7960        &self,
 7961        buffer: &Entity<Buffer>,
 7962        buffer_position: language::Anchor,
 7963        cx: &App,
 7964    ) -> bool {
 7965        maybe!({
 7966            if self.read_only(cx) || self.leader_id.is_some() {
 7967                return Some(false);
 7968            }
 7969            let provider = self.edit_prediction_provider()?;
 7970            if !provider.is_enabled(buffer, buffer_position, cx) {
 7971                return Some(false);
 7972            }
 7973            let buffer = buffer.read(cx);
 7974            let Some(file) = buffer.file() else {
 7975                return Some(true);
 7976            };
 7977            let settings = all_language_settings(Some(file), cx);
 7978            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7979        })
 7980        .unwrap_or(false)
 7981    }
 7982
 7983    pub fn show_edit_prediction(
 7984        &mut self,
 7985        _: &ShowEditPrediction,
 7986        window: &mut Window,
 7987        cx: &mut Context<Self>,
 7988    ) {
 7989        if !self.has_active_edit_prediction() {
 7990            self.refresh_edit_prediction(false, true, window, cx);
 7991            return;
 7992        }
 7993
 7994        self.update_visible_edit_prediction(window, cx);
 7995    }
 7996
 7997    pub fn display_cursor_names(
 7998        &mut self,
 7999        _: &DisplayCursorNames,
 8000        window: &mut Window,
 8001        cx: &mut Context<Self>,
 8002    ) {
 8003        self.show_cursor_names(window, cx);
 8004    }
 8005
 8006    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8007        self.show_cursor_names = true;
 8008        cx.notify();
 8009        cx.spawn_in(window, async move |this, cx| {
 8010            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8011            this.update(cx, |this, cx| {
 8012                this.show_cursor_names = false;
 8013                cx.notify()
 8014            })
 8015            .ok()
 8016        })
 8017        .detach();
 8018    }
 8019
 8020    pub fn accept_partial_edit_prediction(
 8021        &mut self,
 8022        granularity: EditPredictionGranularity,
 8023        window: &mut Window,
 8024        cx: &mut Context<Self>,
 8025    ) {
 8026        if self.show_edit_predictions_in_menu() {
 8027            self.hide_context_menu(window, cx);
 8028        }
 8029
 8030        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8031            return;
 8032        };
 8033
 8034        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8035            return;
 8036        }
 8037
 8038        match &active_edit_prediction.completion {
 8039            EditPrediction::MoveWithin { target, .. } => {
 8040                let target = *target;
 8041
 8042                if matches!(granularity, EditPredictionGranularity::Full) {
 8043                    if let Some(position_map) = &self.last_position_map {
 8044                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8045                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8046
 8047                        if is_visible || !self.edit_prediction_requires_modifier() {
 8048                            self.unfold_ranges(&[target..target], true, false, cx);
 8049                            self.change_selections(
 8050                                SelectionEffects::scroll(Autoscroll::newest()),
 8051                                window,
 8052                                cx,
 8053                                |selections| {
 8054                                    selections.select_anchor_ranges([target..target]);
 8055                                },
 8056                            );
 8057                            self.clear_row_highlights::<EditPredictionPreview>();
 8058                            self.edit_prediction_preview
 8059                                .set_previous_scroll_position(None);
 8060                        } else {
 8061                            // Highlight and request scroll
 8062                            self.edit_prediction_preview
 8063                                .set_previous_scroll_position(Some(
 8064                                    position_map.snapshot.scroll_anchor,
 8065                                ));
 8066                            self.highlight_rows::<EditPredictionPreview>(
 8067                                target..target,
 8068                                cx.theme().colors().editor_highlighted_line_background,
 8069                                RowHighlightOptions {
 8070                                    autoscroll: true,
 8071                                    ..Default::default()
 8072                                },
 8073                                cx,
 8074                            );
 8075                            self.request_autoscroll(Autoscroll::fit(), cx);
 8076                        }
 8077                    }
 8078                } else {
 8079                    self.change_selections(
 8080                        SelectionEffects::scroll(Autoscroll::newest()),
 8081                        window,
 8082                        cx,
 8083                        |selections| {
 8084                            selections.select_anchor_ranges([target..target]);
 8085                        },
 8086                    );
 8087                }
 8088            }
 8089            EditPrediction::MoveOutside { snapshot, target } => {
 8090                if let Some(workspace) = self.workspace() {
 8091                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8092                        .detach_and_log_err(cx);
 8093                }
 8094            }
 8095            EditPrediction::Edit {
 8096                edits,
 8097                cursor_position,
 8098                ..
 8099            } => {
 8100                self.report_edit_prediction_event(
 8101                    active_edit_prediction.completion_id.clone(),
 8102                    true,
 8103                    cx,
 8104                );
 8105
 8106                match granularity {
 8107                    EditPredictionGranularity::Full => {
 8108                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8109
 8110                        // Compute fallback cursor position BEFORE applying the edit,
 8111                        // so the anchor tracks through the edit correctly
 8112                        let fallback_cursor_target = {
 8113                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8114                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8115                        };
 8116
 8117                        self.buffer.update(cx, |buffer, cx| {
 8118                            buffer.edit(edits.iter().cloned(), None, cx)
 8119                        });
 8120
 8121                        if let Some(provider) = self.edit_prediction_provider() {
 8122                            provider.accept(cx);
 8123                        }
 8124
 8125                        // Resolve cursor position after the edit is applied
 8126                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8127                            // The anchor tracks through the edit, then we add the offset
 8128                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8129                            let base_offset = anchor.to_offset(&snapshot).0;
 8130                            let target_offset =
 8131                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8132                            snapshot.anchor_after(target_offset)
 8133                        } else {
 8134                            fallback_cursor_target
 8135                        };
 8136
 8137                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8138                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8139                        });
 8140
 8141                        let selections = self.selections.disjoint_anchors_arc();
 8142                        if let Some(transaction_id_now) =
 8143                            self.buffer.read(cx).last_transaction_id(cx)
 8144                        {
 8145                            if transaction_id_prev != Some(transaction_id_now) {
 8146                                self.selection_history
 8147                                    .insert_transaction(transaction_id_now, selections);
 8148                            }
 8149                        }
 8150
 8151                        self.update_visible_edit_prediction(window, cx);
 8152                        if self.active_edit_prediction.is_none() {
 8153                            self.refresh_edit_prediction(true, true, window, cx);
 8154                        }
 8155                        cx.notify();
 8156                    }
 8157                    _ => {
 8158                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8159                        let cursor_offset = self
 8160                            .selections
 8161                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8162                            .head();
 8163
 8164                        let insertion = edits.iter().find_map(|(range, text)| {
 8165                            let range = range.to_offset(&snapshot);
 8166                            if range.is_empty() && range.start == cursor_offset {
 8167                                Some(text)
 8168                            } else {
 8169                                None
 8170                            }
 8171                        });
 8172
 8173                        if let Some(text) = insertion {
 8174                            let text_to_insert = match granularity {
 8175                                EditPredictionGranularity::Word => {
 8176                                    let mut partial = text
 8177                                        .chars()
 8178                                        .by_ref()
 8179                                        .take_while(|c| c.is_alphabetic())
 8180                                        .collect::<String>();
 8181                                    if partial.is_empty() {
 8182                                        partial = text
 8183                                            .chars()
 8184                                            .by_ref()
 8185                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8186                                            .collect::<String>();
 8187                                    }
 8188                                    partial
 8189                                }
 8190                                EditPredictionGranularity::Line => {
 8191                                    if let Some(line) = text.split_inclusive('\n').next() {
 8192                                        line.to_string()
 8193                                    } else {
 8194                                        text.to_string()
 8195                                    }
 8196                                }
 8197                                EditPredictionGranularity::Full => unreachable!(),
 8198                            };
 8199
 8200                            cx.emit(EditorEvent::InputHandled {
 8201                                utf16_range_to_replace: None,
 8202                                text: text_to_insert.clone().into(),
 8203                            });
 8204
 8205                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8206                            self.refresh_edit_prediction(true, true, window, cx);
 8207                            cx.notify();
 8208                        } else {
 8209                            self.accept_partial_edit_prediction(
 8210                                EditPredictionGranularity::Full,
 8211                                window,
 8212                                cx,
 8213                            );
 8214                        }
 8215                    }
 8216                }
 8217            }
 8218        }
 8219
 8220        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8221    }
 8222
 8223    pub fn accept_next_word_edit_prediction(
 8224        &mut self,
 8225        _: &AcceptNextWordEditPrediction,
 8226        window: &mut Window,
 8227        cx: &mut Context<Self>,
 8228    ) {
 8229        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8230    }
 8231
 8232    pub fn accept_next_line_edit_prediction(
 8233        &mut self,
 8234        _: &AcceptNextLineEditPrediction,
 8235        window: &mut Window,
 8236        cx: &mut Context<Self>,
 8237    ) {
 8238        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8239    }
 8240
 8241    pub fn accept_edit_prediction(
 8242        &mut self,
 8243        _: &AcceptEditPrediction,
 8244        window: &mut Window,
 8245        cx: &mut Context<Self>,
 8246    ) {
 8247        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8248    }
 8249
 8250    fn discard_edit_prediction(
 8251        &mut self,
 8252        reason: EditPredictionDiscardReason,
 8253        cx: &mut Context<Self>,
 8254    ) -> bool {
 8255        if reason == EditPredictionDiscardReason::Rejected {
 8256            let completion_id = self
 8257                .active_edit_prediction
 8258                .as_ref()
 8259                .and_then(|active_completion| active_completion.completion_id.clone());
 8260
 8261            self.report_edit_prediction_event(completion_id, false, cx);
 8262        }
 8263
 8264        if let Some(provider) = self.edit_prediction_provider() {
 8265            provider.discard(reason, cx);
 8266        }
 8267
 8268        self.take_active_edit_prediction(cx)
 8269    }
 8270
 8271    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8272        let Some(provider) = self.edit_prediction_provider() else {
 8273            return;
 8274        };
 8275
 8276        let Some((_, buffer, _)) = self
 8277            .buffer
 8278            .read(cx)
 8279            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8280        else {
 8281            return;
 8282        };
 8283
 8284        let extension = buffer
 8285            .read(cx)
 8286            .file()
 8287            .and_then(|file| Some(file.path().extension()?.to_string()));
 8288
 8289        let event_type = match accepted {
 8290            true => "Edit Prediction Accepted",
 8291            false => "Edit Prediction Discarded",
 8292        };
 8293        telemetry::event!(
 8294            event_type,
 8295            provider = provider.name(),
 8296            prediction_id = id,
 8297            suggestion_accepted = accepted,
 8298            file_extension = extension,
 8299        );
 8300    }
 8301
 8302    fn open_editor_at_anchor(
 8303        snapshot: &language::BufferSnapshot,
 8304        target: language::Anchor,
 8305        workspace: &Entity<Workspace>,
 8306        window: &mut Window,
 8307        cx: &mut App,
 8308    ) -> Task<Result<()>> {
 8309        workspace.update(cx, |workspace, cx| {
 8310            let path = snapshot.file().map(|file| file.full_path(cx));
 8311            let Some(path) =
 8312                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8313            else {
 8314                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8315            };
 8316            let target = text::ToPoint::to_point(&target, snapshot);
 8317            let item = workspace.open_path(path, None, true, window, cx);
 8318            window.spawn(cx, async move |cx| {
 8319                let Some(editor) = item.await?.downcast::<Editor>() else {
 8320                    return Ok(());
 8321                };
 8322                editor
 8323                    .update_in(cx, |editor, window, cx| {
 8324                        editor.go_to_singleton_buffer_point(target, window, cx);
 8325                    })
 8326                    .ok();
 8327                anyhow::Ok(())
 8328            })
 8329        })
 8330    }
 8331
 8332    pub fn has_active_edit_prediction(&self) -> bool {
 8333        self.active_edit_prediction.is_some()
 8334    }
 8335
 8336    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8337        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8338            return false;
 8339        };
 8340
 8341        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8342        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8343        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8344        true
 8345    }
 8346
 8347    /// Returns true when we're displaying the edit prediction popover below the cursor
 8348    /// like we are not previewing and the LSP autocomplete menu is visible
 8349    /// or we are in `when_holding_modifier` mode.
 8350    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8351        if self.edit_prediction_preview_is_active()
 8352            || !self.show_edit_predictions_in_menu()
 8353            || !self.edit_predictions_enabled()
 8354        {
 8355            return false;
 8356        }
 8357
 8358        if self.has_visible_completions_menu() {
 8359            return true;
 8360        }
 8361
 8362        has_completion && self.edit_prediction_requires_modifier()
 8363    }
 8364
 8365    fn handle_modifiers_changed(
 8366        &mut self,
 8367        modifiers: Modifiers,
 8368        position_map: &PositionMap,
 8369        window: &mut Window,
 8370        cx: &mut Context<Self>,
 8371    ) {
 8372        // Ensure that the edit prediction preview is updated, even when not
 8373        // enabled, if there's an active edit prediction preview.
 8374        if self.show_edit_predictions_in_menu()
 8375            || matches!(
 8376                self.edit_prediction_preview,
 8377                EditPredictionPreview::Active { .. }
 8378            )
 8379        {
 8380            self.update_edit_prediction_preview(&modifiers, window, cx);
 8381        }
 8382
 8383        self.update_selection_mode(&modifiers, position_map, window, cx);
 8384
 8385        let mouse_position = window.mouse_position();
 8386        if !position_map.text_hitbox.is_hovered(window) {
 8387            return;
 8388        }
 8389
 8390        self.update_hovered_link(
 8391            position_map.point_for_position(mouse_position),
 8392            Some(mouse_position),
 8393            &position_map.snapshot,
 8394            modifiers,
 8395            window,
 8396            cx,
 8397        )
 8398    }
 8399
 8400    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8401        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8402            MultiCursorModifier::Alt => modifiers.secondary(),
 8403            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8404        }
 8405    }
 8406
 8407    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8408        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8409            MultiCursorModifier::Alt => modifiers.alt,
 8410            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8411        }
 8412    }
 8413
 8414    fn columnar_selection_mode(
 8415        modifiers: &Modifiers,
 8416        cx: &mut Context<Self>,
 8417    ) -> Option<ColumnarMode> {
 8418        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8419            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8420                Some(ColumnarMode::FromMouse)
 8421            } else if Self::is_alt_pressed(modifiers, cx) {
 8422                Some(ColumnarMode::FromSelection)
 8423            } else {
 8424                None
 8425            }
 8426        } else {
 8427            None
 8428        }
 8429    }
 8430
 8431    fn update_selection_mode(
 8432        &mut self,
 8433        modifiers: &Modifiers,
 8434        position_map: &PositionMap,
 8435        window: &mut Window,
 8436        cx: &mut Context<Self>,
 8437    ) {
 8438        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8439            return;
 8440        };
 8441        if self.selections.pending_anchor().is_none() {
 8442            return;
 8443        }
 8444
 8445        let mouse_position = window.mouse_position();
 8446        let point_for_position = position_map.point_for_position(mouse_position);
 8447        let position = point_for_position.previous_valid;
 8448
 8449        self.select(
 8450            SelectPhase::BeginColumnar {
 8451                position,
 8452                reset: false,
 8453                mode,
 8454                goal_column: point_for_position.exact_unclipped.column(),
 8455            },
 8456            window,
 8457            cx,
 8458        );
 8459    }
 8460
 8461    fn update_edit_prediction_preview(
 8462        &mut self,
 8463        modifiers: &Modifiers,
 8464        window: &mut Window,
 8465        cx: &mut Context<Self>,
 8466    ) {
 8467        let mut modifiers_held = false;
 8468
 8469        // Check bindings for all granularities.
 8470        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8471        let granularities = [
 8472            EditPredictionGranularity::Full,
 8473            EditPredictionGranularity::Line,
 8474            EditPredictionGranularity::Word,
 8475        ];
 8476
 8477        for granularity in granularities {
 8478            if let Some(keystroke) = self
 8479                .accept_edit_prediction_keybind(granularity, window, cx)
 8480                .keystroke()
 8481            {
 8482                modifiers_held = modifiers_held
 8483                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8484            }
 8485        }
 8486
 8487        if modifiers_held {
 8488            if matches!(
 8489                self.edit_prediction_preview,
 8490                EditPredictionPreview::Inactive { .. }
 8491            ) {
 8492                self.edit_prediction_preview = EditPredictionPreview::Active {
 8493                    previous_scroll_position: None,
 8494                    since: Instant::now(),
 8495                };
 8496
 8497                self.update_visible_edit_prediction(window, cx);
 8498                cx.notify();
 8499            }
 8500        } else if let EditPredictionPreview::Active {
 8501            previous_scroll_position,
 8502            since,
 8503        } = self.edit_prediction_preview
 8504        {
 8505            if let (Some(previous_scroll_position), Some(position_map)) =
 8506                (previous_scroll_position, self.last_position_map.as_ref())
 8507            {
 8508                self.set_scroll_position(
 8509                    previous_scroll_position
 8510                        .scroll_position(&position_map.snapshot.display_snapshot),
 8511                    window,
 8512                    cx,
 8513                );
 8514            }
 8515
 8516            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8517                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8518            };
 8519            self.clear_row_highlights::<EditPredictionPreview>();
 8520            self.update_visible_edit_prediction(window, cx);
 8521            cx.notify();
 8522        }
 8523    }
 8524
 8525    fn update_visible_edit_prediction(
 8526        &mut self,
 8527        _window: &mut Window,
 8528        cx: &mut Context<Self>,
 8529    ) -> Option<()> {
 8530        if self.ime_transaction.is_some() {
 8531            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8532            return None;
 8533        }
 8534
 8535        let selection = self.selections.newest_anchor();
 8536        let cursor = selection.head();
 8537        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8538
 8539        // Check project-level disable_ai setting for the current buffer
 8540        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8541            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8542                return None;
 8543            }
 8544        }
 8545        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8546        let excerpt_id = cursor.excerpt_id;
 8547
 8548        let show_in_menu = self.show_edit_predictions_in_menu();
 8549        let completions_menu_has_precedence = !show_in_menu
 8550            && (self.context_menu.borrow().is_some()
 8551                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8552
 8553        if completions_menu_has_precedence
 8554            || !offset_selection.is_empty()
 8555            || self
 8556                .active_edit_prediction
 8557                .as_ref()
 8558                .is_some_and(|completion| {
 8559                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8560                        return false;
 8561                    };
 8562                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8563                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8564                    !invalidation_range.contains(&offset_selection.head())
 8565                })
 8566        {
 8567            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8568            return None;
 8569        }
 8570
 8571        self.take_active_edit_prediction(cx);
 8572        let Some(provider) = self.edit_prediction_provider() else {
 8573            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8574            return None;
 8575        };
 8576
 8577        let (buffer, cursor_buffer_position) =
 8578            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8579
 8580        self.edit_prediction_settings =
 8581            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8582
 8583        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8584
 8585        if self.edit_prediction_indent_conflict {
 8586            let cursor_point = cursor.to_point(&multibuffer);
 8587            let mut suggested_indent = None;
 8588            multibuffer.suggested_indents_callback(
 8589                cursor_point.row..cursor_point.row + 1,
 8590                &mut |_, indent| {
 8591                    suggested_indent = Some(indent);
 8592                    ControlFlow::Break(())
 8593                },
 8594                cx,
 8595            );
 8596
 8597            if let Some(indent) = suggested_indent
 8598                && indent.len == cursor_point.column
 8599            {
 8600                self.edit_prediction_indent_conflict = false;
 8601            }
 8602        }
 8603
 8604        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8605
 8606        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8607        {
 8608            edit_prediction_types::EditPrediction::Local {
 8609                id,
 8610                edits,
 8611                cursor_position,
 8612                edit_preview,
 8613            } => (id, edits, cursor_position, edit_preview),
 8614            edit_prediction_types::EditPrediction::Jump {
 8615                id,
 8616                snapshot,
 8617                target,
 8618            } => {
 8619                if let Some(provider) = &self.edit_prediction_provider {
 8620                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8621                }
 8622                self.stale_edit_prediction_in_menu = None;
 8623                self.active_edit_prediction = Some(EditPredictionState {
 8624                    inlay_ids: vec![],
 8625                    completion: EditPrediction::MoveOutside { snapshot, target },
 8626                    completion_id: id,
 8627                    invalidation_range: None,
 8628                });
 8629                cx.notify();
 8630                return Some(());
 8631            }
 8632        };
 8633
 8634        let edits = edits
 8635            .into_iter()
 8636            .flat_map(|(range, new_text)| {
 8637                Some((
 8638                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8639                    new_text,
 8640                ))
 8641            })
 8642            .collect::<Vec<_>>();
 8643        if edits.is_empty() {
 8644            return None;
 8645        }
 8646
 8647        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8648            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8649            Some((anchor, predicted.offset))
 8650        });
 8651
 8652        let first_edit_start = edits.first().unwrap().0.start;
 8653        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8654        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8655
 8656        let last_edit_end = edits.last().unwrap().0.end;
 8657        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8658        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8659
 8660        let cursor_row = cursor.to_point(&multibuffer).row;
 8661
 8662        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8663
 8664        let mut inlay_ids = Vec::new();
 8665        let invalidation_row_range;
 8666        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8667            Some(cursor_row..edit_end_row)
 8668        } else if cursor_row > edit_end_row {
 8669            Some(edit_start_row..cursor_row)
 8670        } else {
 8671            None
 8672        };
 8673        let supports_jump = self
 8674            .edit_prediction_provider
 8675            .as_ref()
 8676            .map(|provider| provider.provider.supports_jump_to_edit())
 8677            .unwrap_or(true);
 8678
 8679        let is_move = supports_jump
 8680            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8681        let completion = if is_move {
 8682            if let Some(provider) = &self.edit_prediction_provider {
 8683                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8684            }
 8685            invalidation_row_range =
 8686                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8687            let target = first_edit_start;
 8688            EditPrediction::MoveWithin { target, snapshot }
 8689        } else {
 8690            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8691                && !self.edit_predictions_hidden_for_vim_mode;
 8692
 8693            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8694                if provider.show_tab_accept_marker() {
 8695                    EditDisplayMode::TabAccept
 8696                } else {
 8697                    EditDisplayMode::Inline
 8698                }
 8699            } else {
 8700                EditDisplayMode::DiffPopover
 8701            };
 8702
 8703            if show_completions_in_buffer {
 8704                if let Some(provider) = &self.edit_prediction_provider {
 8705                    let suggestion_display_type = match display_mode {
 8706                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8707                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8708                            SuggestionDisplayType::GhostText
 8709                        }
 8710                    };
 8711                    provider.provider.did_show(suggestion_display_type, cx);
 8712                }
 8713                if edits
 8714                    .iter()
 8715                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8716                {
 8717                    let mut inlays = Vec::new();
 8718                    for (range, new_text) in &edits {
 8719                        let inlay = Inlay::edit_prediction(
 8720                            post_inc(&mut self.next_inlay_id),
 8721                            range.start,
 8722                            new_text.as_ref(),
 8723                        );
 8724                        inlay_ids.push(inlay.id);
 8725                        inlays.push(inlay);
 8726                    }
 8727
 8728                    self.splice_inlays(&[], inlays, cx);
 8729                } else {
 8730                    let background_color = cx.theme().status().deleted_background;
 8731                    self.highlight_text(
 8732                        HighlightKey::EditPredictionHighlight,
 8733                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8734                        HighlightStyle {
 8735                            background_color: Some(background_color),
 8736                            ..Default::default()
 8737                        },
 8738                        cx,
 8739                    );
 8740                }
 8741            }
 8742
 8743            invalidation_row_range = edit_start_row..edit_end_row;
 8744
 8745            EditPrediction::Edit {
 8746                edits,
 8747                cursor_position,
 8748                edit_preview,
 8749                display_mode,
 8750                snapshot,
 8751            }
 8752        };
 8753
 8754        let invalidation_range = multibuffer
 8755            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8756            ..multibuffer.anchor_after(Point::new(
 8757                invalidation_row_range.end,
 8758                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8759            ));
 8760
 8761        self.stale_edit_prediction_in_menu = None;
 8762        self.active_edit_prediction = Some(EditPredictionState {
 8763            inlay_ids,
 8764            completion,
 8765            completion_id,
 8766            invalidation_range: Some(invalidation_range),
 8767        });
 8768
 8769        cx.notify();
 8770
 8771        Some(())
 8772    }
 8773
 8774    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8775        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8776    }
 8777
 8778    /// Get all display points of breakpoints that will be rendered within editor
 8779    ///
 8780    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8781    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8782    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8783    fn active_breakpoints(
 8784        &self,
 8785        range: Range<DisplayRow>,
 8786        window: &mut Window,
 8787        cx: &mut Context<Self>,
 8788    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8789        let mut breakpoint_display_points = HashMap::default();
 8790
 8791        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8792            return breakpoint_display_points;
 8793        };
 8794
 8795        let snapshot = self.snapshot(window, cx);
 8796
 8797        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8798        let Some(project) = self.project() else {
 8799            return breakpoint_display_points;
 8800        };
 8801
 8802        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8803            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8804
 8805        for (buffer_snapshot, range, excerpt_id) in
 8806            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8807        {
 8808            let Some(buffer) = project
 8809                .read(cx)
 8810                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8811            else {
 8812                continue;
 8813            };
 8814            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8815                &buffer,
 8816                Some(
 8817                    buffer_snapshot.anchor_before(range.start)
 8818                        ..buffer_snapshot.anchor_after(range.end),
 8819                ),
 8820                buffer_snapshot,
 8821                cx,
 8822            );
 8823            for (breakpoint, state) in breakpoints {
 8824                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8825                let position = multi_buffer_anchor
 8826                    .to_point(&multi_buffer_snapshot)
 8827                    .to_display_point(&snapshot);
 8828
 8829                breakpoint_display_points.insert(
 8830                    position.row(),
 8831                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8832                );
 8833            }
 8834        }
 8835
 8836        breakpoint_display_points
 8837    }
 8838
 8839    fn breakpoint_context_menu(
 8840        &self,
 8841        anchor: Anchor,
 8842        window: &mut Window,
 8843        cx: &mut Context<Self>,
 8844    ) -> Entity<ui::ContextMenu> {
 8845        let weak_editor = cx.weak_entity();
 8846        let focus_handle = self.focus_handle(cx);
 8847
 8848        let row = self
 8849            .buffer
 8850            .read(cx)
 8851            .snapshot(cx)
 8852            .summary_for_anchor::<Point>(&anchor)
 8853            .row;
 8854
 8855        let breakpoint = self
 8856            .breakpoint_at_row(row, window, cx)
 8857            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8858
 8859        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8860            "Edit Log Breakpoint"
 8861        } else {
 8862            "Set Log Breakpoint"
 8863        };
 8864
 8865        let condition_breakpoint_msg = if breakpoint
 8866            .as_ref()
 8867            .is_some_and(|bp| bp.1.condition.is_some())
 8868        {
 8869            "Edit Condition Breakpoint"
 8870        } else {
 8871            "Set Condition Breakpoint"
 8872        };
 8873
 8874        let hit_condition_breakpoint_msg = if breakpoint
 8875            .as_ref()
 8876            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8877        {
 8878            "Edit Hit Condition Breakpoint"
 8879        } else {
 8880            "Set Hit Condition Breakpoint"
 8881        };
 8882
 8883        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8884            "Unset Breakpoint"
 8885        } else {
 8886            "Set Breakpoint"
 8887        };
 8888
 8889        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8890
 8891        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8892            BreakpointState::Enabled => Some("Disable"),
 8893            BreakpointState::Disabled => Some("Enable"),
 8894        });
 8895
 8896        let (anchor, breakpoint) =
 8897            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8898
 8899        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8900            menu.on_blur_subscription(Subscription::new(|| {}))
 8901                .context(focus_handle)
 8902                .when(run_to_cursor, |this| {
 8903                    let weak_editor = weak_editor.clone();
 8904                    this.entry("Run to Cursor", None, move |window, cx| {
 8905                        weak_editor
 8906                            .update(cx, |editor, cx| {
 8907                                editor.change_selections(
 8908                                    SelectionEffects::no_scroll(),
 8909                                    window,
 8910                                    cx,
 8911                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8912                                );
 8913                            })
 8914                            .ok();
 8915
 8916                        window.dispatch_action(Box::new(RunToCursor), cx);
 8917                    })
 8918                    .separator()
 8919                })
 8920                .when_some(toggle_state_msg, |this, msg| {
 8921                    this.entry(msg, None, {
 8922                        let weak_editor = weak_editor.clone();
 8923                        let breakpoint = breakpoint.clone();
 8924                        move |_window, cx| {
 8925                            weak_editor
 8926                                .update(cx, |this, cx| {
 8927                                    this.edit_breakpoint_at_anchor(
 8928                                        anchor,
 8929                                        breakpoint.as_ref().clone(),
 8930                                        BreakpointEditAction::InvertState,
 8931                                        cx,
 8932                                    );
 8933                                })
 8934                                .log_err();
 8935                        }
 8936                    })
 8937                })
 8938                .entry(set_breakpoint_msg, None, {
 8939                    let weak_editor = weak_editor.clone();
 8940                    let breakpoint = breakpoint.clone();
 8941                    move |_window, cx| {
 8942                        weak_editor
 8943                            .update(cx, |this, cx| {
 8944                                this.edit_breakpoint_at_anchor(
 8945                                    anchor,
 8946                                    breakpoint.as_ref().clone(),
 8947                                    BreakpointEditAction::Toggle,
 8948                                    cx,
 8949                                );
 8950                            })
 8951                            .log_err();
 8952                    }
 8953                })
 8954                .entry(log_breakpoint_msg, None, {
 8955                    let breakpoint = breakpoint.clone();
 8956                    let weak_editor = weak_editor.clone();
 8957                    move |window, cx| {
 8958                        weak_editor
 8959                            .update(cx, |this, cx| {
 8960                                this.add_edit_breakpoint_block(
 8961                                    anchor,
 8962                                    breakpoint.as_ref(),
 8963                                    BreakpointPromptEditAction::Log,
 8964                                    window,
 8965                                    cx,
 8966                                );
 8967                            })
 8968                            .log_err();
 8969                    }
 8970                })
 8971                .entry(condition_breakpoint_msg, None, {
 8972                    let breakpoint = breakpoint.clone();
 8973                    let weak_editor = weak_editor.clone();
 8974                    move |window, cx| {
 8975                        weak_editor
 8976                            .update(cx, |this, cx| {
 8977                                this.add_edit_breakpoint_block(
 8978                                    anchor,
 8979                                    breakpoint.as_ref(),
 8980                                    BreakpointPromptEditAction::Condition,
 8981                                    window,
 8982                                    cx,
 8983                                );
 8984                            })
 8985                            .log_err();
 8986                    }
 8987                })
 8988                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8989                    weak_editor
 8990                        .update(cx, |this, cx| {
 8991                            this.add_edit_breakpoint_block(
 8992                                anchor,
 8993                                breakpoint.as_ref(),
 8994                                BreakpointPromptEditAction::HitCondition,
 8995                                window,
 8996                                cx,
 8997                            );
 8998                        })
 8999                        .log_err();
 9000                })
 9001        })
 9002    }
 9003
 9004    fn render_breakpoint(
 9005        &self,
 9006        position: Anchor,
 9007        row: DisplayRow,
 9008        breakpoint: &Breakpoint,
 9009        state: Option<BreakpointSessionState>,
 9010        cx: &mut Context<Self>,
 9011    ) -> IconButton {
 9012        let is_rejected = state.is_some_and(|s| !s.verified);
 9013        // Is it a breakpoint that shows up when hovering over gutter?
 9014        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9015            (false, false),
 9016            |PhantomBreakpointIndicator {
 9017                 is_active,
 9018                 display_row,
 9019                 collides_with_existing_breakpoint,
 9020             }| {
 9021                (
 9022                    is_active && display_row == row,
 9023                    collides_with_existing_breakpoint,
 9024                )
 9025            },
 9026        );
 9027
 9028        let (color, icon) = {
 9029            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9030                (false, false) => ui::IconName::DebugBreakpoint,
 9031                (true, false) => ui::IconName::DebugLogBreakpoint,
 9032                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9033                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9034            };
 9035
 9036            let theme_colors = cx.theme().colors();
 9037
 9038            let color = if is_phantom {
 9039                if collides_with_existing {
 9040                    Color::Custom(
 9041                        theme_colors
 9042                            .debugger_accent
 9043                            .blend(theme_colors.text.opacity(0.6)),
 9044                    )
 9045                } else {
 9046                    Color::Hint
 9047                }
 9048            } else if is_rejected {
 9049                Color::Disabled
 9050            } else {
 9051                Color::Debugger
 9052            };
 9053
 9054            (color, icon)
 9055        };
 9056
 9057        let breakpoint = Arc::from(breakpoint.clone());
 9058
 9059        let alt_as_text = gpui::Keystroke {
 9060            modifiers: Modifiers::secondary_key(),
 9061            ..Default::default()
 9062        };
 9063        let primary_action_text = if breakpoint.is_disabled() {
 9064            "Enable breakpoint"
 9065        } else if is_phantom && !collides_with_existing {
 9066            "Set breakpoint"
 9067        } else {
 9068            "Unset breakpoint"
 9069        };
 9070        let focus_handle = self.focus_handle.clone();
 9071
 9072        let meta = if is_rejected {
 9073            SharedString::from("No executable code is associated with this line.")
 9074        } else if collides_with_existing && !breakpoint.is_disabled() {
 9075            SharedString::from(format!(
 9076                "{alt_as_text}-click to disable,\nright-click for more options."
 9077            ))
 9078        } else {
 9079            SharedString::from("Right-click for more options.")
 9080        };
 9081        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9082            .icon_size(IconSize::XSmall)
 9083            .size(ui::ButtonSize::None)
 9084            .when(is_rejected, |this| {
 9085                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9086            })
 9087            .icon_color(color)
 9088            .style(ButtonStyle::Transparent)
 9089            .on_click(cx.listener({
 9090                move |editor, event: &ClickEvent, window, cx| {
 9091                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9092                        BreakpointEditAction::InvertState
 9093                    } else {
 9094                        BreakpointEditAction::Toggle
 9095                    };
 9096
 9097                    window.focus(&editor.focus_handle(cx), cx);
 9098                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9099                    editor.edit_breakpoint_at_anchor(
 9100                        position,
 9101                        breakpoint.as_ref().clone(),
 9102                        edit_action,
 9103                        cx,
 9104                    );
 9105                }
 9106            }))
 9107            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9108                editor.set_breakpoint_context_menu(
 9109                    row,
 9110                    Some(position),
 9111                    event.position(),
 9112                    window,
 9113                    cx,
 9114                );
 9115            }))
 9116            .tooltip(move |_window, cx| {
 9117                Tooltip::with_meta_in(
 9118                    primary_action_text,
 9119                    Some(&ToggleBreakpoint),
 9120                    meta.clone(),
 9121                    &focus_handle,
 9122                    cx,
 9123                )
 9124            })
 9125    }
 9126
 9127    fn build_tasks_context(
 9128        project: &Entity<Project>,
 9129        buffer: &Entity<Buffer>,
 9130        buffer_row: u32,
 9131        tasks: &Arc<RunnableTasks>,
 9132        cx: &mut Context<Self>,
 9133    ) -> Task<Option<task::TaskContext>> {
 9134        let position = Point::new(buffer_row, tasks.column);
 9135        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9136        let location = Location {
 9137            buffer: buffer.clone(),
 9138            range: range_start..range_start,
 9139        };
 9140        // Fill in the environmental variables from the tree-sitter captures
 9141        let mut captured_task_variables = TaskVariables::default();
 9142        for (capture_name, value) in tasks.extra_variables.clone() {
 9143            captured_task_variables.insert(
 9144                task::VariableName::Custom(capture_name.into()),
 9145                value.clone(),
 9146            );
 9147        }
 9148        project.update(cx, |project, cx| {
 9149            project.task_store().update(cx, |task_store, cx| {
 9150                task_store.task_context_for_location(captured_task_variables, location, cx)
 9151            })
 9152        })
 9153    }
 9154
 9155    pub fn context_menu_visible(&self) -> bool {
 9156        !self.edit_prediction_preview_is_active()
 9157            && self
 9158                .context_menu
 9159                .borrow()
 9160                .as_ref()
 9161                .is_some_and(|menu| menu.visible())
 9162    }
 9163
 9164    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9165        self.context_menu
 9166            .borrow()
 9167            .as_ref()
 9168            .map(|menu| menu.origin())
 9169    }
 9170
 9171    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9172        self.context_menu_options = Some(options);
 9173    }
 9174
 9175    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9176    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9177
 9178    fn render_edit_prediction_popover(
 9179        &mut self,
 9180        text_bounds: &Bounds<Pixels>,
 9181        content_origin: gpui::Point<Pixels>,
 9182        right_margin: Pixels,
 9183        editor_snapshot: &EditorSnapshot,
 9184        visible_row_range: Range<DisplayRow>,
 9185        scroll_top: ScrollOffset,
 9186        scroll_bottom: ScrollOffset,
 9187        line_layouts: &[LineWithInvisibles],
 9188        line_height: Pixels,
 9189        scroll_position: gpui::Point<ScrollOffset>,
 9190        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9191        newest_selection_head: Option<DisplayPoint>,
 9192        editor_width: Pixels,
 9193        style: &EditorStyle,
 9194        window: &mut Window,
 9195        cx: &mut App,
 9196    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9197        if self.mode().is_minimap() {
 9198            return None;
 9199        }
 9200        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9201
 9202        if self.edit_prediction_visible_in_cursor_popover(true) {
 9203            return None;
 9204        }
 9205
 9206        match &active_edit_prediction.completion {
 9207            EditPrediction::MoveWithin { target, .. } => {
 9208                let target_display_point = target.to_display_point(editor_snapshot);
 9209
 9210                if self.edit_prediction_requires_modifier() {
 9211                    if !self.edit_prediction_preview_is_active() {
 9212                        return None;
 9213                    }
 9214
 9215                    self.render_edit_prediction_modifier_jump_popover(
 9216                        text_bounds,
 9217                        content_origin,
 9218                        visible_row_range,
 9219                        line_layouts,
 9220                        line_height,
 9221                        scroll_pixel_position,
 9222                        newest_selection_head,
 9223                        target_display_point,
 9224                        window,
 9225                        cx,
 9226                    )
 9227                } else {
 9228                    self.render_edit_prediction_eager_jump_popover(
 9229                        text_bounds,
 9230                        content_origin,
 9231                        editor_snapshot,
 9232                        visible_row_range,
 9233                        scroll_top,
 9234                        scroll_bottom,
 9235                        line_height,
 9236                        scroll_pixel_position,
 9237                        target_display_point,
 9238                        editor_width,
 9239                        window,
 9240                        cx,
 9241                    )
 9242                }
 9243            }
 9244            EditPrediction::Edit {
 9245                display_mode: EditDisplayMode::Inline,
 9246                ..
 9247            } => None,
 9248            EditPrediction::Edit {
 9249                display_mode: EditDisplayMode::TabAccept,
 9250                edits,
 9251                ..
 9252            } => {
 9253                let range = &edits.first()?.0;
 9254                let target_display_point = range.end.to_display_point(editor_snapshot);
 9255
 9256                self.render_edit_prediction_end_of_line_popover(
 9257                    "Accept",
 9258                    editor_snapshot,
 9259                    visible_row_range,
 9260                    target_display_point,
 9261                    line_height,
 9262                    scroll_pixel_position,
 9263                    content_origin,
 9264                    editor_width,
 9265                    window,
 9266                    cx,
 9267                )
 9268            }
 9269            EditPrediction::Edit {
 9270                edits,
 9271                edit_preview,
 9272                display_mode: EditDisplayMode::DiffPopover,
 9273                snapshot,
 9274                ..
 9275            } => self.render_edit_prediction_diff_popover(
 9276                text_bounds,
 9277                content_origin,
 9278                right_margin,
 9279                editor_snapshot,
 9280                visible_row_range,
 9281                line_layouts,
 9282                line_height,
 9283                scroll_position,
 9284                scroll_pixel_position,
 9285                newest_selection_head,
 9286                editor_width,
 9287                style,
 9288                edits,
 9289                edit_preview,
 9290                snapshot,
 9291                window,
 9292                cx,
 9293            ),
 9294            EditPrediction::MoveOutside { snapshot, .. } => {
 9295                let mut element = self
 9296                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9297                    .into_any();
 9298
 9299                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9300                let origin_x = text_bounds.size.width - size.width - px(30.);
 9301                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9302                element.prepaint_at(origin, window, cx);
 9303
 9304                Some((element, origin))
 9305            }
 9306        }
 9307    }
 9308
 9309    fn render_edit_prediction_modifier_jump_popover(
 9310        &mut self,
 9311        text_bounds: &Bounds<Pixels>,
 9312        content_origin: gpui::Point<Pixels>,
 9313        visible_row_range: Range<DisplayRow>,
 9314        line_layouts: &[LineWithInvisibles],
 9315        line_height: Pixels,
 9316        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9317        newest_selection_head: Option<DisplayPoint>,
 9318        target_display_point: DisplayPoint,
 9319        window: &mut Window,
 9320        cx: &mut App,
 9321    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9322        let scrolled_content_origin =
 9323            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9324
 9325        const SCROLL_PADDING_Y: Pixels = px(12.);
 9326
 9327        if target_display_point.row() < visible_row_range.start {
 9328            return self.render_edit_prediction_scroll_popover(
 9329                &|_| SCROLL_PADDING_Y,
 9330                IconName::ArrowUp,
 9331                visible_row_range,
 9332                line_layouts,
 9333                newest_selection_head,
 9334                scrolled_content_origin,
 9335                window,
 9336                cx,
 9337            );
 9338        } else if target_display_point.row() >= visible_row_range.end {
 9339            return self.render_edit_prediction_scroll_popover(
 9340                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9341                IconName::ArrowDown,
 9342                visible_row_range,
 9343                line_layouts,
 9344                newest_selection_head,
 9345                scrolled_content_origin,
 9346                window,
 9347                cx,
 9348            );
 9349        }
 9350
 9351        const POLE_WIDTH: Pixels = px(2.);
 9352
 9353        let line_layout =
 9354            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9355        let target_column = target_display_point.column() as usize;
 9356
 9357        let target_x = line_layout.x_for_index(target_column);
 9358        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9359            - scroll_pixel_position.y;
 9360
 9361        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9362
 9363        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9364        border_color.l += 0.001;
 9365
 9366        let mut element = v_flex()
 9367            .items_end()
 9368            .when(flag_on_right, |el| el.items_start())
 9369            .child(if flag_on_right {
 9370                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9371                    .rounded_bl(px(0.))
 9372                    .rounded_tl(px(0.))
 9373                    .border_l_2()
 9374                    .border_color(border_color)
 9375            } else {
 9376                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9377                    .rounded_br(px(0.))
 9378                    .rounded_tr(px(0.))
 9379                    .border_r_2()
 9380                    .border_color(border_color)
 9381            })
 9382            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9383            .into_any();
 9384
 9385        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9386
 9387        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9388            - point(
 9389                if flag_on_right {
 9390                    POLE_WIDTH
 9391                } else {
 9392                    size.width - POLE_WIDTH
 9393                },
 9394                size.height - line_height,
 9395            );
 9396
 9397        origin.x = origin.x.max(content_origin.x);
 9398
 9399        element.prepaint_at(origin, window, cx);
 9400
 9401        Some((element, origin))
 9402    }
 9403
 9404    fn render_edit_prediction_scroll_popover(
 9405        &mut self,
 9406        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9407        scroll_icon: IconName,
 9408        visible_row_range: Range<DisplayRow>,
 9409        line_layouts: &[LineWithInvisibles],
 9410        newest_selection_head: Option<DisplayPoint>,
 9411        scrolled_content_origin: gpui::Point<Pixels>,
 9412        window: &mut Window,
 9413        cx: &mut App,
 9414    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9415        let mut element = self
 9416            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9417            .into_any();
 9418
 9419        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9420
 9421        let cursor = newest_selection_head?;
 9422        let cursor_row_layout =
 9423            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9424        let cursor_column = cursor.column() as usize;
 9425
 9426        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9427
 9428        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9429
 9430        element.prepaint_at(origin, window, cx);
 9431        Some((element, origin))
 9432    }
 9433
 9434    fn render_edit_prediction_eager_jump_popover(
 9435        &mut self,
 9436        text_bounds: &Bounds<Pixels>,
 9437        content_origin: gpui::Point<Pixels>,
 9438        editor_snapshot: &EditorSnapshot,
 9439        visible_row_range: Range<DisplayRow>,
 9440        scroll_top: ScrollOffset,
 9441        scroll_bottom: ScrollOffset,
 9442        line_height: Pixels,
 9443        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9444        target_display_point: DisplayPoint,
 9445        editor_width: Pixels,
 9446        window: &mut Window,
 9447        cx: &mut App,
 9448    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9449        if target_display_point.row().as_f64() < scroll_top {
 9450            let mut element = self
 9451                .render_edit_prediction_line_popover(
 9452                    "Jump to Edit",
 9453                    Some(IconName::ArrowUp),
 9454                    window,
 9455                    cx,
 9456                )
 9457                .into_any();
 9458
 9459            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9460            let offset = point(
 9461                (text_bounds.size.width - size.width) / 2.,
 9462                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9463            );
 9464
 9465            let origin = text_bounds.origin + offset;
 9466            element.prepaint_at(origin, window, cx);
 9467            Some((element, origin))
 9468        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9469            let mut element = self
 9470                .render_edit_prediction_line_popover(
 9471                    "Jump to Edit",
 9472                    Some(IconName::ArrowDown),
 9473                    window,
 9474                    cx,
 9475                )
 9476                .into_any();
 9477
 9478            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9479            let offset = point(
 9480                (text_bounds.size.width - size.width) / 2.,
 9481                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9482            );
 9483
 9484            let origin = text_bounds.origin + offset;
 9485            element.prepaint_at(origin, window, cx);
 9486            Some((element, origin))
 9487        } else {
 9488            self.render_edit_prediction_end_of_line_popover(
 9489                "Jump to Edit",
 9490                editor_snapshot,
 9491                visible_row_range,
 9492                target_display_point,
 9493                line_height,
 9494                scroll_pixel_position,
 9495                content_origin,
 9496                editor_width,
 9497                window,
 9498                cx,
 9499            )
 9500        }
 9501    }
 9502
 9503    fn render_edit_prediction_end_of_line_popover(
 9504        self: &mut Editor,
 9505        label: &'static str,
 9506        editor_snapshot: &EditorSnapshot,
 9507        visible_row_range: Range<DisplayRow>,
 9508        target_display_point: DisplayPoint,
 9509        line_height: Pixels,
 9510        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9511        content_origin: gpui::Point<Pixels>,
 9512        editor_width: Pixels,
 9513        window: &mut Window,
 9514        cx: &mut App,
 9515    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9516        let target_line_end = DisplayPoint::new(
 9517            target_display_point.row(),
 9518            editor_snapshot.line_len(target_display_point.row()),
 9519        );
 9520
 9521        let mut element = self
 9522            .render_edit_prediction_line_popover(label, None, window, cx)
 9523            .into_any();
 9524
 9525        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9526
 9527        let line_origin =
 9528            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9529
 9530        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9531        let mut origin = start_point
 9532            + line_origin
 9533            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9534        origin.x = origin.x.max(content_origin.x);
 9535
 9536        let max_x = content_origin.x + editor_width - size.width;
 9537
 9538        if origin.x > max_x {
 9539            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9540
 9541            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9542                origin.y += offset;
 9543                IconName::ArrowUp
 9544            } else {
 9545                origin.y -= offset;
 9546                IconName::ArrowDown
 9547            };
 9548
 9549            element = self
 9550                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9551                .into_any();
 9552
 9553            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9554
 9555            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9556        }
 9557
 9558        element.prepaint_at(origin, window, cx);
 9559        Some((element, origin))
 9560    }
 9561
 9562    fn render_edit_prediction_diff_popover(
 9563        self: &Editor,
 9564        text_bounds: &Bounds<Pixels>,
 9565        content_origin: gpui::Point<Pixels>,
 9566        right_margin: Pixels,
 9567        editor_snapshot: &EditorSnapshot,
 9568        visible_row_range: Range<DisplayRow>,
 9569        line_layouts: &[LineWithInvisibles],
 9570        line_height: Pixels,
 9571        scroll_position: gpui::Point<ScrollOffset>,
 9572        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9573        newest_selection_head: Option<DisplayPoint>,
 9574        editor_width: Pixels,
 9575        style: &EditorStyle,
 9576        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9577        edit_preview: &Option<language::EditPreview>,
 9578        snapshot: &language::BufferSnapshot,
 9579        window: &mut Window,
 9580        cx: &mut App,
 9581    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9582        let edit_start = edits
 9583            .first()
 9584            .unwrap()
 9585            .0
 9586            .start
 9587            .to_display_point(editor_snapshot);
 9588        let edit_end = edits
 9589            .last()
 9590            .unwrap()
 9591            .0
 9592            .end
 9593            .to_display_point(editor_snapshot);
 9594
 9595        let is_visible = visible_row_range.contains(&edit_start.row())
 9596            || visible_row_range.contains(&edit_end.row());
 9597        if !is_visible {
 9598            return None;
 9599        }
 9600
 9601        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9602            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9603        } else {
 9604            // Fallback for providers without edit_preview
 9605            crate::edit_prediction_fallback_text(edits, cx)
 9606        };
 9607
 9608        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9609        let line_count = highlighted_edits.text.lines().count();
 9610
 9611        const BORDER_WIDTH: Pixels = px(1.);
 9612
 9613        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9614        let has_keybind = keybind.is_some();
 9615
 9616        let mut element = h_flex()
 9617            .items_start()
 9618            .child(
 9619                h_flex()
 9620                    .bg(cx.theme().colors().editor_background)
 9621                    .border(BORDER_WIDTH)
 9622                    .shadow_xs()
 9623                    .border_color(cx.theme().colors().border)
 9624                    .rounded_l_lg()
 9625                    .when(line_count > 1, |el| el.rounded_br_lg())
 9626                    .pr_1()
 9627                    .child(styled_text),
 9628            )
 9629            .child(
 9630                h_flex()
 9631                    .h(line_height + BORDER_WIDTH * 2.)
 9632                    .px_1p5()
 9633                    .gap_1()
 9634                    // Workaround: For some reason, there's a gap if we don't do this
 9635                    .ml(-BORDER_WIDTH)
 9636                    .shadow(vec![gpui::BoxShadow {
 9637                        color: gpui::black().opacity(0.05),
 9638                        offset: point(px(1.), px(1.)),
 9639                        blur_radius: px(2.),
 9640                        spread_radius: px(0.),
 9641                    }])
 9642                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9643                    .border(BORDER_WIDTH)
 9644                    .border_color(cx.theme().colors().border)
 9645                    .rounded_r_lg()
 9646                    .id("edit_prediction_diff_popover_keybind")
 9647                    .when(!has_keybind, |el| {
 9648                        let status_colors = cx.theme().status();
 9649
 9650                        el.bg(status_colors.error_background)
 9651                            .border_color(status_colors.error.opacity(0.6))
 9652                            .child(Icon::new(IconName::Info).color(Color::Error))
 9653                            .cursor_default()
 9654                            .hoverable_tooltip(move |_window, cx| {
 9655                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9656                            })
 9657                    })
 9658                    .children(keybind),
 9659            )
 9660            .into_any();
 9661
 9662        let longest_row =
 9663            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9664        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9665            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9666        } else {
 9667            layout_line(
 9668                longest_row,
 9669                editor_snapshot,
 9670                style,
 9671                editor_width,
 9672                |_| false,
 9673                window,
 9674                cx,
 9675            )
 9676            .width
 9677        };
 9678
 9679        let viewport_bounds =
 9680            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9681                right: -right_margin,
 9682                ..Default::default()
 9683            });
 9684
 9685        let x_after_longest = Pixels::from(
 9686            ScrollPixelOffset::from(
 9687                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9688            ) - scroll_pixel_position.x,
 9689        );
 9690
 9691        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9692
 9693        // Fully visible if it can be displayed within the window (allow overlapping other
 9694        // panes). However, this is only allowed if the popover starts within text_bounds.
 9695        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9696            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9697
 9698        let mut origin = if can_position_to_the_right {
 9699            point(
 9700                x_after_longest,
 9701                text_bounds.origin.y
 9702                    + Pixels::from(
 9703                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9704                            - scroll_pixel_position.y,
 9705                    ),
 9706            )
 9707        } else {
 9708            let cursor_row = newest_selection_head.map(|head| head.row());
 9709            let above_edit = edit_start
 9710                .row()
 9711                .0
 9712                .checked_sub(line_count as u32)
 9713                .map(DisplayRow);
 9714            let below_edit = Some(edit_end.row() + 1);
 9715            let above_cursor =
 9716                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9717            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9718
 9719            // Place the edit popover adjacent to the edit if there is a location
 9720            // available that is onscreen and does not obscure the cursor. Otherwise,
 9721            // place it adjacent to the cursor.
 9722            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9723                .into_iter()
 9724                .flatten()
 9725                .find(|&start_row| {
 9726                    let end_row = start_row + line_count as u32;
 9727                    visible_row_range.contains(&start_row)
 9728                        && visible_row_range.contains(&end_row)
 9729                        && cursor_row
 9730                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9731                })?;
 9732
 9733            content_origin
 9734                + point(
 9735                    Pixels::from(-scroll_pixel_position.x),
 9736                    Pixels::from(
 9737                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9738                    ),
 9739                )
 9740        };
 9741
 9742        origin.x -= BORDER_WIDTH;
 9743
 9744        window.with_content_mask(
 9745            Some(gpui::ContentMask {
 9746                bounds: *text_bounds,
 9747            }),
 9748            |window| {
 9749                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9750            },
 9751        );
 9752
 9753        // Do not return an element, since it will already be drawn due to defer_draw.
 9754        None
 9755    }
 9756
 9757    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9758        px(30.)
 9759    }
 9760
 9761    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9762        if self.read_only(cx) {
 9763            cx.theme().players().read_only()
 9764        } else {
 9765            self.style.as_ref().unwrap().local_player
 9766        }
 9767    }
 9768
 9769    fn render_edit_prediction_accept_keybind(
 9770        &self,
 9771        window: &mut Window,
 9772        cx: &mut App,
 9773    ) -> Option<AnyElement> {
 9774        let accept_binding =
 9775            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9776        let accept_keystroke = accept_binding.keystroke()?;
 9777
 9778        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9779
 9780        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9781            Color::Accent
 9782        } else {
 9783            Color::Muted
 9784        };
 9785
 9786        h_flex()
 9787            .px_0p5()
 9788            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9789            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9790            .text_size(TextSize::XSmall.rems(cx))
 9791            .child(h_flex().children(ui::render_modifiers(
 9792                accept_keystroke.modifiers(),
 9793                PlatformStyle::platform(),
 9794                Some(modifiers_color),
 9795                Some(IconSize::XSmall.rems().into()),
 9796                true,
 9797            )))
 9798            .when(is_platform_style_mac, |parent| {
 9799                parent.child(accept_keystroke.key().to_string())
 9800            })
 9801            .when(!is_platform_style_mac, |parent| {
 9802                parent.child(
 9803                    Key::new(
 9804                        util::capitalize(accept_keystroke.key()),
 9805                        Some(Color::Default),
 9806                    )
 9807                    .size(Some(IconSize::XSmall.rems().into())),
 9808                )
 9809            })
 9810            .into_any()
 9811            .into()
 9812    }
 9813
 9814    fn render_edit_prediction_line_popover(
 9815        &self,
 9816        label: impl Into<SharedString>,
 9817        icon: Option<IconName>,
 9818        window: &mut Window,
 9819        cx: &mut App,
 9820    ) -> Stateful<Div> {
 9821        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9822
 9823        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9824        let has_keybind = keybind.is_some();
 9825        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9826
 9827        h_flex()
 9828            .id("ep-line-popover")
 9829            .py_0p5()
 9830            .pl_1()
 9831            .pr(padding_right)
 9832            .gap_1()
 9833            .rounded_md()
 9834            .border_1()
 9835            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9836            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9837            .shadow_xs()
 9838            .when(!has_keybind, |el| {
 9839                let status_colors = cx.theme().status();
 9840
 9841                el.bg(status_colors.error_background)
 9842                    .border_color(status_colors.error.opacity(0.6))
 9843                    .pl_2()
 9844                    .child(Icon::new(icons.error).color(Color::Error))
 9845                    .cursor_default()
 9846                    .hoverable_tooltip(move |_window, cx| {
 9847                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9848                    })
 9849            })
 9850            .children(keybind)
 9851            .child(
 9852                Label::new(label)
 9853                    .size(LabelSize::Small)
 9854                    .when(!has_keybind, |el| {
 9855                        el.color(cx.theme().status().error.into()).strikethrough()
 9856                    }),
 9857            )
 9858            .when(!has_keybind, |el| {
 9859                el.child(
 9860                    h_flex().ml_1().child(
 9861                        Icon::new(IconName::Info)
 9862                            .size(IconSize::Small)
 9863                            .color(cx.theme().status().error.into()),
 9864                    ),
 9865                )
 9866            })
 9867            .when_some(icon, |element, icon| {
 9868                element.child(
 9869                    div()
 9870                        .mt(px(1.5))
 9871                        .child(Icon::new(icon).size(IconSize::Small)),
 9872                )
 9873            })
 9874    }
 9875
 9876    fn render_edit_prediction_jump_outside_popover(
 9877        &self,
 9878        snapshot: &BufferSnapshot,
 9879        window: &mut Window,
 9880        cx: &mut App,
 9881    ) -> Stateful<Div> {
 9882        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9883        let has_keybind = keybind.is_some();
 9884        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9885
 9886        let file_name = snapshot
 9887            .file()
 9888            .map(|file| SharedString::new(file.file_name(cx)))
 9889            .unwrap_or(SharedString::new_static("untitled"));
 9890
 9891        h_flex()
 9892            .id("ep-jump-outside-popover")
 9893            .py_1()
 9894            .px_2()
 9895            .gap_1()
 9896            .rounded_md()
 9897            .border_1()
 9898            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9899            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9900            .shadow_xs()
 9901            .when(!has_keybind, |el| {
 9902                let status_colors = cx.theme().status();
 9903
 9904                el.bg(status_colors.error_background)
 9905                    .border_color(status_colors.error.opacity(0.6))
 9906                    .pl_2()
 9907                    .child(Icon::new(icons.error).color(Color::Error))
 9908                    .cursor_default()
 9909                    .hoverable_tooltip(move |_window, cx| {
 9910                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9911                    })
 9912            })
 9913            .children(keybind)
 9914            .child(
 9915                Label::new(file_name)
 9916                    .size(LabelSize::Small)
 9917                    .buffer_font(cx)
 9918                    .when(!has_keybind, |el| {
 9919                        el.color(cx.theme().status().error.into()).strikethrough()
 9920                    }),
 9921            )
 9922            .when(!has_keybind, |el| {
 9923                el.child(
 9924                    h_flex().ml_1().child(
 9925                        Icon::new(IconName::Info)
 9926                            .size(IconSize::Small)
 9927                            .color(cx.theme().status().error.into()),
 9928                    ),
 9929                )
 9930            })
 9931            .child(
 9932                div()
 9933                    .mt(px(1.5))
 9934                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
 9935            )
 9936    }
 9937
 9938    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
 9939        let accent_color = cx.theme().colors().text_accent;
 9940        let editor_bg_color = cx.theme().colors().editor_background;
 9941        editor_bg_color.blend(accent_color.opacity(0.1))
 9942    }
 9943
 9944    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
 9945        let accent_color = cx.theme().colors().text_accent;
 9946        let editor_bg_color = cx.theme().colors().editor_background;
 9947        editor_bg_color.blend(accent_color.opacity(0.6))
 9948    }
 9949    fn get_prediction_provider_icons(
 9950        provider: &Option<RegisteredEditPredictionDelegate>,
 9951        cx: &App,
 9952    ) -> edit_prediction_types::EditPredictionIconSet {
 9953        match provider {
 9954            Some(provider) => provider.provider.icons(cx),
 9955            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
 9956        }
 9957    }
 9958
 9959    fn render_edit_prediction_cursor_popover(
 9960        &self,
 9961        min_width: Pixels,
 9962        max_width: Pixels,
 9963        cursor_point: Point,
 9964        style: &EditorStyle,
 9965        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
 9966        _window: &Window,
 9967        cx: &mut Context<Editor>,
 9968    ) -> Option<AnyElement> {
 9969        let provider = self.edit_prediction_provider.as_ref()?;
 9970        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9971
 9972        let is_refreshing = provider.provider.is_refreshing(cx);
 9973
 9974        fn pending_completion_container(icon: IconName) -> Div {
 9975            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
 9976        }
 9977
 9978        let completion = match &self.active_edit_prediction {
 9979            Some(prediction) => {
 9980                if !self.has_visible_completions_menu() {
 9981                    const RADIUS: Pixels = px(6.);
 9982                    const BORDER_WIDTH: Pixels = px(1.);
 9983
 9984                    return Some(
 9985                        h_flex()
 9986                            .elevation_2(cx)
 9987                            .border(BORDER_WIDTH)
 9988                            .border_color(cx.theme().colors().border)
 9989                            .when(accept_keystroke.is_none(), |el| {
 9990                                el.border_color(cx.theme().status().error)
 9991                            })
 9992                            .rounded(RADIUS)
 9993                            .rounded_tl(px(0.))
 9994                            .overflow_hidden()
 9995                            .child(div().px_1p5().child(match &prediction.completion {
 9996                                EditPrediction::MoveWithin { target, snapshot } => {
 9997                                    use text::ToPoint as _;
 9998                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
 9999                                    {
10000                                        Icon::new(icons.down)
10001                                    } else {
10002                                        Icon::new(icons.up)
10003                                    }
10004                                }
10005                                EditPrediction::MoveOutside { .. } => {
10006                                    // TODO [zeta2] custom icon for external jump?
10007                                    Icon::new(icons.base)
10008                                }
10009                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10010                            }))
10011                            .child(
10012                                h_flex()
10013                                    .gap_1()
10014                                    .py_1()
10015                                    .px_2()
10016                                    .rounded_r(RADIUS - BORDER_WIDTH)
10017                                    .border_l_1()
10018                                    .border_color(cx.theme().colors().border)
10019                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10020                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10021                                        el.child(
10022                                            Label::new("Hold")
10023                                                .size(LabelSize::Small)
10024                                                .when(accept_keystroke.is_none(), |el| {
10025                                                    el.strikethrough()
10026                                                })
10027                                                .line_height_style(LineHeightStyle::UiLabel),
10028                                        )
10029                                    })
10030                                    .id("edit_prediction_cursor_popover_keybind")
10031                                    .when(accept_keystroke.is_none(), |el| {
10032                                        let status_colors = cx.theme().status();
10033
10034                                        el.bg(status_colors.error_background)
10035                                            .border_color(status_colors.error.opacity(0.6))
10036                                            .child(Icon::new(IconName::Info).color(Color::Error))
10037                                            .cursor_default()
10038                                            .hoverable_tooltip(move |_window, cx| {
10039                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10040                                                    .into()
10041                                            })
10042                                    })
10043                                    .when_some(
10044                                        accept_keystroke.as_ref(),
10045                                        |el, accept_keystroke| {
10046                                            el.child(h_flex().children(ui::render_modifiers(
10047                                                accept_keystroke.modifiers(),
10048                                                PlatformStyle::platform(),
10049                                                Some(Color::Default),
10050                                                Some(IconSize::XSmall.rems().into()),
10051                                                false,
10052                                            )))
10053                                        },
10054                                    ),
10055                            )
10056                            .into_any(),
10057                    );
10058                }
10059
10060                self.render_edit_prediction_cursor_popover_preview(
10061                    prediction,
10062                    cursor_point,
10063                    style,
10064                    cx,
10065                )?
10066            }
10067
10068            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10069                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10070                    stale_completion,
10071                    cursor_point,
10072                    style,
10073                    cx,
10074                )?,
10075
10076                None => pending_completion_container(icons.base)
10077                    .child(Label::new("...").size(LabelSize::Small)),
10078            },
10079
10080            None => pending_completion_container(icons.base)
10081                .child(Label::new("...").size(LabelSize::Small)),
10082        };
10083
10084        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10085            completion
10086                .with_animation(
10087                    "loading-completion",
10088                    Animation::new(Duration::from_secs(2))
10089                        .repeat()
10090                        .with_easing(pulsating_between(0.4, 0.8)),
10091                    |label, delta| label.opacity(delta),
10092                )
10093                .into_any_element()
10094        } else {
10095            completion.into_any_element()
10096        };
10097
10098        let has_completion = self.active_edit_prediction.is_some();
10099
10100        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10101        Some(
10102            h_flex()
10103                .min_w(min_width)
10104                .max_w(max_width)
10105                .flex_1()
10106                .elevation_2(cx)
10107                .border_color(cx.theme().colors().border)
10108                .child(
10109                    div()
10110                        .flex_1()
10111                        .py_1()
10112                        .px_2()
10113                        .overflow_hidden()
10114                        .child(completion),
10115                )
10116                .when_some(accept_keystroke, |el, accept_keystroke| {
10117                    if !accept_keystroke.modifiers().modified() {
10118                        return el;
10119                    }
10120
10121                    el.child(
10122                        h_flex()
10123                            .h_full()
10124                            .border_l_1()
10125                            .rounded_r_lg()
10126                            .border_color(cx.theme().colors().border)
10127                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10128                            .gap_1()
10129                            .py_1()
10130                            .px_2()
10131                            .child(
10132                                h_flex()
10133                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10134                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10135                                    .child(h_flex().children(ui::render_modifiers(
10136                                        accept_keystroke.modifiers(),
10137                                        PlatformStyle::platform(),
10138                                        Some(if !has_completion {
10139                                            Color::Muted
10140                                        } else {
10141                                            Color::Default
10142                                        }),
10143                                        None,
10144                                        false,
10145                                    ))),
10146                            )
10147                            .child(Label::new("Preview").into_any_element())
10148                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10149                    )
10150                })
10151                .into_any(),
10152        )
10153    }
10154
10155    fn render_edit_prediction_cursor_popover_preview(
10156        &self,
10157        completion: &EditPredictionState,
10158        cursor_point: Point,
10159        style: &EditorStyle,
10160        cx: &mut Context<Editor>,
10161    ) -> Option<Div> {
10162        use text::ToPoint as _;
10163
10164        fn render_relative_row_jump(
10165            prefix: impl Into<String>,
10166            current_row: u32,
10167            target_row: u32,
10168        ) -> Div {
10169            let (row_diff, arrow) = if target_row < current_row {
10170                (current_row - target_row, IconName::ArrowUp)
10171            } else {
10172                (target_row - current_row, IconName::ArrowDown)
10173            };
10174
10175            h_flex()
10176                .child(
10177                    Label::new(format!("{}{}", prefix.into(), row_diff))
10178                        .color(Color::Muted)
10179                        .size(LabelSize::Small),
10180                )
10181                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10182        }
10183
10184        let supports_jump = self
10185            .edit_prediction_provider
10186            .as_ref()
10187            .map(|provider| provider.provider.supports_jump_to_edit())
10188            .unwrap_or(true);
10189
10190        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10191
10192        match &completion.completion {
10193            EditPrediction::MoveWithin {
10194                target, snapshot, ..
10195            } => {
10196                if !supports_jump {
10197                    return None;
10198                }
10199
10200                Some(
10201                    h_flex()
10202                        .px_2()
10203                        .gap_2()
10204                        .flex_1()
10205                        .child(
10206                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10207                                Icon::new(icons.down)
10208                            } else {
10209                                Icon::new(icons.up)
10210                            },
10211                        )
10212                        .child(Label::new("Jump to Edit")),
10213                )
10214            }
10215            EditPrediction::MoveOutside { snapshot, .. } => {
10216                let file_name = snapshot
10217                    .file()
10218                    .map(|file| file.file_name(cx))
10219                    .unwrap_or("untitled");
10220                Some(
10221                    h_flex()
10222                        .px_2()
10223                        .gap_2()
10224                        .flex_1()
10225                        .child(Icon::new(icons.base))
10226                        .child(Label::new(format!("Jump to {file_name}"))),
10227                )
10228            }
10229            EditPrediction::Edit {
10230                edits,
10231                edit_preview,
10232                snapshot,
10233                ..
10234            } => {
10235                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10236
10237                let (highlighted_edits, has_more_lines) =
10238                    if let Some(edit_preview) = edit_preview.as_ref() {
10239                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10240                            .first_line_preview()
10241                    } else {
10242                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10243                    };
10244
10245                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10246                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10247
10248                let preview = h_flex()
10249                    .gap_1()
10250                    .min_w_16()
10251                    .child(styled_text)
10252                    .when(has_more_lines, |parent| parent.child(""));
10253
10254                let left = if supports_jump && first_edit_row != cursor_point.row {
10255                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10256                        .into_any_element()
10257                } else {
10258                    Icon::new(icons.base).into_any_element()
10259                };
10260
10261                Some(
10262                    h_flex()
10263                        .h_full()
10264                        .flex_1()
10265                        .gap_2()
10266                        .pr_1()
10267                        .overflow_x_hidden()
10268                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10269                        .child(left)
10270                        .child(preview),
10271                )
10272            }
10273        }
10274    }
10275
10276    pub fn render_context_menu(
10277        &mut self,
10278        max_height_in_lines: u32,
10279        window: &mut Window,
10280        cx: &mut Context<Editor>,
10281    ) -> Option<AnyElement> {
10282        let menu = self.context_menu.borrow();
10283        let menu = menu.as_ref()?;
10284        if !menu.visible() {
10285            return None;
10286        };
10287        self.style
10288            .as_ref()
10289            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10290    }
10291
10292    fn render_context_menu_aside(
10293        &mut self,
10294        max_size: Size<Pixels>,
10295        window: &mut Window,
10296        cx: &mut Context<Editor>,
10297    ) -> Option<AnyElement> {
10298        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10299            if menu.visible() {
10300                menu.render_aside(max_size, window, cx)
10301            } else {
10302                None
10303            }
10304        })
10305    }
10306
10307    fn hide_context_menu(
10308        &mut self,
10309        window: &mut Window,
10310        cx: &mut Context<Self>,
10311    ) -> Option<CodeContextMenu> {
10312        cx.notify();
10313        self.completion_tasks.clear();
10314        let context_menu = self.context_menu.borrow_mut().take();
10315        self.stale_edit_prediction_in_menu.take();
10316        self.update_visible_edit_prediction(window, cx);
10317        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10318            && let Some(completion_provider) = &self.completion_provider
10319        {
10320            completion_provider.selection_changed(None, window, cx);
10321        }
10322        context_menu
10323    }
10324
10325    fn show_snippet_choices(
10326        &mut self,
10327        choices: &Vec<String>,
10328        selection: Range<Anchor>,
10329        cx: &mut Context<Self>,
10330    ) {
10331        let Some((_, buffer, _)) = self
10332            .buffer()
10333            .read(cx)
10334            .excerpt_containing(selection.start, cx)
10335        else {
10336            return;
10337        };
10338        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10339        else {
10340            return;
10341        };
10342        if buffer != end_buffer {
10343            log::error!("expected anchor range to have matching buffer IDs");
10344            return;
10345        }
10346
10347        let id = post_inc(&mut self.next_completion_id);
10348        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10349        let mut context_menu = self.context_menu.borrow_mut();
10350        let old_menu = context_menu.take();
10351        *context_menu = Some(CodeContextMenu::Completions(
10352            CompletionsMenu::new_snippet_choices(
10353                id,
10354                true,
10355                choices,
10356                selection,
10357                buffer,
10358                old_menu.map(|menu| menu.primary_scroll_handle()),
10359                snippet_sort_order,
10360            ),
10361        ));
10362    }
10363
10364    pub fn insert_snippet(
10365        &mut self,
10366        insertion_ranges: &[Range<MultiBufferOffset>],
10367        snippet: Snippet,
10368        window: &mut Window,
10369        cx: &mut Context<Self>,
10370    ) -> Result<()> {
10371        struct Tabstop<T> {
10372            is_end_tabstop: bool,
10373            ranges: Vec<Range<T>>,
10374            choices: Option<Vec<String>>,
10375        }
10376
10377        let tabstops = self.buffer.update(cx, |buffer, cx| {
10378            let snippet_text: Arc<str> = snippet.text.clone().into();
10379            let edits = insertion_ranges
10380                .iter()
10381                .cloned()
10382                .map(|range| (range, snippet_text.clone()));
10383            let autoindent_mode = AutoindentMode::Block {
10384                original_indent_columns: Vec::new(),
10385            };
10386            buffer.edit(edits, Some(autoindent_mode), cx);
10387
10388            let snapshot = &*buffer.read(cx);
10389            let snippet = &snippet;
10390            snippet
10391                .tabstops
10392                .iter()
10393                .map(|tabstop| {
10394                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10395                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10396                    });
10397                    let mut tabstop_ranges = tabstop
10398                        .ranges
10399                        .iter()
10400                        .flat_map(|tabstop_range| {
10401                            let mut delta = 0_isize;
10402                            insertion_ranges.iter().map(move |insertion_range| {
10403                                let insertion_start = insertion_range.start + delta;
10404                                delta += snippet.text.len() as isize
10405                                    - (insertion_range.end - insertion_range.start) as isize;
10406
10407                                let start =
10408                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10409                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10410                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10411                            })
10412                        })
10413                        .collect::<Vec<_>>();
10414                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10415
10416                    Tabstop {
10417                        is_end_tabstop,
10418                        ranges: tabstop_ranges,
10419                        choices: tabstop.choices.clone(),
10420                    }
10421                })
10422                .collect::<Vec<_>>()
10423        });
10424        if let Some(tabstop) = tabstops.first() {
10425            self.change_selections(Default::default(), window, cx, |s| {
10426                // Reverse order so that the first range is the newest created selection.
10427                // Completions will use it and autoscroll will prioritize it.
10428                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10429            });
10430
10431            if let Some(choices) = &tabstop.choices
10432                && let Some(selection) = tabstop.ranges.first()
10433            {
10434                self.show_snippet_choices(choices, selection.clone(), cx)
10435            }
10436
10437            // If we're already at the last tabstop and it's at the end of the snippet,
10438            // we're done, we don't need to keep the state around.
10439            if !tabstop.is_end_tabstop {
10440                let choices = tabstops
10441                    .iter()
10442                    .map(|tabstop| tabstop.choices.clone())
10443                    .collect();
10444
10445                let ranges = tabstops
10446                    .into_iter()
10447                    .map(|tabstop| tabstop.ranges)
10448                    .collect::<Vec<_>>();
10449
10450                self.snippet_stack.push(SnippetState {
10451                    active_index: 0,
10452                    ranges,
10453                    choices,
10454                });
10455            }
10456
10457            // Check whether the just-entered snippet ends with an auto-closable bracket.
10458            if self.autoclose_regions.is_empty() {
10459                let snapshot = self.buffer.read(cx).snapshot(cx);
10460                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10461                    let selection_head = selection.head();
10462                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10463                        continue;
10464                    };
10465
10466                    let mut bracket_pair = None;
10467                    let max_lookup_length = scope
10468                        .brackets()
10469                        .map(|(pair, _)| {
10470                            pair.start
10471                                .as_str()
10472                                .chars()
10473                                .count()
10474                                .max(pair.end.as_str().chars().count())
10475                        })
10476                        .max();
10477                    if let Some(max_lookup_length) = max_lookup_length {
10478                        let next_text = snapshot
10479                            .chars_at(selection_head)
10480                            .take(max_lookup_length)
10481                            .collect::<String>();
10482                        let prev_text = snapshot
10483                            .reversed_chars_at(selection_head)
10484                            .take(max_lookup_length)
10485                            .collect::<String>();
10486
10487                        for (pair, enabled) in scope.brackets() {
10488                            if enabled
10489                                && pair.close
10490                                && prev_text.starts_with(pair.start.as_str())
10491                                && next_text.starts_with(pair.end.as_str())
10492                            {
10493                                bracket_pair = Some(pair.clone());
10494                                break;
10495                            }
10496                        }
10497                    }
10498
10499                    if let Some(pair) = bracket_pair {
10500                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10501                        let autoclose_enabled =
10502                            self.use_autoclose && snapshot_settings.use_autoclose;
10503                        if autoclose_enabled {
10504                            let start = snapshot.anchor_after(selection_head);
10505                            let end = snapshot.anchor_after(selection_head);
10506                            self.autoclose_regions.push(AutocloseRegion {
10507                                selection_id: selection.id,
10508                                range: start..end,
10509                                pair,
10510                            });
10511                        }
10512                    }
10513                }
10514            }
10515        }
10516        Ok(())
10517    }
10518
10519    pub fn move_to_next_snippet_tabstop(
10520        &mut self,
10521        window: &mut Window,
10522        cx: &mut Context<Self>,
10523    ) -> bool {
10524        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10525    }
10526
10527    pub fn move_to_prev_snippet_tabstop(
10528        &mut self,
10529        window: &mut Window,
10530        cx: &mut Context<Self>,
10531    ) -> bool {
10532        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10533    }
10534
10535    pub fn move_to_snippet_tabstop(
10536        &mut self,
10537        bias: Bias,
10538        window: &mut Window,
10539        cx: &mut Context<Self>,
10540    ) -> bool {
10541        if let Some(mut snippet) = self.snippet_stack.pop() {
10542            match bias {
10543                Bias::Left => {
10544                    if snippet.active_index > 0 {
10545                        snippet.active_index -= 1;
10546                    } else {
10547                        self.snippet_stack.push(snippet);
10548                        return false;
10549                    }
10550                }
10551                Bias::Right => {
10552                    if snippet.active_index + 1 < snippet.ranges.len() {
10553                        snippet.active_index += 1;
10554                    } else {
10555                        self.snippet_stack.push(snippet);
10556                        return false;
10557                    }
10558                }
10559            }
10560            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10561                self.change_selections(Default::default(), window, cx, |s| {
10562                    // Reverse order so that the first range is the newest created selection.
10563                    // Completions will use it and autoscroll will prioritize it.
10564                    s.select_ranges(current_ranges.iter().rev().cloned())
10565                });
10566
10567                if let Some(choices) = &snippet.choices[snippet.active_index]
10568                    && let Some(selection) = current_ranges.first()
10569                {
10570                    self.show_snippet_choices(choices, selection.clone(), cx);
10571                }
10572
10573                // If snippet state is not at the last tabstop, push it back on the stack
10574                if snippet.active_index + 1 < snippet.ranges.len() {
10575                    self.snippet_stack.push(snippet);
10576                }
10577                return true;
10578            }
10579        }
10580
10581        false
10582    }
10583
10584    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10585        self.transact(window, cx, |this, window, cx| {
10586            this.select_all(&SelectAll, window, cx);
10587            this.insert("", window, cx);
10588        });
10589    }
10590
10591    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10592        if self.read_only(cx) {
10593            return;
10594        }
10595        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10596        self.transact(window, cx, |this, window, cx| {
10597            this.select_autoclose_pair(window, cx);
10598
10599            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10600
10601            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10602            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10603            for selection in &mut selections {
10604                if selection.is_empty() {
10605                    let old_head = selection.head();
10606                    let mut new_head =
10607                        movement::left(&display_map, old_head.to_display_point(&display_map))
10608                            .to_point(&display_map);
10609                    if let Some((buffer, line_buffer_range)) = display_map
10610                        .buffer_snapshot()
10611                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10612                    {
10613                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10614                        let indent_len = match indent_size.kind {
10615                            IndentKind::Space => {
10616                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10617                            }
10618                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10619                        };
10620                        if old_head.column <= indent_size.len && old_head.column > 0 {
10621                            let indent_len = indent_len.get();
10622                            new_head = cmp::min(
10623                                new_head,
10624                                MultiBufferPoint::new(
10625                                    old_head.row,
10626                                    ((old_head.column - 1) / indent_len) * indent_len,
10627                                ),
10628                            );
10629                        }
10630                    }
10631
10632                    selection.set_head(new_head, SelectionGoal::None);
10633                }
10634            }
10635
10636            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10637            this.insert("", window, cx);
10638            linked_edits.apply_with_left_expansion(cx);
10639            this.refresh_edit_prediction(true, false, window, cx);
10640            refresh_linked_ranges(this, window, cx);
10641        });
10642    }
10643
10644    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10645        if self.read_only(cx) {
10646            return;
10647        }
10648        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10649        self.transact(window, cx, |this, window, cx| {
10650            this.change_selections(Default::default(), window, cx, |s| {
10651                s.move_with(&mut |map, selection| {
10652                    if selection.is_empty() {
10653                        let cursor = movement::right(map, selection.head());
10654                        selection.end = cursor;
10655                        selection.reversed = true;
10656                        selection.goal = SelectionGoal::None;
10657                    }
10658                })
10659            });
10660            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10661            this.insert("", window, cx);
10662            linked_edits.apply(cx);
10663            this.refresh_edit_prediction(true, false, window, cx);
10664            refresh_linked_ranges(this, window, cx);
10665        });
10666    }
10667
10668    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10669        if self.mode.is_single_line() {
10670            cx.propagate();
10671            return;
10672        }
10673
10674        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10675        if self.move_to_prev_snippet_tabstop(window, cx) {
10676            return;
10677        }
10678        self.outdent(&Outdent, window, cx);
10679    }
10680
10681    pub fn next_snippet_tabstop(
10682        &mut self,
10683        _: &NextSnippetTabstop,
10684        window: &mut Window,
10685        cx: &mut Context<Self>,
10686    ) {
10687        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10688            cx.propagate();
10689            return;
10690        }
10691
10692        if self.move_to_next_snippet_tabstop(window, cx) {
10693            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10694            return;
10695        }
10696        cx.propagate();
10697    }
10698
10699    pub fn previous_snippet_tabstop(
10700        &mut self,
10701        _: &PreviousSnippetTabstop,
10702        window: &mut Window,
10703        cx: &mut Context<Self>,
10704    ) {
10705        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10706            cx.propagate();
10707            return;
10708        }
10709
10710        if self.move_to_prev_snippet_tabstop(window, cx) {
10711            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10712            return;
10713        }
10714        cx.propagate();
10715    }
10716
10717    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10718        if self.mode.is_single_line() {
10719            cx.propagate();
10720            return;
10721        }
10722
10723        if self.move_to_next_snippet_tabstop(window, cx) {
10724            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10725            return;
10726        }
10727        if self.read_only(cx) {
10728            return;
10729        }
10730        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10731        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10732        let buffer = self.buffer.read(cx);
10733        let snapshot = buffer.snapshot(cx);
10734        let rows_iter = selections.iter().map(|s| s.head().row);
10735        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10736
10737        let has_some_cursor_in_whitespace = selections
10738            .iter()
10739            .filter(|selection| selection.is_empty())
10740            .any(|selection| {
10741                let cursor = selection.head();
10742                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10743                cursor.column < current_indent.len
10744            });
10745
10746        let mut edits = Vec::new();
10747        let mut prev_edited_row = 0;
10748        let mut row_delta = 0;
10749        for selection in &mut selections {
10750            if selection.start.row != prev_edited_row {
10751                row_delta = 0;
10752            }
10753            prev_edited_row = selection.end.row;
10754
10755            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10756            if selection.is_empty() {
10757                let cursor = selection.head();
10758                let settings = buffer.language_settings_at(cursor, cx);
10759                if settings.indent_list_on_tab {
10760                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10761                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10762                            row_delta = Self::indent_selection(
10763                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10764                            );
10765                            continue;
10766                        }
10767                    }
10768                }
10769            }
10770
10771            // If the selection is non-empty, then increase the indentation of the selected lines.
10772            if !selection.is_empty() {
10773                row_delta =
10774                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10775                continue;
10776            }
10777
10778            let cursor = selection.head();
10779            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10780            if let Some(suggested_indent) =
10781                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10782            {
10783                // Don't do anything if already at suggested indent
10784                // and there is any other cursor which is not
10785                if has_some_cursor_in_whitespace
10786                    && cursor.column == current_indent.len
10787                    && current_indent.len == suggested_indent.len
10788                {
10789                    continue;
10790                }
10791
10792                // Adjust line and move cursor to suggested indent
10793                // if cursor is not at suggested indent
10794                if cursor.column < suggested_indent.len
10795                    && cursor.column <= current_indent.len
10796                    && current_indent.len <= suggested_indent.len
10797                {
10798                    selection.start = Point::new(cursor.row, suggested_indent.len);
10799                    selection.end = selection.start;
10800                    if row_delta == 0 {
10801                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10802                            cursor.row,
10803                            current_indent,
10804                            suggested_indent,
10805                        ));
10806                        row_delta = suggested_indent.len - current_indent.len;
10807                    }
10808                    continue;
10809                }
10810
10811                // If current indent is more than suggested indent
10812                // only move cursor to current indent and skip indent
10813                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10814                    selection.start = Point::new(cursor.row, current_indent.len);
10815                    selection.end = selection.start;
10816                    continue;
10817                }
10818            }
10819
10820            // Otherwise, insert a hard or soft tab.
10821            let settings = buffer.language_settings_at(cursor, cx);
10822            let tab_size = if settings.hard_tabs {
10823                IndentSize::tab()
10824            } else {
10825                let tab_size = settings.tab_size.get();
10826                let indent_remainder = snapshot
10827                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10828                    .flat_map(str::chars)
10829                    .fold(row_delta % tab_size, |counter: u32, c| {
10830                        if c == '\t' {
10831                            0
10832                        } else {
10833                            (counter + 1) % tab_size
10834                        }
10835                    });
10836
10837                let chars_to_next_tab_stop = tab_size - indent_remainder;
10838                IndentSize::spaces(chars_to_next_tab_stop)
10839            };
10840            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10841            selection.end = selection.start;
10842            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10843            row_delta += tab_size.len;
10844        }
10845
10846        self.transact(window, cx, |this, window, cx| {
10847            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10848            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10849            this.refresh_edit_prediction(true, false, window, cx);
10850        });
10851    }
10852
10853    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10854        if self.read_only(cx) {
10855            return;
10856        }
10857        if self.mode.is_single_line() {
10858            cx.propagate();
10859            return;
10860        }
10861
10862        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10863        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
10864        let mut prev_edited_row = 0;
10865        let mut row_delta = 0;
10866        let mut edits = Vec::new();
10867        let buffer = self.buffer.read(cx);
10868        let snapshot = buffer.snapshot(cx);
10869        for selection in &mut selections {
10870            if selection.start.row != prev_edited_row {
10871                row_delta = 0;
10872            }
10873            prev_edited_row = selection.end.row;
10874
10875            row_delta =
10876                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10877        }
10878
10879        self.transact(window, cx, |this, window, cx| {
10880            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10881            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10882        });
10883    }
10884
10885    fn indent_selection(
10886        buffer: &MultiBuffer,
10887        snapshot: &MultiBufferSnapshot,
10888        selection: &mut Selection<Point>,
10889        edits: &mut Vec<(Range<Point>, String)>,
10890        delta_for_start_row: u32,
10891        cx: &App,
10892    ) -> u32 {
10893        let settings = buffer.language_settings_at(selection.start, cx);
10894        let tab_size = settings.tab_size.get();
10895        let indent_kind = if settings.hard_tabs {
10896            IndentKind::Tab
10897        } else {
10898            IndentKind::Space
10899        };
10900        let mut start_row = selection.start.row;
10901        let mut end_row = selection.end.row + 1;
10902
10903        // If a selection ends at the beginning of a line, don't indent
10904        // that last line.
10905        if selection.end.column == 0 && selection.end.row > selection.start.row {
10906            end_row -= 1;
10907        }
10908
10909        // Avoid re-indenting a row that has already been indented by a
10910        // previous selection, but still update this selection's column
10911        // to reflect that indentation.
10912        if delta_for_start_row > 0 {
10913            start_row += 1;
10914            selection.start.column += delta_for_start_row;
10915            if selection.end.row == selection.start.row {
10916                selection.end.column += delta_for_start_row;
10917            }
10918        }
10919
10920        let mut delta_for_end_row = 0;
10921        let has_multiple_rows = start_row + 1 != end_row;
10922        for row in start_row..end_row {
10923            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
10924            let indent_delta = match (current_indent.kind, indent_kind) {
10925                (IndentKind::Space, IndentKind::Space) => {
10926                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
10927                    IndentSize::spaces(columns_to_next_tab_stop)
10928                }
10929                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
10930                (_, IndentKind::Tab) => IndentSize::tab(),
10931            };
10932
10933            let start = if has_multiple_rows || current_indent.len < selection.start.column {
10934                0
10935            } else {
10936                selection.start.column
10937            };
10938            let row_start = Point::new(row, start);
10939            edits.push((
10940                row_start..row_start,
10941                indent_delta.chars().collect::<String>(),
10942            ));
10943
10944            // Update this selection's endpoints to reflect the indentation.
10945            if row == selection.start.row {
10946                selection.start.column += indent_delta.len;
10947            }
10948            if row == selection.end.row {
10949                selection.end.column += indent_delta.len;
10950                delta_for_end_row = indent_delta.len;
10951            }
10952        }
10953
10954        if selection.start.row == selection.end.row {
10955            delta_for_start_row + delta_for_end_row
10956        } else {
10957            delta_for_end_row
10958        }
10959    }
10960
10961    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10962        if self.read_only(cx) {
10963            return;
10964        }
10965        if self.mode.is_single_line() {
10966            cx.propagate();
10967            return;
10968        }
10969
10970        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10971        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10972        let selections = self.selections.all::<Point>(&display_map);
10973        let mut deletion_ranges = Vec::new();
10974        let mut last_outdent = None;
10975        {
10976            let buffer = self.buffer.read(cx);
10977            let snapshot = buffer.snapshot(cx);
10978            for selection in &selections {
10979                let settings = buffer.language_settings_at(selection.start, cx);
10980                let tab_size = settings.tab_size.get();
10981                let mut rows = selection.spanned_rows(false, &display_map);
10982
10983                // Avoid re-outdenting a row that has already been outdented by a
10984                // previous selection.
10985                if let Some(last_row) = last_outdent
10986                    && last_row == rows.start
10987                {
10988                    rows.start = rows.start.next_row();
10989                }
10990                let has_multiple_rows = rows.len() > 1;
10991                for row in rows.iter_rows() {
10992                    let indent_size = snapshot.indent_size_for_line(row);
10993                    if indent_size.len > 0 {
10994                        let deletion_len = match indent_size.kind {
10995                            IndentKind::Space => {
10996                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
10997                                if columns_to_prev_tab_stop == 0 {
10998                                    tab_size
10999                                } else {
11000                                    columns_to_prev_tab_stop
11001                                }
11002                            }
11003                            IndentKind::Tab => 1,
11004                        };
11005                        let start = if has_multiple_rows
11006                            || deletion_len > selection.start.column
11007                            || indent_size.len < selection.start.column
11008                        {
11009                            0
11010                        } else {
11011                            selection.start.column - deletion_len
11012                        };
11013                        deletion_ranges.push(
11014                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11015                        );
11016                        last_outdent = Some(row);
11017                    }
11018                }
11019            }
11020        }
11021
11022        self.transact(window, cx, |this, window, cx| {
11023            this.buffer.update(cx, |buffer, cx| {
11024                let empty_str: Arc<str> = Arc::default();
11025                buffer.edit(
11026                    deletion_ranges
11027                        .into_iter()
11028                        .map(|range| (range, empty_str.clone())),
11029                    None,
11030                    cx,
11031                );
11032            });
11033            let selections = this
11034                .selections
11035                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11036            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11037        });
11038    }
11039
11040    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11041        if self.read_only(cx) {
11042            return;
11043        }
11044        if self.mode.is_single_line() {
11045            cx.propagate();
11046            return;
11047        }
11048
11049        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11050        let selections = self
11051            .selections
11052            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11053            .into_iter()
11054            .map(|s| s.range());
11055
11056        self.transact(window, cx, |this, window, cx| {
11057            this.buffer.update(cx, |buffer, cx| {
11058                buffer.autoindent_ranges(selections, cx);
11059            });
11060            let selections = this
11061                .selections
11062                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11063            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11064        });
11065    }
11066
11067    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11068        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11069        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11070        let selections = self.selections.all::<Point>(&display_map);
11071
11072        let mut new_cursors = Vec::new();
11073        let mut edit_ranges = Vec::new();
11074        let mut selections = selections.iter().peekable();
11075        while let Some(selection) = selections.next() {
11076            let mut rows = selection.spanned_rows(false, &display_map);
11077
11078            // Accumulate contiguous regions of rows that we want to delete.
11079            while let Some(next_selection) = selections.peek() {
11080                let next_rows = next_selection.spanned_rows(false, &display_map);
11081                if next_rows.start <= rows.end {
11082                    rows.end = next_rows.end;
11083                    selections.next().unwrap();
11084                } else {
11085                    break;
11086                }
11087            }
11088
11089            let buffer = display_map.buffer_snapshot();
11090            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11091            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11092                // If there's a line after the range, delete the \n from the end of the row range
11093                (
11094                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11095                    rows.end,
11096                )
11097            } else {
11098                // If there isn't a line after the range, delete the \n from the line before the
11099                // start of the row range
11100                edit_start = edit_start.saturating_sub_usize(1);
11101                (buffer.len(), rows.start.previous_row())
11102            };
11103
11104            let text_layout_details = self.text_layout_details(window, cx);
11105            let x = display_map.x_for_display_point(
11106                selection.head().to_display_point(&display_map),
11107                &text_layout_details,
11108            );
11109            let row = Point::new(target_row.0, 0)
11110                .to_display_point(&display_map)
11111                .row();
11112            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11113
11114            new_cursors.push((
11115                selection.id,
11116                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11117                SelectionGoal::None,
11118            ));
11119            edit_ranges.push(edit_start..edit_end);
11120        }
11121
11122        self.transact(window, cx, |this, window, cx| {
11123            let buffer = this.buffer.update(cx, |buffer, cx| {
11124                let empty_str: Arc<str> = Arc::default();
11125                buffer.edit(
11126                    edit_ranges
11127                        .into_iter()
11128                        .map(|range| (range, empty_str.clone())),
11129                    None,
11130                    cx,
11131                );
11132                buffer.snapshot(cx)
11133            });
11134            let new_selections = new_cursors
11135                .into_iter()
11136                .map(|(id, cursor, goal)| {
11137                    let cursor = cursor.to_point(&buffer);
11138                    Selection {
11139                        id,
11140                        start: cursor,
11141                        end: cursor,
11142                        reversed: false,
11143                        goal,
11144                    }
11145                })
11146                .collect();
11147
11148            this.change_selections(Default::default(), window, cx, |s| {
11149                s.select(new_selections);
11150            });
11151        });
11152    }
11153
11154    pub fn join_lines_impl(
11155        &mut self,
11156        insert_whitespace: bool,
11157        window: &mut Window,
11158        cx: &mut Context<Self>,
11159    ) {
11160        if self.read_only(cx) {
11161            return;
11162        }
11163        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11164        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11165            let start = MultiBufferRow(selection.start.row);
11166            // Treat single line selections as if they include the next line. Otherwise this action
11167            // would do nothing for single line selections individual cursors.
11168            let end = if selection.start.row == selection.end.row {
11169                MultiBufferRow(selection.start.row + 1)
11170            } else if selection.end.column == 0 {
11171                // If the selection ends at the start of a line, it's logically at the end of the
11172                // previous line (plus its newline).
11173                // Don't include the end line unless there's only one line selected.
11174                if selection.start.row + 1 == selection.end.row {
11175                    MultiBufferRow(selection.end.row)
11176                } else {
11177                    MultiBufferRow(selection.end.row - 1)
11178                }
11179            } else {
11180                MultiBufferRow(selection.end.row)
11181            };
11182
11183            if let Some(last_row_range) = row_ranges.last_mut()
11184                && start <= last_row_range.end
11185            {
11186                last_row_range.end = end;
11187                continue;
11188            }
11189            row_ranges.push(start..end);
11190        }
11191
11192        let snapshot = self.buffer.read(cx).snapshot(cx);
11193        let mut cursor_positions = Vec::new();
11194        for row_range in &row_ranges {
11195            let anchor = snapshot.anchor_before(Point::new(
11196                row_range.end.previous_row().0,
11197                snapshot.line_len(row_range.end.previous_row()),
11198            ));
11199            cursor_positions.push(anchor..anchor);
11200        }
11201
11202        self.transact(window, cx, |this, window, cx| {
11203            for row_range in row_ranges.into_iter().rev() {
11204                for row in row_range.iter_rows().rev() {
11205                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11206                    let next_line_row = row.next_row();
11207                    let indent = snapshot.indent_size_for_line(next_line_row);
11208                    let mut join_start_column = indent.len;
11209
11210                    if let Some(language_scope) =
11211                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11212                    {
11213                        let line_end =
11214                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11215                        let line_text_after_indent = snapshot
11216                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11217                            .collect::<String>();
11218
11219                        if !line_text_after_indent.is_empty() {
11220                            let block_prefix = language_scope
11221                                .block_comment()
11222                                .map(|c| c.prefix.as_ref())
11223                                .filter(|p| !p.is_empty());
11224                            let doc_prefix = language_scope
11225                                .documentation_comment()
11226                                .map(|c| c.prefix.as_ref())
11227                                .filter(|p| !p.is_empty());
11228                            let all_prefixes = language_scope
11229                                .line_comment_prefixes()
11230                                .iter()
11231                                .map(|p| p.as_ref())
11232                                .chain(block_prefix)
11233                                .chain(doc_prefix)
11234                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11235
11236                            let mut longest_prefix_len = None;
11237                            for prefix in all_prefixes {
11238                                let trimmed = prefix.trim_end();
11239                                if line_text_after_indent.starts_with(trimmed) {
11240                                    let candidate_len =
11241                                        if line_text_after_indent.starts_with(prefix) {
11242                                            prefix.len()
11243                                        } else {
11244                                            trimmed.len()
11245                                        };
11246                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11247                                        longest_prefix_len = Some(candidate_len);
11248                                    }
11249                                }
11250                            }
11251
11252                            if let Some(prefix_len) = longest_prefix_len {
11253                                join_start_column =
11254                                    join_start_column.saturating_add(prefix_len as u32);
11255                            }
11256                        }
11257                    }
11258
11259                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11260
11261                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11262                        && insert_whitespace
11263                    {
11264                        " "
11265                    } else {
11266                        ""
11267                    };
11268
11269                    this.buffer.update(cx, |buffer, cx| {
11270                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11271                    });
11272                }
11273            }
11274
11275            this.change_selections(Default::default(), window, cx, |s| {
11276                s.select_anchor_ranges(cursor_positions)
11277            });
11278        });
11279    }
11280
11281    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11282        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11283        self.join_lines_impl(true, window, cx);
11284    }
11285
11286    pub fn sort_lines_case_sensitive(
11287        &mut self,
11288        _: &SortLinesCaseSensitive,
11289        window: &mut Window,
11290        cx: &mut Context<Self>,
11291    ) {
11292        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11293    }
11294
11295    pub fn sort_lines_by_length(
11296        &mut self,
11297        _: &SortLinesByLength,
11298        window: &mut Window,
11299        cx: &mut Context<Self>,
11300    ) {
11301        self.manipulate_immutable_lines(window, cx, |lines| {
11302            lines.sort_by_key(|&line| line.chars().count())
11303        })
11304    }
11305
11306    pub fn sort_lines_case_insensitive(
11307        &mut self,
11308        _: &SortLinesCaseInsensitive,
11309        window: &mut Window,
11310        cx: &mut Context<Self>,
11311    ) {
11312        self.manipulate_immutable_lines(window, cx, |lines| {
11313            lines.sort_by_key(|line| line.to_lowercase())
11314        })
11315    }
11316
11317    pub fn unique_lines_case_insensitive(
11318        &mut self,
11319        _: &UniqueLinesCaseInsensitive,
11320        window: &mut Window,
11321        cx: &mut Context<Self>,
11322    ) {
11323        self.manipulate_immutable_lines(window, cx, |lines| {
11324            let mut seen = HashSet::default();
11325            lines.retain(|line| seen.insert(line.to_lowercase()));
11326        })
11327    }
11328
11329    pub fn unique_lines_case_sensitive(
11330        &mut self,
11331        _: &UniqueLinesCaseSensitive,
11332        window: &mut Window,
11333        cx: &mut Context<Self>,
11334    ) {
11335        self.manipulate_immutable_lines(window, cx, |lines| {
11336            let mut seen = HashSet::default();
11337            lines.retain(|line| seen.insert(*line));
11338        })
11339    }
11340
11341    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11342        let snapshot = self.buffer.read(cx).snapshot(cx);
11343        for selection in self.selections.disjoint_anchors_arc().iter() {
11344            if snapshot
11345                .language_at(selection.start)
11346                .and_then(|lang| lang.config().wrap_characters.as_ref())
11347                .is_some()
11348            {
11349                return true;
11350            }
11351        }
11352        false
11353    }
11354
11355    fn wrap_selections_in_tag(
11356        &mut self,
11357        _: &WrapSelectionsInTag,
11358        window: &mut Window,
11359        cx: &mut Context<Self>,
11360    ) {
11361        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11362
11363        let snapshot = self.buffer.read(cx).snapshot(cx);
11364
11365        let mut edits = Vec::new();
11366        let mut boundaries = Vec::new();
11367
11368        for selection in self
11369            .selections
11370            .all_adjusted(&self.display_snapshot(cx))
11371            .iter()
11372        {
11373            let Some(wrap_config) = snapshot
11374                .language_at(selection.start)
11375                .and_then(|lang| lang.config().wrap_characters.clone())
11376            else {
11377                continue;
11378            };
11379
11380            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11381            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11382
11383            let start_before = snapshot.anchor_before(selection.start);
11384            let end_after = snapshot.anchor_after(selection.end);
11385
11386            edits.push((start_before..start_before, open_tag));
11387            edits.push((end_after..end_after, close_tag));
11388
11389            boundaries.push((
11390                start_before,
11391                end_after,
11392                wrap_config.start_prefix.len(),
11393                wrap_config.end_suffix.len(),
11394            ));
11395        }
11396
11397        if edits.is_empty() {
11398            return;
11399        }
11400
11401        self.transact(window, cx, |this, window, cx| {
11402            let buffer = this.buffer.update(cx, |buffer, cx| {
11403                buffer.edit(edits, None, cx);
11404                buffer.snapshot(cx)
11405            });
11406
11407            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11408            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11409                boundaries.into_iter()
11410            {
11411                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11412                let close_offset = end_after
11413                    .to_offset(&buffer)
11414                    .saturating_sub_usize(end_suffix_len);
11415                new_selections.push(open_offset..open_offset);
11416                new_selections.push(close_offset..close_offset);
11417            }
11418
11419            this.change_selections(Default::default(), window, cx, |s| {
11420                s.select_ranges(new_selections);
11421            });
11422
11423            this.request_autoscroll(Autoscroll::fit(), cx);
11424        });
11425    }
11426
11427    pub fn toggle_read_only(
11428        &mut self,
11429        _: &workspace::ToggleReadOnlyFile,
11430        _: &mut Window,
11431        cx: &mut Context<Self>,
11432    ) {
11433        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11434            buffer.update(cx, |buffer, cx| {
11435                buffer.set_capability(
11436                    match buffer.capability() {
11437                        Capability::ReadWrite => Capability::Read,
11438                        Capability::Read => Capability::ReadWrite,
11439                        Capability::ReadOnly => Capability::ReadOnly,
11440                    },
11441                    cx,
11442                );
11443            })
11444        }
11445    }
11446
11447    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11448        let Some(project) = self.project.clone() else {
11449            return;
11450        };
11451        let task = self.reload(project, window, cx);
11452        self.detach_and_notify_err(task, window, cx);
11453    }
11454
11455    pub fn restore_file(
11456        &mut self,
11457        _: &::git::RestoreFile,
11458        window: &mut Window,
11459        cx: &mut Context<Self>,
11460    ) {
11461        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11462        let mut buffer_ids = HashSet::default();
11463        let snapshot = self.buffer().read(cx).snapshot(cx);
11464        for selection in self
11465            .selections
11466            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11467        {
11468            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11469        }
11470
11471        let buffer = self.buffer().read(cx);
11472        let ranges = buffer_ids
11473            .into_iter()
11474            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11475            .collect::<Vec<_>>();
11476
11477        self.restore_hunks_in_ranges(ranges, window, cx);
11478    }
11479
11480    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11481        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11482        let selections = self
11483            .selections
11484            .all(&self.display_snapshot(cx))
11485            .into_iter()
11486            .map(|s| s.range())
11487            .collect();
11488        self.restore_hunks_in_ranges(selections, window, cx);
11489    }
11490
11491    /// Restores the diff hunks in the editor's selections and moves the cursor
11492    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11493    /// not all diff hunks are expanded.
11494    pub fn restore_and_next(
11495        &mut self,
11496        _: &::git::RestoreAndNext,
11497        window: &mut Window,
11498        cx: &mut Context<Self>,
11499    ) {
11500        let selections = self
11501            .selections
11502            .all(&self.display_snapshot(cx))
11503            .into_iter()
11504            .map(|selection| selection.range())
11505            .collect();
11506
11507        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11508        self.restore_hunks_in_ranges(selections, window, cx);
11509
11510        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11511        let wrap_around = !all_diff_hunks_expanded;
11512        let snapshot = self.snapshot(window, cx);
11513        let position = self
11514            .selections
11515            .newest::<Point>(&snapshot.display_snapshot)
11516            .head();
11517
11518        self.go_to_hunk_before_or_after_position(
11519            &snapshot,
11520            position,
11521            Direction::Next,
11522            wrap_around,
11523            window,
11524            cx,
11525        );
11526    }
11527
11528    pub fn restore_hunks_in_ranges(
11529        &mut self,
11530        ranges: Vec<Range<Point>>,
11531        window: &mut Window,
11532        cx: &mut Context<Editor>,
11533    ) {
11534        if self.delegate_stage_and_restore {
11535            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11536            if !hunks.is_empty() {
11537                cx.emit(EditorEvent::RestoreRequested { hunks });
11538            }
11539            return;
11540        }
11541        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11542        self.transact(window, cx, |editor, window, cx| {
11543            editor.restore_diff_hunks(hunks, cx);
11544            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11545                selections.refresh()
11546            });
11547        });
11548    }
11549
11550    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11551        let mut revert_changes = HashMap::default();
11552        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11553        for (buffer_id, hunks) in &chunk_by {
11554            let hunks = hunks.collect::<Vec<_>>();
11555            for hunk in &hunks {
11556                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11557            }
11558            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11559        }
11560        if !revert_changes.is_empty() {
11561            self.buffer().update(cx, |multi_buffer, cx| {
11562                for (buffer_id, changes) in revert_changes {
11563                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11564                        buffer.update(cx, |buffer, cx| {
11565                            buffer.edit(
11566                                changes
11567                                    .into_iter()
11568                                    .map(|(range, text)| (range, text.to_string())),
11569                                None,
11570                                cx,
11571                            );
11572                        });
11573                    }
11574                }
11575            });
11576        }
11577    }
11578
11579    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11580        if let Some(status) = self
11581            .addons
11582            .iter()
11583            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11584        {
11585            return Some(status);
11586        }
11587        self.project
11588            .as_ref()?
11589            .read(cx)
11590            .status_for_buffer_id(buffer_id, cx)
11591    }
11592
11593    pub fn open_active_item_in_terminal(
11594        &mut self,
11595        _: &OpenInTerminal,
11596        window: &mut Window,
11597        cx: &mut Context<Self>,
11598    ) {
11599        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11600            let project_path = buffer.read(cx).project_path(cx)?;
11601            let project = self.project()?.read(cx);
11602            let entry = project.entry_for_path(&project_path, cx)?;
11603            let parent = match &entry.canonical_path {
11604                Some(canonical_path) => canonical_path.to_path_buf(),
11605                None => project.absolute_path(&project_path, cx)?,
11606            }
11607            .parent()?
11608            .to_path_buf();
11609            Some(parent)
11610        }) {
11611            window.dispatch_action(
11612                OpenTerminal {
11613                    working_directory,
11614                    local: false,
11615                }
11616                .boxed_clone(),
11617                cx,
11618            );
11619        }
11620    }
11621
11622    fn set_breakpoint_context_menu(
11623        &mut self,
11624        display_row: DisplayRow,
11625        position: Option<Anchor>,
11626        clicked_point: gpui::Point<Pixels>,
11627        window: &mut Window,
11628        cx: &mut Context<Self>,
11629    ) {
11630        let source = self
11631            .buffer
11632            .read(cx)
11633            .snapshot(cx)
11634            .anchor_before(Point::new(display_row.0, 0u32));
11635
11636        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11637
11638        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11639            self,
11640            source,
11641            clicked_point,
11642            context_menu,
11643            window,
11644            cx,
11645        );
11646    }
11647
11648    fn add_edit_breakpoint_block(
11649        &mut self,
11650        anchor: Anchor,
11651        breakpoint: &Breakpoint,
11652        edit_action: BreakpointPromptEditAction,
11653        window: &mut Window,
11654        cx: &mut Context<Self>,
11655    ) {
11656        let weak_editor = cx.weak_entity();
11657        let bp_prompt = cx.new(|cx| {
11658            BreakpointPromptEditor::new(
11659                weak_editor,
11660                anchor,
11661                breakpoint.clone(),
11662                edit_action,
11663                window,
11664                cx,
11665            )
11666        });
11667
11668        let height = bp_prompt.update(cx, |this, cx| {
11669            this.prompt
11670                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11671        });
11672        let cloned_prompt = bp_prompt.clone();
11673        let blocks = vec![BlockProperties {
11674            style: BlockStyle::Sticky,
11675            placement: BlockPlacement::Above(anchor),
11676            height: Some(height),
11677            render: Arc::new(move |cx| {
11678                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11679                cloned_prompt.clone().into_any_element()
11680            }),
11681            priority: 0,
11682        }];
11683
11684        let focus_handle = bp_prompt.focus_handle(cx);
11685        window.focus(&focus_handle, cx);
11686
11687        let block_ids = self.insert_blocks(blocks, None, cx);
11688        bp_prompt.update(cx, |prompt, _| {
11689            prompt.add_block_ids(block_ids);
11690        });
11691    }
11692
11693    pub(crate) fn breakpoint_at_row(
11694        &self,
11695        row: u32,
11696        window: &mut Window,
11697        cx: &mut Context<Self>,
11698    ) -> Option<(Anchor, Breakpoint)> {
11699        let snapshot = self.snapshot(window, cx);
11700        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11701
11702        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11703    }
11704
11705    pub(crate) fn breakpoint_at_anchor(
11706        &self,
11707        breakpoint_position: Anchor,
11708        snapshot: &EditorSnapshot,
11709        cx: &mut Context<Self>,
11710    ) -> Option<(Anchor, Breakpoint)> {
11711        let buffer = self
11712            .buffer
11713            .read(cx)
11714            .buffer_for_anchor(breakpoint_position, cx)?;
11715
11716        let enclosing_excerpt = breakpoint_position.excerpt_id;
11717        let buffer_snapshot = buffer.read(cx).snapshot();
11718
11719        let row = buffer_snapshot
11720            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11721            .row;
11722
11723        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11724        let anchor_end = snapshot
11725            .buffer_snapshot()
11726            .anchor_after(Point::new(row, line_len));
11727
11728        self.breakpoint_store
11729            .as_ref()?
11730            .read_with(cx, |breakpoint_store, cx| {
11731                breakpoint_store
11732                    .breakpoints(
11733                        &buffer,
11734                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11735                        &buffer_snapshot,
11736                        cx,
11737                    )
11738                    .next()
11739                    .and_then(|(bp, _)| {
11740                        let breakpoint_row = buffer_snapshot
11741                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11742                            .row;
11743
11744                        if breakpoint_row == row {
11745                            snapshot
11746                                .buffer_snapshot()
11747                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11748                                .map(|position| (position, bp.bp.clone()))
11749                        } else {
11750                            None
11751                        }
11752                    })
11753            })
11754    }
11755
11756    pub fn edit_log_breakpoint(
11757        &mut self,
11758        _: &EditLogBreakpoint,
11759        window: &mut Window,
11760        cx: &mut Context<Self>,
11761    ) {
11762        if self.breakpoint_store.is_none() {
11763            return;
11764        }
11765
11766        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11767            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11768                message: None,
11769                state: BreakpointState::Enabled,
11770                condition: None,
11771                hit_condition: None,
11772            });
11773
11774            self.add_edit_breakpoint_block(
11775                anchor,
11776                &breakpoint,
11777                BreakpointPromptEditAction::Log,
11778                window,
11779                cx,
11780            );
11781        }
11782    }
11783
11784    fn breakpoints_at_cursors(
11785        &self,
11786        window: &mut Window,
11787        cx: &mut Context<Self>,
11788    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11789        let snapshot = self.snapshot(window, cx);
11790        let cursors = self
11791            .selections
11792            .disjoint_anchors_arc()
11793            .iter()
11794            .map(|selection| {
11795                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11796
11797                let breakpoint_position = self
11798                    .breakpoint_at_row(cursor_position.row, window, cx)
11799                    .map(|bp| bp.0)
11800                    .unwrap_or_else(|| {
11801                        snapshot
11802                            .display_snapshot
11803                            .buffer_snapshot()
11804                            .anchor_after(Point::new(cursor_position.row, 0))
11805                    });
11806
11807                let breakpoint = self
11808                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11809                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11810
11811                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11812            })
11813            // 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.
11814            .collect::<HashMap<Anchor, _>>();
11815
11816        cursors.into_iter().collect()
11817    }
11818
11819    pub fn enable_breakpoint(
11820        &mut self,
11821        _: &crate::actions::EnableBreakpoint,
11822        window: &mut Window,
11823        cx: &mut Context<Self>,
11824    ) {
11825        if self.breakpoint_store.is_none() {
11826            return;
11827        }
11828
11829        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11830            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11831                continue;
11832            };
11833            self.edit_breakpoint_at_anchor(
11834                anchor,
11835                breakpoint,
11836                BreakpointEditAction::InvertState,
11837                cx,
11838            );
11839        }
11840    }
11841
11842    pub fn disable_breakpoint(
11843        &mut self,
11844        _: &crate::actions::DisableBreakpoint,
11845        window: &mut Window,
11846        cx: &mut Context<Self>,
11847    ) {
11848        if self.breakpoint_store.is_none() {
11849            return;
11850        }
11851
11852        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11853            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11854                continue;
11855            };
11856            self.edit_breakpoint_at_anchor(
11857                anchor,
11858                breakpoint,
11859                BreakpointEditAction::InvertState,
11860                cx,
11861            );
11862        }
11863    }
11864
11865    pub fn toggle_breakpoint(
11866        &mut self,
11867        _: &crate::actions::ToggleBreakpoint,
11868        window: &mut Window,
11869        cx: &mut Context<Self>,
11870    ) {
11871        if self.breakpoint_store.is_none() {
11872            return;
11873        }
11874
11875        let snapshot = self.snapshot(window, cx);
11876        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11877            if self.gutter_breakpoint_indicator.0.is_some() {
11878                let display_row = anchor
11879                    .to_point(snapshot.buffer_snapshot())
11880                    .to_display_point(&snapshot.display_snapshot)
11881                    .row();
11882                self.update_breakpoint_collision_on_toggle(
11883                    display_row,
11884                    &BreakpointEditAction::Toggle,
11885                );
11886            }
11887
11888            if let Some(breakpoint) = breakpoint {
11889                self.edit_breakpoint_at_anchor(
11890                    anchor,
11891                    breakpoint,
11892                    BreakpointEditAction::Toggle,
11893                    cx,
11894                );
11895            } else {
11896                self.edit_breakpoint_at_anchor(
11897                    anchor,
11898                    Breakpoint::new_standard(),
11899                    BreakpointEditAction::Toggle,
11900                    cx,
11901                );
11902            }
11903        }
11904    }
11905
11906    fn update_breakpoint_collision_on_toggle(
11907        &mut self,
11908        display_row: DisplayRow,
11909        edit_action: &BreakpointEditAction,
11910    ) {
11911        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
11912            if breakpoint_indicator.display_row == display_row
11913                && matches!(edit_action, BreakpointEditAction::Toggle)
11914            {
11915                breakpoint_indicator.collides_with_existing_breakpoint =
11916                    !breakpoint_indicator.collides_with_existing_breakpoint;
11917            }
11918        }
11919    }
11920
11921    pub fn edit_breakpoint_at_anchor(
11922        &mut self,
11923        breakpoint_position: Anchor,
11924        breakpoint: Breakpoint,
11925        edit_action: BreakpointEditAction,
11926        cx: &mut Context<Self>,
11927    ) {
11928        let Some(breakpoint_store) = &self.breakpoint_store else {
11929            return;
11930        };
11931
11932        let Some(buffer) = self
11933            .buffer
11934            .read(cx)
11935            .buffer_for_anchor(breakpoint_position, cx)
11936        else {
11937            return;
11938        };
11939
11940        breakpoint_store.update(cx, |breakpoint_store, cx| {
11941            breakpoint_store.toggle_breakpoint(
11942                buffer,
11943                BreakpointWithPosition {
11944                    position: breakpoint_position.text_anchor,
11945                    bp: breakpoint,
11946                },
11947                edit_action,
11948                cx,
11949            );
11950        });
11951
11952        cx.notify();
11953    }
11954
11955    #[cfg(any(test, feature = "test-support"))]
11956    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
11957        self.breakpoint_store.clone()
11958    }
11959
11960    pub fn prepare_restore_change(
11961        &self,
11962        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11963        hunk: &MultiBufferDiffHunk,
11964        cx: &mut App,
11965    ) -> Option<()> {
11966        if hunk.is_created_file() {
11967            return None;
11968        }
11969        let buffer = self.buffer.read(cx);
11970        let diff = buffer.diff_for(hunk.buffer_id)?;
11971        let buffer = buffer.buffer(hunk.buffer_id)?;
11972        let buffer = buffer.read(cx);
11973        let original_text = diff
11974            .read(cx)
11975            .base_text(cx)
11976            .as_rope()
11977            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
11978        let buffer_snapshot = buffer.snapshot();
11979        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
11980        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
11981            probe
11982                .0
11983                .start
11984                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
11985                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
11986        }) {
11987            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
11988            Some(())
11989        } else {
11990            None
11991        }
11992    }
11993
11994    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
11995        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
11996    }
11997
11998    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
11999        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12000    }
12001
12002    pub fn rotate_selections_forward(
12003        &mut self,
12004        _: &RotateSelectionsForward,
12005        window: &mut Window,
12006        cx: &mut Context<Self>,
12007    ) {
12008        self.rotate_selections(window, cx, false)
12009    }
12010
12011    pub fn rotate_selections_backward(
12012        &mut self,
12013        _: &RotateSelectionsBackward,
12014        window: &mut Window,
12015        cx: &mut Context<Self>,
12016    ) {
12017        self.rotate_selections(window, cx, true)
12018    }
12019
12020    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12021        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12022        let display_snapshot = self.display_snapshot(cx);
12023        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12024
12025        if selections.len() < 2 {
12026            return;
12027        }
12028
12029        let (edits, new_selections) = {
12030            let buffer = self.buffer.read(cx).read(cx);
12031            let has_selections = selections.iter().any(|s| !s.is_empty());
12032            if has_selections {
12033                let mut selected_texts: Vec<String> = selections
12034                    .iter()
12035                    .map(|selection| {
12036                        buffer
12037                            .text_for_range(selection.start..selection.end)
12038                            .collect()
12039                    })
12040                    .collect();
12041
12042                if reverse {
12043                    selected_texts.rotate_left(1);
12044                } else {
12045                    selected_texts.rotate_right(1);
12046                }
12047
12048                let mut offset_delta: i64 = 0;
12049                let mut new_selections = Vec::new();
12050                let edits: Vec<_> = selections
12051                    .iter()
12052                    .zip(selected_texts.iter())
12053                    .map(|(selection, new_text)| {
12054                        let old_len = (selection.end.0 - selection.start.0) as i64;
12055                        let new_len = new_text.len() as i64;
12056                        let adjusted_start =
12057                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12058                        let adjusted_end =
12059                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12060
12061                        new_selections.push(Selection {
12062                            id: selection.id,
12063                            start: adjusted_start,
12064                            end: adjusted_end,
12065                            reversed: selection.reversed,
12066                            goal: selection.goal,
12067                        });
12068
12069                        offset_delta += new_len - old_len;
12070                        (selection.start..selection.end, new_text.clone())
12071                    })
12072                    .collect();
12073                (edits, new_selections)
12074            } else {
12075                let mut all_rows: Vec<u32> = selections
12076                    .iter()
12077                    .map(|selection| buffer.offset_to_point(selection.start).row)
12078                    .collect();
12079                all_rows.sort_unstable();
12080                all_rows.dedup();
12081
12082                if all_rows.len() < 2 {
12083                    return;
12084                }
12085
12086                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12087                    .iter()
12088                    .map(|&row| {
12089                        let start = Point::new(row, 0);
12090                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12091                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12092                    })
12093                    .collect();
12094
12095                let mut line_texts: Vec<String> = line_ranges
12096                    .iter()
12097                    .map(|range| buffer.text_for_range(range.clone()).collect())
12098                    .collect();
12099
12100                if reverse {
12101                    line_texts.rotate_left(1);
12102                } else {
12103                    line_texts.rotate_right(1);
12104                }
12105
12106                let edits = line_ranges
12107                    .iter()
12108                    .zip(line_texts.iter())
12109                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12110                    .collect();
12111
12112                let num_rows = all_rows.len();
12113                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12114                    .iter()
12115                    .enumerate()
12116                    .map(|(i, &row)| (row, i))
12117                    .collect();
12118
12119                // Compute new line start offsets after rotation (handles CRLF)
12120                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12121                let first_line_start = line_ranges[0].start.0;
12122                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12123                for text in line_texts.iter().take(num_rows - 1) {
12124                    let prev_start = *new_line_starts.last().unwrap();
12125                    new_line_starts.push(prev_start + text.len() + newline_len);
12126                }
12127
12128                let new_selections = selections
12129                    .iter()
12130                    .map(|selection| {
12131                        let point = buffer.offset_to_point(selection.start);
12132                        let old_index = row_to_index[&point.row];
12133                        let new_index = if reverse {
12134                            (old_index + num_rows - 1) % num_rows
12135                        } else {
12136                            (old_index + 1) % num_rows
12137                        };
12138                        let new_offset =
12139                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12140                        Selection {
12141                            id: selection.id,
12142                            start: new_offset,
12143                            end: new_offset,
12144                            reversed: selection.reversed,
12145                            goal: selection.goal,
12146                        }
12147                    })
12148                    .collect();
12149
12150                (edits, new_selections)
12151            }
12152        };
12153
12154        self.transact(window, cx, |this, window, cx| {
12155            this.buffer.update(cx, |buffer, cx| {
12156                buffer.edit(edits, None, cx);
12157            });
12158            this.change_selections(Default::default(), window, cx, |s| {
12159                s.select(new_selections);
12160            });
12161        });
12162    }
12163
12164    fn manipulate_lines<M>(
12165        &mut self,
12166        window: &mut Window,
12167        cx: &mut Context<Self>,
12168        mut manipulate: M,
12169    ) where
12170        M: FnMut(&str) -> LineManipulationResult,
12171    {
12172        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12173
12174        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12175        let buffer = self.buffer.read(cx).snapshot(cx);
12176
12177        let mut edits = Vec::new();
12178
12179        let selections = self.selections.all::<Point>(&display_map);
12180        let mut selections = selections.iter().peekable();
12181        let mut contiguous_row_selections = Vec::new();
12182        let mut new_selections = Vec::new();
12183        let mut added_lines = 0;
12184        let mut removed_lines = 0;
12185
12186        while let Some(selection) = selections.next() {
12187            let (start_row, end_row) = consume_contiguous_rows(
12188                &mut contiguous_row_selections,
12189                selection,
12190                &display_map,
12191                &mut selections,
12192            );
12193
12194            let start_point = Point::new(start_row.0, 0);
12195            let end_point = Point::new(
12196                end_row.previous_row().0,
12197                buffer.line_len(end_row.previous_row()),
12198            );
12199            let text = buffer
12200                .text_for_range(start_point..end_point)
12201                .collect::<String>();
12202
12203            let LineManipulationResult {
12204                new_text,
12205                line_count_before,
12206                line_count_after,
12207            } = manipulate(&text);
12208
12209            edits.push((start_point..end_point, new_text));
12210
12211            // Selections must change based on added and removed line count
12212            let start_row =
12213                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12214            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12215            new_selections.push(Selection {
12216                id: selection.id,
12217                start: start_row,
12218                end: end_row,
12219                goal: SelectionGoal::None,
12220                reversed: selection.reversed,
12221            });
12222
12223            if line_count_after > line_count_before {
12224                added_lines += line_count_after - line_count_before;
12225            } else if line_count_before > line_count_after {
12226                removed_lines += line_count_before - line_count_after;
12227            }
12228        }
12229
12230        self.transact(window, cx, |this, window, cx| {
12231            let buffer = this.buffer.update(cx, |buffer, cx| {
12232                buffer.edit(edits, None, cx);
12233                buffer.snapshot(cx)
12234            });
12235
12236            // Recalculate offsets on newly edited buffer
12237            let new_selections = new_selections
12238                .iter()
12239                .map(|s| {
12240                    let start_point = Point::new(s.start.0, 0);
12241                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12242                    Selection {
12243                        id: s.id,
12244                        start: buffer.point_to_offset(start_point),
12245                        end: buffer.point_to_offset(end_point),
12246                        goal: s.goal,
12247                        reversed: s.reversed,
12248                    }
12249                })
12250                .collect();
12251
12252            this.change_selections(Default::default(), window, cx, |s| {
12253                s.select(new_selections);
12254            });
12255
12256            this.request_autoscroll(Autoscroll::fit(), cx);
12257        });
12258    }
12259
12260    fn manipulate_immutable_lines<Fn>(
12261        &mut self,
12262        window: &mut Window,
12263        cx: &mut Context<Self>,
12264        mut callback: Fn,
12265    ) where
12266        Fn: FnMut(&mut Vec<&str>),
12267    {
12268        self.manipulate_lines(window, cx, |text| {
12269            let mut lines: Vec<&str> = text.split('\n').collect();
12270            let line_count_before = lines.len();
12271
12272            callback(&mut lines);
12273
12274            LineManipulationResult {
12275                new_text: lines.join("\n"),
12276                line_count_before,
12277                line_count_after: lines.len(),
12278            }
12279        });
12280    }
12281
12282    fn manipulate_mutable_lines<Fn>(
12283        &mut self,
12284        window: &mut Window,
12285        cx: &mut Context<Self>,
12286        mut callback: Fn,
12287    ) where
12288        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12289    {
12290        self.manipulate_lines(window, cx, |text| {
12291            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12292            let line_count_before = lines.len();
12293
12294            callback(&mut lines);
12295
12296            LineManipulationResult {
12297                new_text: lines.join("\n"),
12298                line_count_before,
12299                line_count_after: lines.len(),
12300            }
12301        });
12302    }
12303
12304    pub fn convert_indentation_to_spaces(
12305        &mut self,
12306        _: &ConvertIndentationToSpaces,
12307        window: &mut Window,
12308        cx: &mut Context<Self>,
12309    ) {
12310        let settings = self.buffer.read(cx).language_settings(cx);
12311        let tab_size = settings.tab_size.get() as usize;
12312
12313        self.manipulate_mutable_lines(window, cx, |lines| {
12314            // Allocates a reasonably sized scratch buffer once for the whole loop
12315            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12316            // Avoids recomputing spaces that could be inserted many times
12317            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12318                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12319                .collect();
12320
12321            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12322                let mut chars = line.as_ref().chars();
12323                let mut col = 0;
12324                let mut changed = false;
12325
12326                for ch in chars.by_ref() {
12327                    match ch {
12328                        ' ' => {
12329                            reindented_line.push(' ');
12330                            col += 1;
12331                        }
12332                        '\t' => {
12333                            // \t are converted to spaces depending on the current column
12334                            let spaces_len = tab_size - (col % tab_size);
12335                            reindented_line.extend(&space_cache[spaces_len - 1]);
12336                            col += spaces_len;
12337                            changed = true;
12338                        }
12339                        _ => {
12340                            // If we dont append before break, the character is consumed
12341                            reindented_line.push(ch);
12342                            break;
12343                        }
12344                    }
12345                }
12346
12347                if !changed {
12348                    reindented_line.clear();
12349                    continue;
12350                }
12351                // Append the rest of the line and replace old reference with new one
12352                reindented_line.extend(chars);
12353                *line = Cow::Owned(reindented_line.clone());
12354                reindented_line.clear();
12355            }
12356        });
12357    }
12358
12359    pub fn convert_indentation_to_tabs(
12360        &mut self,
12361        _: &ConvertIndentationToTabs,
12362        window: &mut Window,
12363        cx: &mut Context<Self>,
12364    ) {
12365        let settings = self.buffer.read(cx).language_settings(cx);
12366        let tab_size = settings.tab_size.get() as usize;
12367
12368        self.manipulate_mutable_lines(window, cx, |lines| {
12369            // Allocates a reasonably sized buffer once for the whole loop
12370            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12371            // Avoids recomputing spaces that could be inserted many times
12372            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12373                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12374                .collect();
12375
12376            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12377                let mut chars = line.chars();
12378                let mut spaces_count = 0;
12379                let mut first_non_indent_char = None;
12380                let mut changed = false;
12381
12382                for ch in chars.by_ref() {
12383                    match ch {
12384                        ' ' => {
12385                            // Keep track of spaces. Append \t when we reach tab_size
12386                            spaces_count += 1;
12387                            changed = true;
12388                            if spaces_count == tab_size {
12389                                reindented_line.push('\t');
12390                                spaces_count = 0;
12391                            }
12392                        }
12393                        '\t' => {
12394                            reindented_line.push('\t');
12395                            spaces_count = 0;
12396                        }
12397                        _ => {
12398                            // Dont append it yet, we might have remaining spaces
12399                            first_non_indent_char = Some(ch);
12400                            break;
12401                        }
12402                    }
12403                }
12404
12405                if !changed {
12406                    reindented_line.clear();
12407                    continue;
12408                }
12409                // Remaining spaces that didn't make a full tab stop
12410                if spaces_count > 0 {
12411                    reindented_line.extend(&space_cache[spaces_count - 1]);
12412                }
12413                // If we consume an extra character that was not indentation, add it back
12414                if let Some(extra_char) = first_non_indent_char {
12415                    reindented_line.push(extra_char);
12416                }
12417                // Append the rest of the line and replace old reference with new one
12418                reindented_line.extend(chars);
12419                *line = Cow::Owned(reindented_line.clone());
12420                reindented_line.clear();
12421            }
12422        });
12423    }
12424
12425    pub fn convert_to_upper_case(
12426        &mut self,
12427        _: &ConvertToUpperCase,
12428        window: &mut Window,
12429        cx: &mut Context<Self>,
12430    ) {
12431        self.manipulate_text(window, cx, |text| text.to_uppercase())
12432    }
12433
12434    pub fn convert_to_lower_case(
12435        &mut self,
12436        _: &ConvertToLowerCase,
12437        window: &mut Window,
12438        cx: &mut Context<Self>,
12439    ) {
12440        self.manipulate_text(window, cx, |text| text.to_lowercase())
12441    }
12442
12443    pub fn convert_to_title_case(
12444        &mut self,
12445        _: &ConvertToTitleCase,
12446        window: &mut Window,
12447        cx: &mut Context<Self>,
12448    ) {
12449        self.manipulate_text(window, cx, |text| {
12450            Self::convert_text_case(text, Case::Title)
12451        })
12452    }
12453
12454    pub fn convert_to_snake_case(
12455        &mut self,
12456        _: &ConvertToSnakeCase,
12457        window: &mut Window,
12458        cx: &mut Context<Self>,
12459    ) {
12460        self.manipulate_text(window, cx, |text| {
12461            Self::convert_text_case(text, Case::Snake)
12462        })
12463    }
12464
12465    pub fn convert_to_kebab_case(
12466        &mut self,
12467        _: &ConvertToKebabCase,
12468        window: &mut Window,
12469        cx: &mut Context<Self>,
12470    ) {
12471        self.manipulate_text(window, cx, |text| {
12472            Self::convert_text_case(text, Case::Kebab)
12473        })
12474    }
12475
12476    pub fn convert_to_upper_camel_case(
12477        &mut self,
12478        _: &ConvertToUpperCamelCase,
12479        window: &mut Window,
12480        cx: &mut Context<Self>,
12481    ) {
12482        self.manipulate_text(window, cx, |text| {
12483            Self::convert_text_case(text, Case::UpperCamel)
12484        })
12485    }
12486
12487    pub fn convert_to_lower_camel_case(
12488        &mut self,
12489        _: &ConvertToLowerCamelCase,
12490        window: &mut Window,
12491        cx: &mut Context<Self>,
12492    ) {
12493        self.manipulate_text(window, cx, |text| {
12494            Self::convert_text_case(text, Case::Camel)
12495        })
12496    }
12497
12498    pub fn convert_to_opposite_case(
12499        &mut self,
12500        _: &ConvertToOppositeCase,
12501        window: &mut Window,
12502        cx: &mut Context<Self>,
12503    ) {
12504        self.manipulate_text(window, cx, |text| {
12505            text.chars()
12506                .fold(String::with_capacity(text.len()), |mut t, c| {
12507                    if c.is_uppercase() {
12508                        t.extend(c.to_lowercase());
12509                    } else {
12510                        t.extend(c.to_uppercase());
12511                    }
12512                    t
12513                })
12514        })
12515    }
12516
12517    pub fn convert_to_sentence_case(
12518        &mut self,
12519        _: &ConvertToSentenceCase,
12520        window: &mut Window,
12521        cx: &mut Context<Self>,
12522    ) {
12523        self.manipulate_text(window, cx, |text| {
12524            Self::convert_text_case(text, Case::Sentence)
12525        })
12526    }
12527
12528    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12529        self.manipulate_text(window, cx, |text| {
12530            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12531            if has_upper_case_characters {
12532                text.to_lowercase()
12533            } else {
12534                text.to_uppercase()
12535            }
12536        })
12537    }
12538
12539    pub fn convert_to_rot13(
12540        &mut self,
12541        _: &ConvertToRot13,
12542        window: &mut Window,
12543        cx: &mut Context<Self>,
12544    ) {
12545        self.manipulate_text(window, cx, |text| {
12546            text.chars()
12547                .map(|c| match c {
12548                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12549                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12550                    _ => c,
12551                })
12552                .collect()
12553        })
12554    }
12555
12556    fn convert_text_case(text: &str, case: Case) -> String {
12557        text.lines()
12558            .map(|line| {
12559                let trimmed_start = line.trim_start();
12560                let leading = &line[..line.len() - trimmed_start.len()];
12561                let trimmed = trimmed_start.trim_end();
12562                let trailing = &trimmed_start[trimmed.len()..];
12563                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12564            })
12565            .join("\n")
12566    }
12567
12568    pub fn convert_to_rot47(
12569        &mut self,
12570        _: &ConvertToRot47,
12571        window: &mut Window,
12572        cx: &mut Context<Self>,
12573    ) {
12574        self.manipulate_text(window, cx, |text| {
12575            text.chars()
12576                .map(|c| {
12577                    let code_point = c as u32;
12578                    if code_point >= 33 && code_point <= 126 {
12579                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12580                    }
12581                    c
12582                })
12583                .collect()
12584        })
12585    }
12586
12587    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12588    where
12589        Fn: FnMut(&str) -> String,
12590    {
12591        let buffer = self.buffer.read(cx).snapshot(cx);
12592
12593        let mut new_selections = Vec::new();
12594        let mut edits = Vec::new();
12595        let mut selection_adjustment = 0isize;
12596
12597        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12598            let selection_is_empty = selection.is_empty();
12599
12600            let (start, end) = if selection_is_empty {
12601                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12602                (word_range.start, word_range.end)
12603            } else {
12604                (
12605                    buffer.point_to_offset(selection.start),
12606                    buffer.point_to_offset(selection.end),
12607                )
12608            };
12609
12610            let text = buffer.text_for_range(start..end).collect::<String>();
12611            let old_length = text.len() as isize;
12612            let text = callback(&text);
12613
12614            new_selections.push(Selection {
12615                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12616                end: MultiBufferOffset(
12617                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12618                ),
12619                goal: SelectionGoal::None,
12620                id: selection.id,
12621                reversed: selection.reversed,
12622            });
12623
12624            selection_adjustment += old_length - text.len() as isize;
12625
12626            edits.push((start..end, text));
12627        }
12628
12629        self.transact(window, cx, |this, window, cx| {
12630            this.buffer.update(cx, |buffer, cx| {
12631                buffer.edit(edits, None, cx);
12632            });
12633
12634            this.change_selections(Default::default(), window, cx, |s| {
12635                s.select(new_selections);
12636            });
12637
12638            this.request_autoscroll(Autoscroll::fit(), cx);
12639        });
12640    }
12641
12642    pub fn move_selection_on_drop(
12643        &mut self,
12644        selection: &Selection<Anchor>,
12645        target: DisplayPoint,
12646        is_cut: bool,
12647        window: &mut Window,
12648        cx: &mut Context<Self>,
12649    ) {
12650        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12651        let buffer = display_map.buffer_snapshot();
12652        let mut edits = Vec::new();
12653        let insert_point = display_map
12654            .clip_point(target, Bias::Left)
12655            .to_point(&display_map);
12656        let text = buffer
12657            .text_for_range(selection.start..selection.end)
12658            .collect::<String>();
12659        if is_cut {
12660            edits.push(((selection.start..selection.end), String::new()));
12661        }
12662        let insert_anchor = buffer.anchor_before(insert_point);
12663        edits.push(((insert_anchor..insert_anchor), text));
12664        let last_edit_start = insert_anchor.bias_left(buffer);
12665        let last_edit_end = insert_anchor.bias_right(buffer);
12666        self.transact(window, cx, |this, window, cx| {
12667            this.buffer.update(cx, |buffer, cx| {
12668                buffer.edit(edits, None, cx);
12669            });
12670            this.change_selections(Default::default(), window, cx, |s| {
12671                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12672            });
12673        });
12674    }
12675
12676    pub fn clear_selection_drag_state(&mut self) {
12677        self.selection_drag_state = SelectionDragState::None;
12678    }
12679
12680    pub fn duplicate(
12681        &mut self,
12682        upwards: bool,
12683        whole_lines: bool,
12684        window: &mut Window,
12685        cx: &mut Context<Self>,
12686    ) {
12687        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12688
12689        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12690        let buffer = display_map.buffer_snapshot();
12691        let selections = self.selections.all::<Point>(&display_map);
12692
12693        let mut edits = Vec::new();
12694        let mut selections_iter = selections.iter().peekable();
12695        while let Some(selection) = selections_iter.next() {
12696            let mut rows = selection.spanned_rows(false, &display_map);
12697            // duplicate line-wise
12698            if whole_lines || selection.start == selection.end {
12699                // Avoid duplicating the same lines twice.
12700                while let Some(next_selection) = selections_iter.peek() {
12701                    let next_rows = next_selection.spanned_rows(false, &display_map);
12702                    if next_rows.start < rows.end {
12703                        rows.end = next_rows.end;
12704                        selections_iter.next().unwrap();
12705                    } else {
12706                        break;
12707                    }
12708                }
12709
12710                // Copy the text from the selected row region and splice it either at the start
12711                // or end of the region.
12712                let start = Point::new(rows.start.0, 0);
12713                let end = Point::new(
12714                    rows.end.previous_row().0,
12715                    buffer.line_len(rows.end.previous_row()),
12716                );
12717
12718                let mut text = buffer.text_for_range(start..end).collect::<String>();
12719
12720                let insert_location = if upwards {
12721                    // When duplicating upward, we need to insert before the current line.
12722                    // If we're on the last line and it doesn't end with a newline,
12723                    // we need to add a newline before the duplicated content.
12724                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12725                        && buffer.max_point().column > 0
12726                        && !text.ends_with('\n');
12727
12728                    if needs_leading_newline {
12729                        text.insert(0, '\n');
12730                        end
12731                    } else {
12732                        text.push('\n');
12733                        Point::new(rows.start.0, 0)
12734                    }
12735                } else {
12736                    text.push('\n');
12737                    start
12738                };
12739                edits.push((insert_location..insert_location, text));
12740            } else {
12741                // duplicate character-wise
12742                let start = selection.start;
12743                let end = selection.end;
12744                let text = buffer.text_for_range(start..end).collect::<String>();
12745                edits.push((selection.end..selection.end, text));
12746            }
12747        }
12748
12749        self.transact(window, cx, |this, window, cx| {
12750            this.buffer.update(cx, |buffer, cx| {
12751                buffer.edit(edits, None, cx);
12752            });
12753
12754            // When duplicating upward with whole lines, move the cursor to the duplicated line
12755            if upwards && whole_lines {
12756                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12757
12758                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12759                    let mut new_ranges = Vec::new();
12760                    let selections = s.all::<Point>(&display_map);
12761                    let mut selections_iter = selections.iter().peekable();
12762
12763                    while let Some(first_selection) = selections_iter.next() {
12764                        // Group contiguous selections together to find the total row span
12765                        let mut group_selections = vec![first_selection];
12766                        let mut rows = first_selection.spanned_rows(false, &display_map);
12767
12768                        while let Some(next_selection) = selections_iter.peek() {
12769                            let next_rows = next_selection.spanned_rows(false, &display_map);
12770                            if next_rows.start < rows.end {
12771                                rows.end = next_rows.end;
12772                                group_selections.push(selections_iter.next().unwrap());
12773                            } else {
12774                                break;
12775                            }
12776                        }
12777
12778                        let row_count = rows.end.0 - rows.start.0;
12779
12780                        // Move all selections in this group up by the total number of duplicated rows
12781                        for selection in group_selections {
12782                            let new_start = Point::new(
12783                                selection.start.row.saturating_sub(row_count),
12784                                selection.start.column,
12785                            );
12786
12787                            let new_end = Point::new(
12788                                selection.end.row.saturating_sub(row_count),
12789                                selection.end.column,
12790                            );
12791
12792                            new_ranges.push(new_start..new_end);
12793                        }
12794                    }
12795
12796                    s.select_ranges(new_ranges);
12797                });
12798            }
12799
12800            this.request_autoscroll(Autoscroll::fit(), cx);
12801        });
12802    }
12803
12804    pub fn duplicate_line_up(
12805        &mut self,
12806        _: &DuplicateLineUp,
12807        window: &mut Window,
12808        cx: &mut Context<Self>,
12809    ) {
12810        self.duplicate(true, true, window, cx);
12811    }
12812
12813    pub fn duplicate_line_down(
12814        &mut self,
12815        _: &DuplicateLineDown,
12816        window: &mut Window,
12817        cx: &mut Context<Self>,
12818    ) {
12819        self.duplicate(false, true, window, cx);
12820    }
12821
12822    pub fn duplicate_selection(
12823        &mut self,
12824        _: &DuplicateSelection,
12825        window: &mut Window,
12826        cx: &mut Context<Self>,
12827    ) {
12828        self.duplicate(false, false, window, cx);
12829    }
12830
12831    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12832        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12833        if self.mode.is_single_line() {
12834            cx.propagate();
12835            return;
12836        }
12837
12838        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12839        let buffer = self.buffer.read(cx).snapshot(cx);
12840
12841        let mut edits = Vec::new();
12842        let mut unfold_ranges = Vec::new();
12843        let mut refold_creases = Vec::new();
12844
12845        let selections = self.selections.all::<Point>(&display_map);
12846        let mut selections = selections.iter().peekable();
12847        let mut contiguous_row_selections = Vec::new();
12848        let mut new_selections = Vec::new();
12849
12850        while let Some(selection) = selections.next() {
12851            // Find all the selections that span a contiguous row range
12852            let (start_row, end_row) = consume_contiguous_rows(
12853                &mut contiguous_row_selections,
12854                selection,
12855                &display_map,
12856                &mut selections,
12857            );
12858
12859            // Move the text spanned by the row range to be before the line preceding the row range
12860            if start_row.0 > 0 {
12861                let range_to_move = Point::new(
12862                    start_row.previous_row().0,
12863                    buffer.line_len(start_row.previous_row()),
12864                )
12865                    ..Point::new(
12866                        end_row.previous_row().0,
12867                        buffer.line_len(end_row.previous_row()),
12868                    );
12869                let insertion_point = display_map
12870                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12871                    .0;
12872
12873                // Don't move lines across excerpts
12874                if buffer
12875                    .excerpt_containing(insertion_point..range_to_move.end)
12876                    .is_some()
12877                {
12878                    let text = buffer
12879                        .text_for_range(range_to_move.clone())
12880                        .flat_map(|s| s.chars())
12881                        .skip(1)
12882                        .chain(['\n'])
12883                        .collect::<String>();
12884
12885                    edits.push((
12886                        buffer.anchor_after(range_to_move.start)
12887                            ..buffer.anchor_before(range_to_move.end),
12888                        String::new(),
12889                    ));
12890                    let insertion_anchor = buffer.anchor_after(insertion_point);
12891                    edits.push((insertion_anchor..insertion_anchor, text));
12892
12893                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12894
12895                    // Move selections up
12896                    new_selections.extend(contiguous_row_selections.drain(..).map(
12897                        |mut selection| {
12898                            selection.start.row -= row_delta;
12899                            selection.end.row -= row_delta;
12900                            selection
12901                        },
12902                    ));
12903
12904                    // Move folds up
12905                    unfold_ranges.push(range_to_move.clone());
12906                    for fold in display_map.folds_in_range(
12907                        buffer.anchor_before(range_to_move.start)
12908                            ..buffer.anchor_after(range_to_move.end),
12909                    ) {
12910                        let mut start = fold.range.start.to_point(&buffer);
12911                        let mut end = fold.range.end.to_point(&buffer);
12912                        start.row -= row_delta;
12913                        end.row -= row_delta;
12914                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12915                    }
12916                }
12917            }
12918
12919            // If we didn't move line(s), preserve the existing selections
12920            new_selections.append(&mut contiguous_row_selections);
12921        }
12922
12923        self.transact(window, cx, |this, window, cx| {
12924            this.unfold_ranges(&unfold_ranges, true, true, cx);
12925            this.buffer.update(cx, |buffer, cx| {
12926                for (range, text) in edits {
12927                    buffer.edit([(range, text)], None, cx);
12928                }
12929            });
12930            this.fold_creases(refold_creases, true, window, cx);
12931            this.change_selections(Default::default(), window, cx, |s| {
12932                s.select(new_selections);
12933            })
12934        });
12935    }
12936
12937    pub fn move_line_down(
12938        &mut self,
12939        _: &MoveLineDown,
12940        window: &mut Window,
12941        cx: &mut Context<Self>,
12942    ) {
12943        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12944        if self.mode.is_single_line() {
12945            cx.propagate();
12946            return;
12947        }
12948
12949        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12950        let buffer = self.buffer.read(cx).snapshot(cx);
12951
12952        let mut edits = Vec::new();
12953        let mut unfold_ranges = Vec::new();
12954        let mut refold_creases = Vec::new();
12955
12956        let selections = self.selections.all::<Point>(&display_map);
12957        let mut selections = selections.iter().peekable();
12958        let mut contiguous_row_selections = Vec::new();
12959        let mut new_selections = Vec::new();
12960
12961        while let Some(selection) = selections.next() {
12962            // Find all the selections that span a contiguous row range
12963            let (start_row, end_row) = consume_contiguous_rows(
12964                &mut contiguous_row_selections,
12965                selection,
12966                &display_map,
12967                &mut selections,
12968            );
12969
12970            // Move the text spanned by the row range to be after the last line of the row range
12971            if end_row.0 <= buffer.max_point().row {
12972                let range_to_move =
12973                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
12974                let insertion_point = display_map
12975                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
12976                    .0;
12977
12978                // Don't move lines across excerpt boundaries
12979                if buffer
12980                    .excerpt_containing(range_to_move.start..insertion_point)
12981                    .is_some()
12982                {
12983                    let mut text = String::from("\n");
12984                    text.extend(buffer.text_for_range(range_to_move.clone()));
12985                    text.pop(); // Drop trailing newline
12986                    edits.push((
12987                        buffer.anchor_after(range_to_move.start)
12988                            ..buffer.anchor_before(range_to_move.end),
12989                        String::new(),
12990                    ));
12991                    let insertion_anchor = buffer.anchor_after(insertion_point);
12992                    edits.push((insertion_anchor..insertion_anchor, text));
12993
12994                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
12995
12996                    // Move selections down
12997                    new_selections.extend(contiguous_row_selections.drain(..).map(
12998                        |mut selection| {
12999                            selection.start.row += row_delta;
13000                            selection.end.row += row_delta;
13001                            selection
13002                        },
13003                    ));
13004
13005                    // Move folds down
13006                    unfold_ranges.push(range_to_move.clone());
13007                    for fold in display_map.folds_in_range(
13008                        buffer.anchor_before(range_to_move.start)
13009                            ..buffer.anchor_after(range_to_move.end),
13010                    ) {
13011                        let mut start = fold.range.start.to_point(&buffer);
13012                        let mut end = fold.range.end.to_point(&buffer);
13013                        start.row += row_delta;
13014                        end.row += row_delta;
13015                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13016                    }
13017                }
13018            }
13019
13020            // If we didn't move line(s), preserve the existing selections
13021            new_selections.append(&mut contiguous_row_selections);
13022        }
13023
13024        self.transact(window, cx, |this, window, cx| {
13025            this.unfold_ranges(&unfold_ranges, true, true, cx);
13026            this.buffer.update(cx, |buffer, cx| {
13027                for (range, text) in edits {
13028                    buffer.edit([(range, text)], None, cx);
13029                }
13030            });
13031            this.fold_creases(refold_creases, true, window, cx);
13032            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13033        });
13034    }
13035
13036    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13037        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13038        let text_layout_details = &self.text_layout_details(window, cx);
13039        self.transact(window, cx, |this, window, cx| {
13040            let edits = this.change_selections(Default::default(), window, cx, |s| {
13041                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13042                s.move_with(&mut |display_map, selection| {
13043                    if !selection.is_empty() {
13044                        return;
13045                    }
13046
13047                    let mut head = selection.head();
13048                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13049                    if head.column() == display_map.line_len(head.row()) {
13050                        transpose_offset = display_map
13051                            .buffer_snapshot()
13052                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13053                    }
13054
13055                    if transpose_offset == MultiBufferOffset(0) {
13056                        return;
13057                    }
13058
13059                    *head.column_mut() += 1;
13060                    head = display_map.clip_point(head, Bias::Right);
13061                    let goal = SelectionGoal::HorizontalPosition(
13062                        display_map
13063                            .x_for_display_point(head, text_layout_details)
13064                            .into(),
13065                    );
13066                    selection.collapse_to(head, goal);
13067
13068                    let transpose_start = display_map
13069                        .buffer_snapshot()
13070                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13071                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13072                        let transpose_end = display_map
13073                            .buffer_snapshot()
13074                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13075                        if let Some(ch) = display_map
13076                            .buffer_snapshot()
13077                            .chars_at(transpose_start)
13078                            .next()
13079                        {
13080                            edits.push((transpose_start..transpose_offset, String::new()));
13081                            edits.push((transpose_end..transpose_end, ch.to_string()));
13082                        }
13083                    }
13084                });
13085                edits
13086            });
13087            this.buffer
13088                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13089            let selections = this
13090                .selections
13091                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13092            this.change_selections(Default::default(), window, cx, |s| {
13093                s.select(selections);
13094            });
13095        });
13096    }
13097
13098    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13099        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13100        if self.mode.is_single_line() {
13101            cx.propagate();
13102            return;
13103        }
13104
13105        self.rewrap_impl(RewrapOptions::default(), cx)
13106    }
13107
13108    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13109        let buffer = self.buffer.read(cx).snapshot(cx);
13110        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13111
13112        #[derive(Clone, Debug, PartialEq)]
13113        enum CommentFormat {
13114            /// single line comment, with prefix for line
13115            Line(String),
13116            /// single line within a block comment, with prefix for line
13117            BlockLine(String),
13118            /// a single line of a block comment that includes the initial delimiter
13119            BlockCommentWithStart(BlockCommentConfig),
13120            /// a single line of a block comment that includes the ending delimiter
13121            BlockCommentWithEnd(BlockCommentConfig),
13122        }
13123
13124        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13125        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13126            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13127                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13128                .peekable();
13129
13130            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13131                row
13132            } else {
13133                return Vec::new();
13134            };
13135
13136            let language_settings = buffer.language_settings_at(selection.head(), cx);
13137            let language_scope = buffer.language_scope_at(selection.head());
13138
13139            let indent_and_prefix_for_row =
13140                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13141                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13142                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13143                        &language_scope
13144                    {
13145                        let indent_end = Point::new(row, indent.len);
13146                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13147                        let line_text_after_indent = buffer
13148                            .text_for_range(indent_end..line_end)
13149                            .collect::<String>();
13150
13151                        let is_within_comment_override = buffer
13152                            .language_scope_at(indent_end)
13153                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13154                        let comment_delimiters = if is_within_comment_override {
13155                            // we are within a comment syntax node, but we don't
13156                            // yet know what kind of comment: block, doc or line
13157                            match (
13158                                language_scope.documentation_comment(),
13159                                language_scope.block_comment(),
13160                            ) {
13161                                (Some(config), _) | (_, Some(config))
13162                                    if buffer.contains_str_at(indent_end, &config.start) =>
13163                                {
13164                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13165                                }
13166                                (Some(config), _) | (_, Some(config))
13167                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13168                                {
13169                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13170                                }
13171                                (Some(config), _) | (_, Some(config))
13172                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13173                                {
13174                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13175                                }
13176                                (_, _) => language_scope
13177                                    .line_comment_prefixes()
13178                                    .iter()
13179                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13180                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13181                            }
13182                        } else {
13183                            // we not in an overridden comment node, but we may
13184                            // be within a non-overridden line comment node
13185                            language_scope
13186                                .line_comment_prefixes()
13187                                .iter()
13188                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13189                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13190                        };
13191
13192                        let rewrap_prefix = language_scope
13193                            .rewrap_prefixes()
13194                            .iter()
13195                            .find_map(|prefix_regex| {
13196                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13197                                    if mat.start() == 0 {
13198                                        Some(mat.as_str().to_string())
13199                                    } else {
13200                                        None
13201                                    }
13202                                })
13203                            })
13204                            .flatten();
13205                        (comment_delimiters, rewrap_prefix)
13206                    } else {
13207                        (None, None)
13208                    };
13209                    (indent, comment_prefix, rewrap_prefix)
13210                };
13211
13212            let mut ranges = Vec::new();
13213            let from_empty_selection = selection.is_empty();
13214
13215            let mut current_range_start = first_row;
13216            let mut prev_row = first_row;
13217            let (
13218                mut current_range_indent,
13219                mut current_range_comment_delimiters,
13220                mut current_range_rewrap_prefix,
13221            ) = indent_and_prefix_for_row(first_row);
13222
13223            for row in non_blank_rows_iter.skip(1) {
13224                let has_paragraph_break = row > prev_row + 1;
13225
13226                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13227                    indent_and_prefix_for_row(row);
13228
13229                let has_indent_change = row_indent != current_range_indent;
13230                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13231
13232                let has_boundary_change = has_comment_change
13233                    || row_rewrap_prefix.is_some()
13234                    || (has_indent_change && current_range_comment_delimiters.is_some());
13235
13236                if has_paragraph_break || has_boundary_change {
13237                    ranges.push((
13238                        language_settings.clone(),
13239                        Point::new(current_range_start, 0)
13240                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13241                        current_range_indent,
13242                        current_range_comment_delimiters.clone(),
13243                        current_range_rewrap_prefix.clone(),
13244                        from_empty_selection,
13245                    ));
13246                    current_range_start = row;
13247                    current_range_indent = row_indent;
13248                    current_range_comment_delimiters = row_comment_delimiters;
13249                    current_range_rewrap_prefix = row_rewrap_prefix;
13250                }
13251                prev_row = row;
13252            }
13253
13254            ranges.push((
13255                language_settings.clone(),
13256                Point::new(current_range_start, 0)
13257                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13258                current_range_indent,
13259                current_range_comment_delimiters,
13260                current_range_rewrap_prefix,
13261                from_empty_selection,
13262            ));
13263
13264            ranges
13265        });
13266
13267        let mut edits = Vec::new();
13268        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13269
13270        for (
13271            language_settings,
13272            wrap_range,
13273            mut indent_size,
13274            comment_prefix,
13275            rewrap_prefix,
13276            from_empty_selection,
13277        ) in wrap_ranges
13278        {
13279            let mut start_row = wrap_range.start.row;
13280            let mut end_row = wrap_range.end.row;
13281
13282            // Skip selections that overlap with a range that has already been rewrapped.
13283            let selection_range = start_row..end_row;
13284            if rewrapped_row_ranges
13285                .iter()
13286                .any(|range| range.overlaps(&selection_range))
13287            {
13288                continue;
13289            }
13290
13291            let tab_size = language_settings.tab_size;
13292
13293            let (line_prefix, inside_comment) = match &comment_prefix {
13294                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13295                    (Some(prefix.as_str()), true)
13296                }
13297                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13298                    (Some(prefix.as_ref()), true)
13299                }
13300                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13301                    start: _,
13302                    end: _,
13303                    prefix,
13304                    tab_size,
13305                })) => {
13306                    indent_size.len += tab_size;
13307                    (Some(prefix.as_ref()), true)
13308                }
13309                None => (None, false),
13310            };
13311            let indent_prefix = indent_size.chars().collect::<String>();
13312            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13313
13314            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13315                RewrapBehavior::InComments => inside_comment,
13316                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13317                RewrapBehavior::Anywhere => true,
13318            };
13319
13320            let should_rewrap = options.override_language_settings
13321                || allow_rewrap_based_on_language
13322                || self.hard_wrap.is_some();
13323            if !should_rewrap {
13324                continue;
13325            }
13326
13327            if from_empty_selection {
13328                'expand_upwards: while start_row > 0 {
13329                    let prev_row = start_row - 1;
13330                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13331                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13332                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13333                    {
13334                        start_row = prev_row;
13335                    } else {
13336                        break 'expand_upwards;
13337                    }
13338                }
13339
13340                'expand_downwards: while end_row < buffer.max_point().row {
13341                    let next_row = end_row + 1;
13342                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13343                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13344                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13345                    {
13346                        end_row = next_row;
13347                    } else {
13348                        break 'expand_downwards;
13349                    }
13350                }
13351            }
13352
13353            let start = Point::new(start_row, 0);
13354            let start_offset = ToOffset::to_offset(&start, &buffer);
13355            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13356            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13357            let mut first_line_delimiter = None;
13358            let mut last_line_delimiter = None;
13359            let Some(lines_without_prefixes) = selection_text
13360                .lines()
13361                .enumerate()
13362                .map(|(ix, line)| {
13363                    let line_trimmed = line.trim_start();
13364                    if rewrap_prefix.is_some() && ix > 0 {
13365                        Ok(line_trimmed)
13366                    } else if let Some(
13367                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13368                            start,
13369                            prefix,
13370                            end,
13371                            tab_size,
13372                        })
13373                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13374                            start,
13375                            prefix,
13376                            end,
13377                            tab_size,
13378                        }),
13379                    ) = &comment_prefix
13380                    {
13381                        let line_trimmed = line_trimmed
13382                            .strip_prefix(start.as_ref())
13383                            .map(|s| {
13384                                let mut indent_size = indent_size;
13385                                indent_size.len -= tab_size;
13386                                let indent_prefix: String = indent_size.chars().collect();
13387                                first_line_delimiter = Some((indent_prefix, start));
13388                                s.trim_start()
13389                            })
13390                            .unwrap_or(line_trimmed);
13391                        let line_trimmed = line_trimmed
13392                            .strip_suffix(end.as_ref())
13393                            .map(|s| {
13394                                last_line_delimiter = Some(end);
13395                                s.trim_end()
13396                            })
13397                            .unwrap_or(line_trimmed);
13398                        let line_trimmed = line_trimmed
13399                            .strip_prefix(prefix.as_ref())
13400                            .unwrap_or(line_trimmed);
13401                        Ok(line_trimmed)
13402                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13403                        line_trimmed.strip_prefix(prefix).with_context(|| {
13404                            format!("line did not start with prefix {prefix:?}: {line:?}")
13405                        })
13406                    } else {
13407                        line_trimmed
13408                            .strip_prefix(&line_prefix.trim_start())
13409                            .with_context(|| {
13410                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13411                            })
13412                    }
13413                })
13414                .collect::<Result<Vec<_>, _>>()
13415                .log_err()
13416            else {
13417                continue;
13418            };
13419
13420            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13421                buffer
13422                    .language_settings_at(Point::new(start_row, 0), cx)
13423                    .preferred_line_length as usize
13424            });
13425
13426            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13427                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13428            } else {
13429                line_prefix.clone()
13430            };
13431
13432            let wrapped_text = {
13433                let mut wrapped_text = wrap_with_prefix(
13434                    line_prefix,
13435                    subsequent_lines_prefix,
13436                    lines_without_prefixes.join("\n"),
13437                    wrap_column,
13438                    tab_size,
13439                    options.preserve_existing_whitespace,
13440                );
13441
13442                if let Some((indent, delimiter)) = first_line_delimiter {
13443                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13444                }
13445                if let Some(last_line) = last_line_delimiter {
13446                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13447                }
13448
13449                wrapped_text
13450            };
13451
13452            // TODO: should always use char-based diff while still supporting cursor behavior that
13453            // matches vim.
13454            let mut diff_options = DiffOptions::default();
13455            if options.override_language_settings {
13456                diff_options.max_word_diff_len = 0;
13457                diff_options.max_word_diff_line_count = 0;
13458            } else {
13459                diff_options.max_word_diff_len = usize::MAX;
13460                diff_options.max_word_diff_line_count = usize::MAX;
13461            }
13462
13463            for (old_range, new_text) in
13464                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13465            {
13466                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13467                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13468                edits.push((edit_start..edit_end, new_text));
13469            }
13470
13471            rewrapped_row_ranges.push(start_row..=end_row);
13472        }
13473
13474        self.buffer
13475            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13476    }
13477
13478    pub fn cut_common(
13479        &mut self,
13480        cut_no_selection_line: bool,
13481        window: &mut Window,
13482        cx: &mut Context<Self>,
13483    ) -> ClipboardItem {
13484        let mut text = String::new();
13485        let buffer = self.buffer.read(cx).snapshot(cx);
13486        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13487        let mut clipboard_selections = Vec::with_capacity(selections.len());
13488        {
13489            let max_point = buffer.max_point();
13490            let mut is_first = true;
13491            let mut prev_selection_was_entire_line = false;
13492            for selection in &mut selections {
13493                let is_entire_line =
13494                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13495                if is_entire_line {
13496                    selection.start = Point::new(selection.start.row, 0);
13497                    if !selection.is_empty() && selection.end.column == 0 {
13498                        selection.end = cmp::min(max_point, selection.end);
13499                    } else {
13500                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13501                    }
13502                    selection.goal = SelectionGoal::None;
13503                }
13504                if is_first {
13505                    is_first = false;
13506                } else if !prev_selection_was_entire_line {
13507                    text += "\n";
13508                }
13509                prev_selection_was_entire_line = is_entire_line;
13510                let mut len = 0;
13511                for chunk in buffer.text_for_range(selection.start..selection.end) {
13512                    text.push_str(chunk);
13513                    len += chunk.len();
13514                }
13515
13516                clipboard_selections.push(ClipboardSelection::for_buffer(
13517                    len,
13518                    is_entire_line,
13519                    selection.range(),
13520                    &buffer,
13521                    self.project.as_ref(),
13522                    cx,
13523                ));
13524            }
13525        }
13526
13527        self.transact(window, cx, |this, window, cx| {
13528            this.change_selections(Default::default(), window, cx, |s| {
13529                s.select(selections);
13530            });
13531            this.insert("", window, cx);
13532        });
13533        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13534    }
13535
13536    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13537        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13538        let item = self.cut_common(true, window, cx);
13539        cx.write_to_clipboard(item);
13540    }
13541
13542    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13543        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13544        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13545            s.move_with(&mut |snapshot, sel| {
13546                if sel.is_empty() {
13547                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13548                }
13549                if sel.is_empty() {
13550                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13551                }
13552            });
13553        });
13554        let item = self.cut_common(false, window, cx);
13555        cx.set_global(KillRing(item))
13556    }
13557
13558    pub fn kill_ring_yank(
13559        &mut self,
13560        _: &KillRingYank,
13561        window: &mut Window,
13562        cx: &mut Context<Self>,
13563    ) {
13564        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13565        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13566            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13567                (kill_ring.text().to_string(), kill_ring.metadata_json())
13568            } else {
13569                return;
13570            }
13571        } else {
13572            return;
13573        };
13574        self.do_paste(&text, metadata, false, window, cx);
13575    }
13576
13577    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13578        self.do_copy(true, cx);
13579    }
13580
13581    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13582        self.do_copy(false, cx);
13583    }
13584
13585    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13586        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13587        let buffer = self.buffer.read(cx).read(cx);
13588        let mut text = String::new();
13589        let mut clipboard_selections = Vec::with_capacity(selections.len());
13590
13591        let max_point = buffer.max_point();
13592        let mut is_first = true;
13593        for selection in &selections {
13594            let mut start = selection.start;
13595            let mut end = selection.end;
13596            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13597            let mut add_trailing_newline = false;
13598            if is_entire_line {
13599                start = Point::new(start.row, 0);
13600                let next_line_start = Point::new(end.row + 1, 0);
13601                if next_line_start <= max_point {
13602                    end = next_line_start;
13603                } else {
13604                    // We're on the last line without a trailing newline.
13605                    // Copy to the end of the line and add a newline afterwards.
13606                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13607                    add_trailing_newline = true;
13608                }
13609            }
13610
13611            let mut trimmed_selections = Vec::new();
13612            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13613                let row = MultiBufferRow(start.row);
13614                let first_indent = buffer.indent_size_for_line(row);
13615                if first_indent.len == 0 || start.column > first_indent.len {
13616                    trimmed_selections.push(start..end);
13617                } else {
13618                    trimmed_selections.push(
13619                        Point::new(row.0, first_indent.len)
13620                            ..Point::new(row.0, buffer.line_len(row)),
13621                    );
13622                    for row in start.row + 1..=end.row {
13623                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13624                        if row == end.row {
13625                            line_len = end.column;
13626                        }
13627                        if line_len == 0 {
13628                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13629                            continue;
13630                        }
13631                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13632                        if row_indent_size.len >= first_indent.len {
13633                            trimmed_selections
13634                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13635                        } else {
13636                            trimmed_selections.clear();
13637                            trimmed_selections.push(start..end);
13638                            break;
13639                        }
13640                    }
13641                }
13642            } else {
13643                trimmed_selections.push(start..end);
13644            }
13645
13646            let is_multiline_trim = trimmed_selections.len() > 1;
13647            let mut selection_len: usize = 0;
13648            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13649
13650            for trimmed_range in trimmed_selections {
13651                if is_first {
13652                    is_first = false;
13653                } else if is_multiline_trim || !prev_selection_was_entire_line {
13654                    text.push('\n');
13655                    if is_multiline_trim {
13656                        selection_len += 1;
13657                    }
13658                }
13659                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13660                    text.push_str(chunk);
13661                    selection_len += chunk.len();
13662                }
13663                if add_trailing_newline {
13664                    text.push('\n');
13665                    selection_len += 1;
13666                }
13667            }
13668
13669            clipboard_selections.push(ClipboardSelection::for_buffer(
13670                selection_len,
13671                is_entire_line,
13672                start..end,
13673                &buffer,
13674                self.project.as_ref(),
13675                cx,
13676            ));
13677        }
13678
13679        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13680            text,
13681            clipboard_selections,
13682        ));
13683    }
13684
13685    pub fn do_paste(
13686        &mut self,
13687        text: &String,
13688        clipboard_selections: Option<Vec<ClipboardSelection>>,
13689        handle_entire_lines: bool,
13690        window: &mut Window,
13691        cx: &mut Context<Self>,
13692    ) {
13693        if self.read_only(cx) {
13694            return;
13695        }
13696
13697        let clipboard_text = Cow::Borrowed(text.as_str());
13698
13699        self.transact(window, cx, |this, window, cx| {
13700            let had_active_edit_prediction = this.has_active_edit_prediction();
13701            let display_map = this.display_snapshot(cx);
13702            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13703            let cursor_offset = this
13704                .selections
13705                .last::<MultiBufferOffset>(&display_map)
13706                .head();
13707
13708            if let Some(mut clipboard_selections) = clipboard_selections {
13709                let all_selections_were_entire_line =
13710                    clipboard_selections.iter().all(|s| s.is_entire_line);
13711                let first_selection_indent_column =
13712                    clipboard_selections.first().map(|s| s.first_line_indent);
13713                if clipboard_selections.len() != old_selections.len() {
13714                    clipboard_selections.drain(..);
13715                }
13716                let mut auto_indent_on_paste = true;
13717
13718                this.buffer.update(cx, |buffer, cx| {
13719                    let snapshot = buffer.read(cx);
13720                    auto_indent_on_paste = snapshot
13721                        .language_settings_at(cursor_offset, cx)
13722                        .auto_indent_on_paste;
13723
13724                    let mut start_offset = 0;
13725                    let mut edits = Vec::new();
13726                    let mut original_indent_columns = Vec::new();
13727                    for (ix, selection) in old_selections.iter().enumerate() {
13728                        let to_insert;
13729                        let entire_line;
13730                        let original_indent_column;
13731                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13732                            let end_offset = start_offset + clipboard_selection.len;
13733                            to_insert = &clipboard_text[start_offset..end_offset];
13734                            entire_line = clipboard_selection.is_entire_line;
13735                            start_offset = if entire_line {
13736                                end_offset
13737                            } else {
13738                                end_offset + 1
13739                            };
13740                            original_indent_column = Some(clipboard_selection.first_line_indent);
13741                        } else {
13742                            to_insert = &*clipboard_text;
13743                            entire_line = all_selections_were_entire_line;
13744                            original_indent_column = first_selection_indent_column
13745                        }
13746
13747                        let (range, to_insert) =
13748                            if selection.is_empty() && handle_entire_lines && entire_line {
13749                                // If the corresponding selection was empty when this slice of the
13750                                // clipboard text was written, then the entire line containing the
13751                                // selection was copied. If this selection is also currently empty,
13752                                // then paste the line before the current line of the buffer.
13753                                let column = selection.start.to_point(&snapshot).column as usize;
13754                                let line_start = selection.start - column;
13755                                (line_start..line_start, Cow::Borrowed(to_insert))
13756                            } else {
13757                                let language = snapshot.language_at(selection.head());
13758                                let range = selection.range();
13759                                if let Some(language) = language
13760                                    && language.name() == "Markdown"
13761                                {
13762                                    edit_for_markdown_paste(
13763                                        &snapshot,
13764                                        range,
13765                                        to_insert,
13766                                        url::Url::parse(to_insert).ok(),
13767                                    )
13768                                } else {
13769                                    (range, Cow::Borrowed(to_insert))
13770                                }
13771                            };
13772
13773                        edits.push((range, to_insert));
13774                        original_indent_columns.push(original_indent_column);
13775                    }
13776                    drop(snapshot);
13777
13778                    buffer.edit(
13779                        edits,
13780                        if auto_indent_on_paste {
13781                            Some(AutoindentMode::Block {
13782                                original_indent_columns,
13783                            })
13784                        } else {
13785                            None
13786                        },
13787                        cx,
13788                    );
13789                });
13790
13791                let selections = this
13792                    .selections
13793                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13794                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13795            } else {
13796                let url = url::Url::parse(&clipboard_text).ok();
13797
13798                let auto_indent_mode = if !clipboard_text.is_empty() {
13799                    Some(AutoindentMode::Block {
13800                        original_indent_columns: Vec::new(),
13801                    })
13802                } else {
13803                    None
13804                };
13805
13806                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13807                    let snapshot = buffer.snapshot(cx);
13808
13809                    let anchors = old_selections
13810                        .iter()
13811                        .map(|s| {
13812                            let anchor = snapshot.anchor_after(s.head());
13813                            s.map(|_| anchor)
13814                        })
13815                        .collect::<Vec<_>>();
13816
13817                    let mut edits = Vec::new();
13818
13819                    // When pasting text without metadata (e.g. copied from an
13820                    // external editor using multiple cursors) and the number of
13821                    // lines matches the number of selections, distribute one
13822                    // line per cursor instead of pasting the whole text at each.
13823                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
13824                    let distribute_lines =
13825                        old_selections.len() > 1 && lines.len() == old_selections.len();
13826
13827                    for (ix, selection) in old_selections.iter().enumerate() {
13828                        let language = snapshot.language_at(selection.head());
13829                        let range = selection.range();
13830
13831                        let text_for_cursor: &str = if distribute_lines {
13832                            lines[ix]
13833                        } else {
13834                            &clipboard_text
13835                        };
13836
13837                        let (edit_range, edit_text) = if let Some(language) = language
13838                            && language.name() == "Markdown"
13839                        {
13840                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
13841                        } else {
13842                            (range, Cow::Borrowed(text_for_cursor))
13843                        };
13844
13845                        edits.push((edit_range, edit_text));
13846                    }
13847
13848                    drop(snapshot);
13849                    buffer.edit(edits, auto_indent_mode, cx);
13850
13851                    anchors
13852                });
13853
13854                this.change_selections(Default::default(), window, cx, |s| {
13855                    s.select_anchors(selection_anchors);
13856                });
13857            }
13858
13859            //   🤔                 |    ..     | show_in_menu |
13860            // | ..                  |   true        true
13861            // | had_edit_prediction |   false       true
13862
13863            let trigger_in_words =
13864                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13865
13866            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13867        });
13868    }
13869
13870    pub fn diff_clipboard_with_selection(
13871        &mut self,
13872        _: &DiffClipboardWithSelection,
13873        window: &mut Window,
13874        cx: &mut Context<Self>,
13875    ) {
13876        let selections = self
13877            .selections
13878            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13879
13880        if selections.is_empty() {
13881            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13882            return;
13883        };
13884
13885        let clipboard_text = match cx.read_from_clipboard() {
13886            Some(item) => match item.entries().first() {
13887                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13888                _ => None,
13889            },
13890            None => None,
13891        };
13892
13893        let Some(clipboard_text) = clipboard_text else {
13894            log::warn!("Clipboard doesn't contain text.");
13895            return;
13896        };
13897
13898        window.dispatch_action(
13899            Box::new(DiffClipboardWithSelectionData {
13900                clipboard_text,
13901                editor: cx.entity(),
13902            }),
13903            cx,
13904        );
13905    }
13906
13907    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13908        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13909        if let Some(item) = cx.read_from_clipboard() {
13910            let entries = item.entries();
13911
13912            match entries.first() {
13913                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13914                // of all the pasted entries.
13915                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13916                    .do_paste(
13917                        clipboard_string.text(),
13918                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13919                        true,
13920                        window,
13921                        cx,
13922                    ),
13923                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13924            }
13925        }
13926    }
13927
13928    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13929        if self.read_only(cx) {
13930            return;
13931        }
13932
13933        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13934
13935        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13936            if let Some((selections, _)) =
13937                self.selection_history.transaction(transaction_id).cloned()
13938            {
13939                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13940                    s.select_anchors(selections.to_vec());
13941                });
13942            } else {
13943                log::error!(
13944                    "No entry in selection_history found for undo. \
13945                     This may correspond to a bug where undo does not update the selection. \
13946                     If this is occurring, please add details to \
13947                     https://github.com/zed-industries/zed/issues/22692"
13948                );
13949            }
13950            self.request_autoscroll(Autoscroll::fit(), cx);
13951            self.unmark_text(window, cx);
13952            self.refresh_edit_prediction(true, false, window, cx);
13953            cx.emit(EditorEvent::Edited { transaction_id });
13954            cx.emit(EditorEvent::TransactionUndone { transaction_id });
13955        }
13956    }
13957
13958    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
13959        if self.read_only(cx) {
13960            return;
13961        }
13962
13963        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13964
13965        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
13966            if let Some((_, Some(selections))) =
13967                self.selection_history.transaction(transaction_id).cloned()
13968            {
13969                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13970                    s.select_anchors(selections.to_vec());
13971                });
13972            } else {
13973                log::error!(
13974                    "No entry in selection_history found for redo. \
13975                     This may correspond to a bug where undo does not update the selection. \
13976                     If this is occurring, please add details to \
13977                     https://github.com/zed-industries/zed/issues/22692"
13978                );
13979            }
13980            self.request_autoscroll(Autoscroll::fit(), cx);
13981            self.unmark_text(window, cx);
13982            self.refresh_edit_prediction(true, false, window, cx);
13983            cx.emit(EditorEvent::Edited { transaction_id });
13984        }
13985    }
13986
13987    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
13988        self.buffer
13989            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
13990    }
13991
13992    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
13993        self.buffer
13994            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
13995    }
13996
13997    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
13998        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13999        self.change_selections(Default::default(), window, cx, |s| {
14000            s.move_with(&mut |map, selection| {
14001                let cursor = if selection.is_empty() {
14002                    movement::left(map, selection.start)
14003                } else {
14004                    selection.start
14005                };
14006                selection.collapse_to(cursor, SelectionGoal::None);
14007            });
14008        })
14009    }
14010
14011    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14012        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14013        self.change_selections(Default::default(), window, cx, |s| {
14014            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14015        })
14016    }
14017
14018    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14019        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14020        self.change_selections(Default::default(), window, cx, |s| {
14021            s.move_with(&mut |map, selection| {
14022                let cursor = if selection.is_empty() {
14023                    movement::right(map, selection.end)
14024                } else {
14025                    selection.end
14026                };
14027                selection.collapse_to(cursor, SelectionGoal::None)
14028            });
14029        })
14030    }
14031
14032    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14033        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14034        self.change_selections(Default::default(), window, cx, |s| {
14035            s.move_heads_with(&mut |map, head, _| {
14036                (movement::right(map, head), SelectionGoal::None)
14037            });
14038        });
14039    }
14040
14041    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14042        if self.take_rename(true, window, cx).is_some() {
14043            return;
14044        }
14045
14046        if self.mode.is_single_line() {
14047            cx.propagate();
14048            return;
14049        }
14050
14051        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14052
14053        let text_layout_details = &self.text_layout_details(window, cx);
14054        let selection_count = self.selections.count();
14055        let first_selection = self.selections.first_anchor();
14056
14057        self.change_selections(Default::default(), window, cx, |s| {
14058            s.move_with(&mut |map, selection| {
14059                if !selection.is_empty() {
14060                    selection.goal = SelectionGoal::None;
14061                }
14062                let (cursor, goal) = movement::up(
14063                    map,
14064                    selection.start,
14065                    selection.goal,
14066                    false,
14067                    text_layout_details,
14068                );
14069                selection.collapse_to(cursor, goal);
14070            });
14071        });
14072
14073        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14074        {
14075            cx.propagate();
14076        }
14077    }
14078
14079    pub fn move_up_by_lines(
14080        &mut self,
14081        action: &MoveUpByLines,
14082        window: &mut Window,
14083        cx: &mut Context<Self>,
14084    ) {
14085        if self.take_rename(true, window, cx).is_some() {
14086            return;
14087        }
14088
14089        if self.mode.is_single_line() {
14090            cx.propagate();
14091            return;
14092        }
14093
14094        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14095
14096        let text_layout_details = &self.text_layout_details(window, cx);
14097
14098        self.change_selections(Default::default(), window, cx, |s| {
14099            s.move_with(&mut |map, selection| {
14100                if !selection.is_empty() {
14101                    selection.goal = SelectionGoal::None;
14102                }
14103                let (cursor, goal) = movement::up_by_rows(
14104                    map,
14105                    selection.start,
14106                    action.lines,
14107                    selection.goal,
14108                    false,
14109                    text_layout_details,
14110                );
14111                selection.collapse_to(cursor, goal);
14112            });
14113        })
14114    }
14115
14116    pub fn move_down_by_lines(
14117        &mut self,
14118        action: &MoveDownByLines,
14119        window: &mut Window,
14120        cx: &mut Context<Self>,
14121    ) {
14122        if self.take_rename(true, window, cx).is_some() {
14123            return;
14124        }
14125
14126        if self.mode.is_single_line() {
14127            cx.propagate();
14128            return;
14129        }
14130
14131        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14132
14133        let text_layout_details = &self.text_layout_details(window, cx);
14134
14135        self.change_selections(Default::default(), window, cx, |s| {
14136            s.move_with(&mut |map, selection| {
14137                if !selection.is_empty() {
14138                    selection.goal = SelectionGoal::None;
14139                }
14140                let (cursor, goal) = movement::down_by_rows(
14141                    map,
14142                    selection.start,
14143                    action.lines,
14144                    selection.goal,
14145                    false,
14146                    text_layout_details,
14147                );
14148                selection.collapse_to(cursor, goal);
14149            });
14150        })
14151    }
14152
14153    pub fn select_down_by_lines(
14154        &mut self,
14155        action: &SelectDownByLines,
14156        window: &mut Window,
14157        cx: &mut Context<Self>,
14158    ) {
14159        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14160        let text_layout_details = &self.text_layout_details(window, cx);
14161        self.change_selections(Default::default(), window, cx, |s| {
14162            s.move_heads_with(&mut |map, head, goal| {
14163                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14164            })
14165        })
14166    }
14167
14168    pub fn select_up_by_lines(
14169        &mut self,
14170        action: &SelectUpByLines,
14171        window: &mut Window,
14172        cx: &mut Context<Self>,
14173    ) {
14174        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14175        let text_layout_details = &self.text_layout_details(window, cx);
14176        self.change_selections(Default::default(), window, cx, |s| {
14177            s.move_heads_with(&mut |map, head, goal| {
14178                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14179            })
14180        })
14181    }
14182
14183    pub fn select_page_up(
14184        &mut self,
14185        _: &SelectPageUp,
14186        window: &mut Window,
14187        cx: &mut Context<Self>,
14188    ) {
14189        let Some(row_count) = self.visible_row_count() else {
14190            return;
14191        };
14192
14193        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14194
14195        let text_layout_details = &self.text_layout_details(window, cx);
14196
14197        self.change_selections(Default::default(), window, cx, |s| {
14198            s.move_heads_with(&mut |map, head, goal| {
14199                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14200            })
14201        })
14202    }
14203
14204    pub fn move_page_up(
14205        &mut self,
14206        action: &MovePageUp,
14207        window: &mut Window,
14208        cx: &mut Context<Self>,
14209    ) {
14210        if self.take_rename(true, window, cx).is_some() {
14211            return;
14212        }
14213
14214        if self
14215            .context_menu
14216            .borrow_mut()
14217            .as_mut()
14218            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14219            .unwrap_or(false)
14220        {
14221            return;
14222        }
14223
14224        if matches!(self.mode, EditorMode::SingleLine) {
14225            cx.propagate();
14226            return;
14227        }
14228
14229        let Some(row_count) = self.visible_row_count() else {
14230            return;
14231        };
14232
14233        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14234
14235        let effects = if action.center_cursor {
14236            SelectionEffects::scroll(Autoscroll::center())
14237        } else {
14238            SelectionEffects::default()
14239        };
14240
14241        let text_layout_details = &self.text_layout_details(window, cx);
14242
14243        self.change_selections(effects, window, cx, |s| {
14244            s.move_with(&mut |map, selection| {
14245                if !selection.is_empty() {
14246                    selection.goal = SelectionGoal::None;
14247                }
14248                let (cursor, goal) = movement::up_by_rows(
14249                    map,
14250                    selection.end,
14251                    row_count,
14252                    selection.goal,
14253                    false,
14254                    text_layout_details,
14255                );
14256                selection.collapse_to(cursor, goal);
14257            });
14258        });
14259    }
14260
14261    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14262        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14263        let text_layout_details = &self.text_layout_details(window, cx);
14264        self.change_selections(Default::default(), window, cx, |s| {
14265            s.move_heads_with(&mut |map, head, goal| {
14266                movement::up(map, head, goal, false, text_layout_details)
14267            })
14268        })
14269    }
14270
14271    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14272        self.take_rename(true, window, cx);
14273
14274        if self.mode.is_single_line() {
14275            cx.propagate();
14276            return;
14277        }
14278
14279        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14280
14281        let text_layout_details = &self.text_layout_details(window, cx);
14282        let selection_count = self.selections.count();
14283        let first_selection = self.selections.first_anchor();
14284
14285        self.change_selections(Default::default(), window, cx, |s| {
14286            s.move_with(&mut |map, selection| {
14287                if !selection.is_empty() {
14288                    selection.goal = SelectionGoal::None;
14289                }
14290                let (cursor, goal) = movement::down(
14291                    map,
14292                    selection.end,
14293                    selection.goal,
14294                    false,
14295                    text_layout_details,
14296                );
14297                selection.collapse_to(cursor, goal);
14298            });
14299        });
14300
14301        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14302        {
14303            cx.propagate();
14304        }
14305    }
14306
14307    pub fn select_page_down(
14308        &mut self,
14309        _: &SelectPageDown,
14310        window: &mut Window,
14311        cx: &mut Context<Self>,
14312    ) {
14313        let Some(row_count) = self.visible_row_count() else {
14314            return;
14315        };
14316
14317        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14318
14319        let text_layout_details = &self.text_layout_details(window, cx);
14320
14321        self.change_selections(Default::default(), window, cx, |s| {
14322            s.move_heads_with(&mut |map, head, goal| {
14323                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14324            })
14325        })
14326    }
14327
14328    pub fn move_page_down(
14329        &mut self,
14330        action: &MovePageDown,
14331        window: &mut Window,
14332        cx: &mut Context<Self>,
14333    ) {
14334        if self.take_rename(true, window, cx).is_some() {
14335            return;
14336        }
14337
14338        if self
14339            .context_menu
14340            .borrow_mut()
14341            .as_mut()
14342            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14343            .unwrap_or(false)
14344        {
14345            return;
14346        }
14347
14348        if matches!(self.mode, EditorMode::SingleLine) {
14349            cx.propagate();
14350            return;
14351        }
14352
14353        let Some(row_count) = self.visible_row_count() else {
14354            return;
14355        };
14356
14357        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14358
14359        let effects = if action.center_cursor {
14360            SelectionEffects::scroll(Autoscroll::center())
14361        } else {
14362            SelectionEffects::default()
14363        };
14364
14365        let text_layout_details = &self.text_layout_details(window, cx);
14366        self.change_selections(effects, window, cx, |s| {
14367            s.move_with(&mut |map, selection| {
14368                if !selection.is_empty() {
14369                    selection.goal = SelectionGoal::None;
14370                }
14371                let (cursor, goal) = movement::down_by_rows(
14372                    map,
14373                    selection.end,
14374                    row_count,
14375                    selection.goal,
14376                    false,
14377                    text_layout_details,
14378                );
14379                selection.collapse_to(cursor, goal);
14380            });
14381        });
14382    }
14383
14384    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14385        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14386        let text_layout_details = &self.text_layout_details(window, cx);
14387        self.change_selections(Default::default(), window, cx, |s| {
14388            s.move_heads_with(&mut |map, head, goal| {
14389                movement::down(map, head, goal, false, text_layout_details)
14390            })
14391        });
14392    }
14393
14394    pub fn context_menu_first(
14395        &mut self,
14396        _: &ContextMenuFirst,
14397        window: &mut Window,
14398        cx: &mut Context<Self>,
14399    ) {
14400        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14401            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14402        }
14403    }
14404
14405    pub fn context_menu_prev(
14406        &mut self,
14407        _: &ContextMenuPrevious,
14408        window: &mut Window,
14409        cx: &mut Context<Self>,
14410    ) {
14411        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14412            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14413        }
14414    }
14415
14416    pub fn context_menu_next(
14417        &mut self,
14418        _: &ContextMenuNext,
14419        window: &mut Window,
14420        cx: &mut Context<Self>,
14421    ) {
14422        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14423            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14424        }
14425    }
14426
14427    pub fn context_menu_last(
14428        &mut self,
14429        _: &ContextMenuLast,
14430        window: &mut Window,
14431        cx: &mut Context<Self>,
14432    ) {
14433        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14434            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14435        }
14436    }
14437
14438    pub fn signature_help_prev(
14439        &mut self,
14440        _: &SignatureHelpPrevious,
14441        _: &mut Window,
14442        cx: &mut Context<Self>,
14443    ) {
14444        if let Some(popover) = self.signature_help_state.popover_mut() {
14445            if popover.current_signature == 0 {
14446                popover.current_signature = popover.signatures.len() - 1;
14447            } else {
14448                popover.current_signature -= 1;
14449            }
14450            cx.notify();
14451        }
14452    }
14453
14454    pub fn signature_help_next(
14455        &mut self,
14456        _: &SignatureHelpNext,
14457        _: &mut Window,
14458        cx: &mut Context<Self>,
14459    ) {
14460        if let Some(popover) = self.signature_help_state.popover_mut() {
14461            if popover.current_signature + 1 == popover.signatures.len() {
14462                popover.current_signature = 0;
14463            } else {
14464                popover.current_signature += 1;
14465            }
14466            cx.notify();
14467        }
14468    }
14469
14470    pub fn move_to_previous_word_start(
14471        &mut self,
14472        _: &MoveToPreviousWordStart,
14473        window: &mut Window,
14474        cx: &mut Context<Self>,
14475    ) {
14476        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14477        self.change_selections(Default::default(), window, cx, |s| {
14478            s.move_cursors_with(&mut |map, head, _| {
14479                (
14480                    movement::previous_word_start(map, head),
14481                    SelectionGoal::None,
14482                )
14483            });
14484        })
14485    }
14486
14487    pub fn move_to_previous_subword_start(
14488        &mut self,
14489        _: &MoveToPreviousSubwordStart,
14490        window: &mut Window,
14491        cx: &mut Context<Self>,
14492    ) {
14493        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14494        self.change_selections(Default::default(), window, cx, |s| {
14495            s.move_cursors_with(&mut |map, head, _| {
14496                (
14497                    movement::previous_subword_start(map, head),
14498                    SelectionGoal::None,
14499                )
14500            });
14501        })
14502    }
14503
14504    pub fn select_to_previous_word_start(
14505        &mut self,
14506        _: &SelectToPreviousWordStart,
14507        window: &mut Window,
14508        cx: &mut Context<Self>,
14509    ) {
14510        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14511        self.change_selections(Default::default(), window, cx, |s| {
14512            s.move_heads_with(&mut |map, head, _| {
14513                (
14514                    movement::previous_word_start(map, head),
14515                    SelectionGoal::None,
14516                )
14517            });
14518        })
14519    }
14520
14521    pub fn select_to_previous_subword_start(
14522        &mut self,
14523        _: &SelectToPreviousSubwordStart,
14524        window: &mut Window,
14525        cx: &mut Context<Self>,
14526    ) {
14527        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14528        self.change_selections(Default::default(), window, cx, |s| {
14529            s.move_heads_with(&mut |map, head, _| {
14530                (
14531                    movement::previous_subword_start(map, head),
14532                    SelectionGoal::None,
14533                )
14534            });
14535        })
14536    }
14537
14538    pub fn delete_to_previous_word_start(
14539        &mut self,
14540        action: &DeleteToPreviousWordStart,
14541        window: &mut Window,
14542        cx: &mut Context<Self>,
14543    ) {
14544        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14545        self.transact(window, cx, |this, window, cx| {
14546            this.select_autoclose_pair(window, cx);
14547            this.change_selections(Default::default(), window, cx, |s| {
14548                s.move_with(&mut |map, selection| {
14549                    if selection.is_empty() {
14550                        let mut cursor = if action.ignore_newlines {
14551                            movement::previous_word_start(map, selection.head())
14552                        } else {
14553                            movement::previous_word_start_or_newline(map, selection.head())
14554                        };
14555                        cursor = movement::adjust_greedy_deletion(
14556                            map,
14557                            selection.head(),
14558                            cursor,
14559                            action.ignore_brackets,
14560                        );
14561                        selection.set_head(cursor, SelectionGoal::None);
14562                    }
14563                });
14564            });
14565            this.insert("", window, cx);
14566        });
14567    }
14568
14569    pub fn delete_to_previous_subword_start(
14570        &mut self,
14571        action: &DeleteToPreviousSubwordStart,
14572        window: &mut Window,
14573        cx: &mut Context<Self>,
14574    ) {
14575        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14576        self.transact(window, cx, |this, window, cx| {
14577            this.select_autoclose_pair(window, cx);
14578            this.change_selections(Default::default(), window, cx, |s| {
14579                s.move_with(&mut |map, selection| {
14580                    if selection.is_empty() {
14581                        let mut cursor = if action.ignore_newlines {
14582                            movement::previous_subword_start(map, selection.head())
14583                        } else {
14584                            movement::previous_subword_start_or_newline(map, selection.head())
14585                        };
14586                        cursor = movement::adjust_greedy_deletion(
14587                            map,
14588                            selection.head(),
14589                            cursor,
14590                            action.ignore_brackets,
14591                        );
14592                        selection.set_head(cursor, SelectionGoal::None);
14593                    }
14594                });
14595            });
14596            this.insert("", window, cx);
14597        });
14598    }
14599
14600    pub fn move_to_next_word_end(
14601        &mut self,
14602        _: &MoveToNextWordEnd,
14603        window: &mut Window,
14604        cx: &mut Context<Self>,
14605    ) {
14606        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14607        self.change_selections(Default::default(), window, cx, |s| {
14608            s.move_cursors_with(&mut |map, head, _| {
14609                (movement::next_word_end(map, head), SelectionGoal::None)
14610            });
14611        })
14612    }
14613
14614    pub fn move_to_next_subword_end(
14615        &mut self,
14616        _: &MoveToNextSubwordEnd,
14617        window: &mut Window,
14618        cx: &mut Context<Self>,
14619    ) {
14620        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14621        self.change_selections(Default::default(), window, cx, |s| {
14622            s.move_cursors_with(&mut |map, head, _| {
14623                (movement::next_subword_end(map, head), SelectionGoal::None)
14624            });
14625        })
14626    }
14627
14628    pub fn select_to_next_word_end(
14629        &mut self,
14630        _: &SelectToNextWordEnd,
14631        window: &mut Window,
14632        cx: &mut Context<Self>,
14633    ) {
14634        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14635        self.change_selections(Default::default(), window, cx, |s| {
14636            s.move_heads_with(&mut |map, head, _| {
14637                (movement::next_word_end(map, head), SelectionGoal::None)
14638            });
14639        })
14640    }
14641
14642    pub fn select_to_next_subword_end(
14643        &mut self,
14644        _: &SelectToNextSubwordEnd,
14645        window: &mut Window,
14646        cx: &mut Context<Self>,
14647    ) {
14648        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14649        self.change_selections(Default::default(), window, cx, |s| {
14650            s.move_heads_with(&mut |map, head, _| {
14651                (movement::next_subword_end(map, head), SelectionGoal::None)
14652            });
14653        })
14654    }
14655
14656    pub fn delete_to_next_word_end(
14657        &mut self,
14658        action: &DeleteToNextWordEnd,
14659        window: &mut Window,
14660        cx: &mut Context<Self>,
14661    ) {
14662        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14663        self.transact(window, cx, |this, window, cx| {
14664            this.change_selections(Default::default(), window, cx, |s| {
14665                s.move_with(&mut |map, selection| {
14666                    if selection.is_empty() {
14667                        let mut cursor = if action.ignore_newlines {
14668                            movement::next_word_end(map, selection.head())
14669                        } else {
14670                            movement::next_word_end_or_newline(map, selection.head())
14671                        };
14672                        cursor = movement::adjust_greedy_deletion(
14673                            map,
14674                            selection.head(),
14675                            cursor,
14676                            action.ignore_brackets,
14677                        );
14678                        selection.set_head(cursor, SelectionGoal::None);
14679                    }
14680                });
14681            });
14682            this.insert("", window, cx);
14683        });
14684    }
14685
14686    pub fn delete_to_next_subword_end(
14687        &mut self,
14688        action: &DeleteToNextSubwordEnd,
14689        window: &mut Window,
14690        cx: &mut Context<Self>,
14691    ) {
14692        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14693        self.transact(window, cx, |this, window, cx| {
14694            this.change_selections(Default::default(), window, cx, |s| {
14695                s.move_with(&mut |map, selection| {
14696                    if selection.is_empty() {
14697                        let mut cursor = if action.ignore_newlines {
14698                            movement::next_subword_end(map, selection.head())
14699                        } else {
14700                            movement::next_subword_end_or_newline(map, selection.head())
14701                        };
14702                        cursor = movement::adjust_greedy_deletion(
14703                            map,
14704                            selection.head(),
14705                            cursor,
14706                            action.ignore_brackets,
14707                        );
14708                        selection.set_head(cursor, SelectionGoal::None);
14709                    }
14710                });
14711            });
14712            this.insert("", window, cx);
14713        });
14714    }
14715
14716    pub fn move_to_beginning_of_line(
14717        &mut self,
14718        action: &MoveToBeginningOfLine,
14719        window: &mut Window,
14720        cx: &mut Context<Self>,
14721    ) {
14722        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
14723        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14724        self.change_selections(Default::default(), window, cx, |s| {
14725            s.move_cursors_with(&mut |map, head, _| {
14726                (
14727                    movement::indented_line_beginning(
14728                        map,
14729                        head,
14730                        action.stop_at_soft_wraps,
14731                        stop_at_indent,
14732                    ),
14733                    SelectionGoal::None,
14734                )
14735            });
14736        })
14737    }
14738
14739    pub fn select_to_beginning_of_line(
14740        &mut self,
14741        action: &SelectToBeginningOfLine,
14742        window: &mut Window,
14743        cx: &mut Context<Self>,
14744    ) {
14745        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
14746        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14747        self.change_selections(Default::default(), window, cx, |s| {
14748            s.move_heads_with(&mut |map, head, _| {
14749                (
14750                    movement::indented_line_beginning(
14751                        map,
14752                        head,
14753                        action.stop_at_soft_wraps,
14754                        stop_at_indent,
14755                    ),
14756                    SelectionGoal::None,
14757                )
14758            });
14759        });
14760    }
14761
14762    pub fn delete_to_beginning_of_line(
14763        &mut self,
14764        action: &DeleteToBeginningOfLine,
14765        window: &mut Window,
14766        cx: &mut Context<Self>,
14767    ) {
14768        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14769        self.transact(window, cx, |this, window, cx| {
14770            this.change_selections(Default::default(), window, cx, |s| {
14771                s.move_with(&mut |_, selection| {
14772                    selection.reversed = true;
14773                });
14774            });
14775
14776            this.select_to_beginning_of_line(
14777                &SelectToBeginningOfLine {
14778                    stop_at_soft_wraps: false,
14779                    stop_at_indent: action.stop_at_indent,
14780                },
14781                window,
14782                cx,
14783            );
14784            this.backspace(&Backspace, window, cx);
14785        });
14786    }
14787
14788    pub fn move_to_end_of_line(
14789        &mut self,
14790        action: &MoveToEndOfLine,
14791        window: &mut Window,
14792        cx: &mut Context<Self>,
14793    ) {
14794        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14795        self.change_selections(Default::default(), window, cx, |s| {
14796            s.move_cursors_with(&mut |map, head, _| {
14797                (
14798                    movement::line_end(map, head, action.stop_at_soft_wraps),
14799                    SelectionGoal::None,
14800                )
14801            });
14802        })
14803    }
14804
14805    pub fn select_to_end_of_line(
14806        &mut self,
14807        action: &SelectToEndOfLine,
14808        window: &mut Window,
14809        cx: &mut Context<Self>,
14810    ) {
14811        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14812        self.change_selections(Default::default(), window, cx, |s| {
14813            s.move_heads_with(&mut |map, head, _| {
14814                (
14815                    movement::line_end(map, head, action.stop_at_soft_wraps),
14816                    SelectionGoal::None,
14817                )
14818            });
14819        })
14820    }
14821
14822    pub fn delete_to_end_of_line(
14823        &mut self,
14824        _: &DeleteToEndOfLine,
14825        window: &mut Window,
14826        cx: &mut Context<Self>,
14827    ) {
14828        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14829        self.transact(window, cx, |this, window, cx| {
14830            this.select_to_end_of_line(
14831                &SelectToEndOfLine {
14832                    stop_at_soft_wraps: false,
14833                },
14834                window,
14835                cx,
14836            );
14837            this.delete(&Delete, window, cx);
14838        });
14839    }
14840
14841    pub fn cut_to_end_of_line(
14842        &mut self,
14843        action: &CutToEndOfLine,
14844        window: &mut Window,
14845        cx: &mut Context<Self>,
14846    ) {
14847        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14848        self.transact(window, cx, |this, window, cx| {
14849            this.select_to_end_of_line(
14850                &SelectToEndOfLine {
14851                    stop_at_soft_wraps: false,
14852                },
14853                window,
14854                cx,
14855            );
14856            if !action.stop_at_newlines {
14857                this.change_selections(Default::default(), window, cx, |s| {
14858                    s.move_with(&mut |_, sel| {
14859                        if sel.is_empty() {
14860                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14861                        }
14862                    });
14863                });
14864            }
14865            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14866            let item = this.cut_common(false, window, cx);
14867            cx.write_to_clipboard(item);
14868        });
14869    }
14870
14871    pub fn move_to_start_of_paragraph(
14872        &mut self,
14873        _: &MoveToStartOfParagraph,
14874        window: &mut Window,
14875        cx: &mut Context<Self>,
14876    ) {
14877        if matches!(self.mode, EditorMode::SingleLine) {
14878            cx.propagate();
14879            return;
14880        }
14881        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14882        self.change_selections(Default::default(), window, cx, |s| {
14883            s.move_with(&mut |map, selection| {
14884                selection.collapse_to(
14885                    movement::start_of_paragraph(map, selection.head(), 1),
14886                    SelectionGoal::None,
14887                )
14888            });
14889        })
14890    }
14891
14892    pub fn move_to_end_of_paragraph(
14893        &mut self,
14894        _: &MoveToEndOfParagraph,
14895        window: &mut Window,
14896        cx: &mut Context<Self>,
14897    ) {
14898        if matches!(self.mode, EditorMode::SingleLine) {
14899            cx.propagate();
14900            return;
14901        }
14902        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14903        self.change_selections(Default::default(), window, cx, |s| {
14904            s.move_with(&mut |map, selection| {
14905                selection.collapse_to(
14906                    movement::end_of_paragraph(map, selection.head(), 1),
14907                    SelectionGoal::None,
14908                )
14909            });
14910        })
14911    }
14912
14913    pub fn select_to_start_of_paragraph(
14914        &mut self,
14915        _: &SelectToStartOfParagraph,
14916        window: &mut Window,
14917        cx: &mut Context<Self>,
14918    ) {
14919        if matches!(self.mode, EditorMode::SingleLine) {
14920            cx.propagate();
14921            return;
14922        }
14923        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14924        self.change_selections(Default::default(), window, cx, |s| {
14925            s.move_heads_with(&mut |map, head, _| {
14926                (
14927                    movement::start_of_paragraph(map, head, 1),
14928                    SelectionGoal::None,
14929                )
14930            });
14931        })
14932    }
14933
14934    pub fn select_to_end_of_paragraph(
14935        &mut self,
14936        _: &SelectToEndOfParagraph,
14937        window: &mut Window,
14938        cx: &mut Context<Self>,
14939    ) {
14940        if matches!(self.mode, EditorMode::SingleLine) {
14941            cx.propagate();
14942            return;
14943        }
14944        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14945        self.change_selections(Default::default(), window, cx, |s| {
14946            s.move_heads_with(&mut |map, head, _| {
14947                (
14948                    movement::end_of_paragraph(map, head, 1),
14949                    SelectionGoal::None,
14950                )
14951            });
14952        })
14953    }
14954
14955    pub fn move_to_start_of_excerpt(
14956        &mut self,
14957        _: &MoveToStartOfExcerpt,
14958        window: &mut Window,
14959        cx: &mut Context<Self>,
14960    ) {
14961        if matches!(self.mode, EditorMode::SingleLine) {
14962            cx.propagate();
14963            return;
14964        }
14965        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14966        self.change_selections(Default::default(), window, cx, |s| {
14967            s.move_with(&mut |map, selection| {
14968                selection.collapse_to(
14969                    movement::start_of_excerpt(
14970                        map,
14971                        selection.head(),
14972                        workspace::searchable::Direction::Prev,
14973                    ),
14974                    SelectionGoal::None,
14975                )
14976            });
14977        })
14978    }
14979
14980    pub fn move_to_start_of_next_excerpt(
14981        &mut self,
14982        _: &MoveToStartOfNextExcerpt,
14983        window: &mut Window,
14984        cx: &mut Context<Self>,
14985    ) {
14986        if matches!(self.mode, EditorMode::SingleLine) {
14987            cx.propagate();
14988            return;
14989        }
14990
14991        self.change_selections(Default::default(), window, cx, |s| {
14992            s.move_with(&mut |map, selection| {
14993                selection.collapse_to(
14994                    movement::start_of_excerpt(
14995                        map,
14996                        selection.head(),
14997                        workspace::searchable::Direction::Next,
14998                    ),
14999                    SelectionGoal::None,
15000                )
15001            });
15002        })
15003    }
15004
15005    pub fn move_to_end_of_excerpt(
15006        &mut self,
15007        _: &MoveToEndOfExcerpt,
15008        window: &mut Window,
15009        cx: &mut Context<Self>,
15010    ) {
15011        if matches!(self.mode, EditorMode::SingleLine) {
15012            cx.propagate();
15013            return;
15014        }
15015        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15016        self.change_selections(Default::default(), window, cx, |s| {
15017            s.move_with(&mut |map, selection| {
15018                selection.collapse_to(
15019                    movement::end_of_excerpt(
15020                        map,
15021                        selection.head(),
15022                        workspace::searchable::Direction::Next,
15023                    ),
15024                    SelectionGoal::None,
15025                )
15026            });
15027        })
15028    }
15029
15030    pub fn move_to_end_of_previous_excerpt(
15031        &mut self,
15032        _: &MoveToEndOfPreviousExcerpt,
15033        window: &mut Window,
15034        cx: &mut Context<Self>,
15035    ) {
15036        if matches!(self.mode, EditorMode::SingleLine) {
15037            cx.propagate();
15038            return;
15039        }
15040        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15041        self.change_selections(Default::default(), window, cx, |s| {
15042            s.move_with(&mut |map, selection| {
15043                selection.collapse_to(
15044                    movement::end_of_excerpt(
15045                        map,
15046                        selection.head(),
15047                        workspace::searchable::Direction::Prev,
15048                    ),
15049                    SelectionGoal::None,
15050                )
15051            });
15052        })
15053    }
15054
15055    pub fn select_to_start_of_excerpt(
15056        &mut self,
15057        _: &SelectToStartOfExcerpt,
15058        window: &mut Window,
15059        cx: &mut Context<Self>,
15060    ) {
15061        if matches!(self.mode, EditorMode::SingleLine) {
15062            cx.propagate();
15063            return;
15064        }
15065        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15066        self.change_selections(Default::default(), window, cx, |s| {
15067            s.move_heads_with(&mut |map, head, _| {
15068                (
15069                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15070                    SelectionGoal::None,
15071                )
15072            });
15073        })
15074    }
15075
15076    pub fn select_to_start_of_next_excerpt(
15077        &mut self,
15078        _: &SelectToStartOfNextExcerpt,
15079        window: &mut Window,
15080        cx: &mut Context<Self>,
15081    ) {
15082        if matches!(self.mode, EditorMode::SingleLine) {
15083            cx.propagate();
15084            return;
15085        }
15086        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15087        self.change_selections(Default::default(), window, cx, |s| {
15088            s.move_heads_with(&mut |map, head, _| {
15089                (
15090                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15091                    SelectionGoal::None,
15092                )
15093            });
15094        })
15095    }
15096
15097    pub fn select_to_end_of_excerpt(
15098        &mut self,
15099        _: &SelectToEndOfExcerpt,
15100        window: &mut Window,
15101        cx: &mut Context<Self>,
15102    ) {
15103        if matches!(self.mode, EditorMode::SingleLine) {
15104            cx.propagate();
15105            return;
15106        }
15107        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15108        self.change_selections(Default::default(), window, cx, |s| {
15109            s.move_heads_with(&mut |map, head, _| {
15110                (
15111                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15112                    SelectionGoal::None,
15113                )
15114            });
15115        })
15116    }
15117
15118    pub fn select_to_end_of_previous_excerpt(
15119        &mut self,
15120        _: &SelectToEndOfPreviousExcerpt,
15121        window: &mut Window,
15122        cx: &mut Context<Self>,
15123    ) {
15124        if matches!(self.mode, EditorMode::SingleLine) {
15125            cx.propagate();
15126            return;
15127        }
15128        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15129        self.change_selections(Default::default(), window, cx, |s| {
15130            s.move_heads_with(&mut |map, head, _| {
15131                (
15132                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15133                    SelectionGoal::None,
15134                )
15135            });
15136        })
15137    }
15138
15139    pub fn move_to_beginning(
15140        &mut self,
15141        _: &MoveToBeginning,
15142        window: &mut Window,
15143        cx: &mut Context<Self>,
15144    ) {
15145        if matches!(self.mode, EditorMode::SingleLine) {
15146            cx.propagate();
15147            return;
15148        }
15149        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15150        self.change_selections(Default::default(), window, cx, |s| {
15151            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15152        });
15153    }
15154
15155    pub fn select_to_beginning(
15156        &mut self,
15157        _: &SelectToBeginning,
15158        window: &mut Window,
15159        cx: &mut Context<Self>,
15160    ) {
15161        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15162        selection.set_head(Point::zero(), SelectionGoal::None);
15163        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15164        self.change_selections(Default::default(), window, cx, |s| {
15165            s.select(vec![selection]);
15166        });
15167    }
15168
15169    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15170        if matches!(self.mode, EditorMode::SingleLine) {
15171            cx.propagate();
15172            return;
15173        }
15174        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15175        let cursor = self.buffer.read(cx).read(cx).len();
15176        self.change_selections(Default::default(), window, cx, |s| {
15177            s.select_ranges(vec![cursor..cursor])
15178        });
15179    }
15180
15181    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15182        self.nav_history = nav_history;
15183    }
15184
15185    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15186        self.nav_history.as_ref()
15187    }
15188
15189    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15190        self.push_to_nav_history(
15191            self.selections.newest_anchor().head(),
15192            None,
15193            false,
15194            true,
15195            cx,
15196        );
15197    }
15198
15199    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15200        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15201        let buffer = self.buffer.read(cx).read(cx);
15202        let cursor_position = cursor_anchor.to_point(&buffer);
15203        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15204        let scroll_top_row = scroll_anchor.top_row(&buffer);
15205        drop(buffer);
15206
15207        NavigationData {
15208            cursor_anchor,
15209            cursor_position,
15210            scroll_anchor,
15211            scroll_top_row,
15212        }
15213    }
15214
15215    fn navigation_entry(
15216        &self,
15217        cursor_anchor: Anchor,
15218        cx: &mut Context<Self>,
15219    ) -> Option<NavigationEntry> {
15220        let Some(history) = self.nav_history.clone() else {
15221            return None;
15222        };
15223        let data = self.navigation_data(cursor_anchor, cx);
15224        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15225    }
15226
15227    fn push_to_nav_history(
15228        &mut self,
15229        cursor_anchor: Anchor,
15230        new_position: Option<Point>,
15231        is_deactivate: bool,
15232        always: bool,
15233        cx: &mut Context<Self>,
15234    ) {
15235        let data = self.navigation_data(cursor_anchor, cx);
15236        if let Some(nav_history) = self.nav_history.as_mut() {
15237            if let Some(new_position) = new_position {
15238                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15239                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15240                    return;
15241                }
15242            }
15243
15244            nav_history.push(Some(data), cx);
15245            cx.emit(EditorEvent::PushedToNavHistory {
15246                anchor: cursor_anchor,
15247                is_deactivate,
15248            })
15249        }
15250    }
15251
15252    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15253        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15254        let buffer = self.buffer.read(cx).snapshot(cx);
15255        let mut selection = self
15256            .selections
15257            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15258        selection.set_head(buffer.len(), SelectionGoal::None);
15259        self.change_selections(Default::default(), window, cx, |s| {
15260            s.select(vec![selection]);
15261        });
15262    }
15263
15264    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15265        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15266        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15267            s.select_ranges([Anchor::min()..Anchor::max()]);
15268        });
15269    }
15270
15271    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15272        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15273        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15274        let mut selections = self.selections.all::<Point>(&display_map);
15275        let max_point = display_map.buffer_snapshot().max_point();
15276        for selection in &mut selections {
15277            let rows = selection.spanned_rows(true, &display_map);
15278            selection.start = Point::new(rows.start.0, 0);
15279            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15280            selection.reversed = false;
15281        }
15282        self.change_selections(Default::default(), window, cx, |s| {
15283            s.select(selections);
15284        });
15285    }
15286
15287    pub fn split_selection_into_lines(
15288        &mut self,
15289        action: &SplitSelectionIntoLines,
15290        window: &mut Window,
15291        cx: &mut Context<Self>,
15292    ) {
15293        let selections = self
15294            .selections
15295            .all::<Point>(&self.display_snapshot(cx))
15296            .into_iter()
15297            .map(|selection| selection.start..selection.end)
15298            .collect::<Vec<_>>();
15299        self.unfold_ranges(&selections, true, false, cx);
15300
15301        let mut new_selection_ranges = Vec::new();
15302        {
15303            let buffer = self.buffer.read(cx).read(cx);
15304            for selection in selections {
15305                for row in selection.start.row..selection.end.row {
15306                    let line_start = Point::new(row, 0);
15307                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15308
15309                    if action.keep_selections {
15310                        // Keep the selection range for each line
15311                        let selection_start = if row == selection.start.row {
15312                            selection.start
15313                        } else {
15314                            line_start
15315                        };
15316                        new_selection_ranges.push(selection_start..line_end);
15317                    } else {
15318                        // Collapse to cursor at end of line
15319                        new_selection_ranges.push(line_end..line_end);
15320                    }
15321                }
15322
15323                let is_multiline_selection = selection.start.row != selection.end.row;
15324                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15325                // so this action feels more ergonomic when paired with other selection operations
15326                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15327                if !should_skip_last {
15328                    if action.keep_selections {
15329                        if is_multiline_selection {
15330                            let line_start = Point::new(selection.end.row, 0);
15331                            new_selection_ranges.push(line_start..selection.end);
15332                        } else {
15333                            new_selection_ranges.push(selection.start..selection.end);
15334                        }
15335                    } else {
15336                        new_selection_ranges.push(selection.end..selection.end);
15337                    }
15338                }
15339            }
15340        }
15341        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15342            s.select_ranges(new_selection_ranges);
15343        });
15344    }
15345
15346    pub fn add_selection_above(
15347        &mut self,
15348        action: &AddSelectionAbove,
15349        window: &mut Window,
15350        cx: &mut Context<Self>,
15351    ) {
15352        self.add_selection(true, action.skip_soft_wrap, window, cx);
15353    }
15354
15355    pub fn add_selection_below(
15356        &mut self,
15357        action: &AddSelectionBelow,
15358        window: &mut Window,
15359        cx: &mut Context<Self>,
15360    ) {
15361        self.add_selection(false, action.skip_soft_wrap, window, cx);
15362    }
15363
15364    fn add_selection(
15365        &mut self,
15366        above: bool,
15367        skip_soft_wrap: bool,
15368        window: &mut Window,
15369        cx: &mut Context<Self>,
15370    ) {
15371        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15372
15373        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15374        let all_selections = self.selections.all::<Point>(&display_map);
15375        let text_layout_details = self.text_layout_details(window, cx);
15376
15377        let (mut columnar_selections, new_selections_to_columnarize) = {
15378            if let Some(state) = self.add_selections_state.as_ref() {
15379                let columnar_selection_ids: HashSet<_> = state
15380                    .groups
15381                    .iter()
15382                    .flat_map(|group| group.stack.iter())
15383                    .copied()
15384                    .collect();
15385
15386                all_selections
15387                    .into_iter()
15388                    .partition(|s| columnar_selection_ids.contains(&s.id))
15389            } else {
15390                (Vec::new(), all_selections)
15391            }
15392        };
15393
15394        let mut state = self
15395            .add_selections_state
15396            .take()
15397            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15398
15399        for selection in new_selections_to_columnarize {
15400            let range = selection.display_range(&display_map).sorted();
15401            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15402            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15403            let positions = start_x.min(end_x)..start_x.max(end_x);
15404            let mut stack = Vec::new();
15405            for row in range.start.row().0..=range.end.row().0 {
15406                if let Some(selection) = self.selections.build_columnar_selection(
15407                    &display_map,
15408                    DisplayRow(row),
15409                    &positions,
15410                    selection.reversed,
15411                    &text_layout_details,
15412                ) {
15413                    stack.push(selection.id);
15414                    columnar_selections.push(selection);
15415                }
15416            }
15417            if !stack.is_empty() {
15418                if above {
15419                    stack.reverse();
15420                }
15421                state.groups.push(AddSelectionsGroup { above, stack });
15422            }
15423        }
15424
15425        let mut final_selections = Vec::new();
15426        let end_row = if above {
15427            DisplayRow(0)
15428        } else {
15429            display_map.max_point().row()
15430        };
15431
15432        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15433        // positions to place new selections, so we need to keep track of the
15434        // column range of the oldest selection in each group, because
15435        // intermediate selections may have been clamped to shorter lines.
15436        // selections may have been clamped to shorter lines.
15437        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15438            let mut map = HashMap::default();
15439            for group in state.groups.iter() {
15440                if let Some(oldest_id) = group.stack.first() {
15441                    if let Some(oldest_selection) =
15442                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15443                    {
15444                        let start_col = oldest_selection.start.column;
15445                        let end_col = oldest_selection.end.column;
15446                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15447                        for id in &group.stack {
15448                            map.insert(*id, goal_columns.clone());
15449                        }
15450                    }
15451                }
15452            }
15453            map
15454        } else {
15455            HashMap::default()
15456        };
15457
15458        let mut last_added_item_per_group = HashMap::default();
15459        for group in state.groups.iter_mut() {
15460            if let Some(last_id) = group.stack.last() {
15461                last_added_item_per_group.insert(*last_id, group);
15462            }
15463        }
15464
15465        for selection in columnar_selections {
15466            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15467                if above == group.above {
15468                    let range = selection.display_range(&display_map).sorted();
15469                    debug_assert_eq!(range.start.row(), range.end.row());
15470                    let row = range.start.row();
15471                    let positions =
15472                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15473                            Pixels::from(start)..Pixels::from(end)
15474                        } else {
15475                            let start_x =
15476                                display_map.x_for_display_point(range.start, &text_layout_details);
15477                            let end_x =
15478                                display_map.x_for_display_point(range.end, &text_layout_details);
15479                            start_x.min(end_x)..start_x.max(end_x)
15480                        };
15481
15482                    let maybe_new_selection = if skip_soft_wrap {
15483                        let goal_columns = goal_columns_by_selection_id
15484                            .remove(&selection.id)
15485                            .unwrap_or_else(|| {
15486                                let start_col = selection.start.column;
15487                                let end_col = selection.end.column;
15488                                start_col.min(end_col)..start_col.max(end_col)
15489                            });
15490                        self.selections.find_next_columnar_selection_by_buffer_row(
15491                            &display_map,
15492                            row,
15493                            end_row,
15494                            above,
15495                            &goal_columns,
15496                            selection.reversed,
15497                            &text_layout_details,
15498                        )
15499                    } else {
15500                        self.selections.find_next_columnar_selection_by_display_row(
15501                            &display_map,
15502                            row,
15503                            end_row,
15504                            above,
15505                            &positions,
15506                            selection.reversed,
15507                            &text_layout_details,
15508                        )
15509                    };
15510
15511                    if let Some(new_selection) = maybe_new_selection {
15512                        group.stack.push(new_selection.id);
15513                        if above {
15514                            final_selections.push(new_selection);
15515                            final_selections.push(selection);
15516                        } else {
15517                            final_selections.push(selection);
15518                            final_selections.push(new_selection);
15519                        }
15520                    } else {
15521                        final_selections.push(selection);
15522                    }
15523                } else {
15524                    group.stack.pop();
15525                }
15526            } else {
15527                final_selections.push(selection);
15528            }
15529        }
15530
15531        self.change_selections(Default::default(), window, cx, |s| {
15532            s.select(final_selections);
15533        });
15534
15535        let final_selection_ids: HashSet<_> = self
15536            .selections
15537            .all::<Point>(&display_map)
15538            .iter()
15539            .map(|s| s.id)
15540            .collect();
15541        state.groups.retain_mut(|group| {
15542            // selections might get merged above so we remove invalid items from stacks
15543            group.stack.retain(|id| final_selection_ids.contains(id));
15544
15545            // single selection in stack can be treated as initial state
15546            group.stack.len() > 1
15547        });
15548
15549        if !state.groups.is_empty() {
15550            self.add_selections_state = Some(state);
15551        }
15552    }
15553
15554    pub fn insert_snippet_at_selections(
15555        &mut self,
15556        action: &InsertSnippet,
15557        window: &mut Window,
15558        cx: &mut Context<Self>,
15559    ) {
15560        self.try_insert_snippet_at_selections(action, window, cx)
15561            .log_err();
15562    }
15563
15564    fn try_insert_snippet_at_selections(
15565        &mut self,
15566        action: &InsertSnippet,
15567        window: &mut Window,
15568        cx: &mut Context<Self>,
15569    ) -> Result<()> {
15570        let insertion_ranges = self
15571            .selections
15572            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15573            .into_iter()
15574            .map(|selection| selection.range())
15575            .collect_vec();
15576
15577        let snippet = if let Some(snippet_body) = &action.snippet {
15578            if action.language.is_none() && action.name.is_none() {
15579                Snippet::parse(snippet_body)?
15580            } else {
15581                bail!("`snippet` is mutually exclusive with `language` and `name`")
15582            }
15583        } else if let Some(name) = &action.name {
15584            let project = self.project().context("no project")?;
15585            let snippet_store = project.read(cx).snippets().read(cx);
15586            let snippet = snippet_store
15587                .snippets_for(action.language.clone(), cx)
15588                .into_iter()
15589                .find(|snippet| snippet.name == *name)
15590                .context("snippet not found")?;
15591            Snippet::parse(&snippet.body)?
15592        } else {
15593            // todo(andrew): open modal to select snippet
15594            bail!("`name` or `snippet` is required")
15595        };
15596
15597        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15598    }
15599
15600    fn select_match_ranges(
15601        &mut self,
15602        range: Range<MultiBufferOffset>,
15603        reversed: bool,
15604        replace_newest: bool,
15605        auto_scroll: Option<Autoscroll>,
15606        window: &mut Window,
15607        cx: &mut Context<Editor>,
15608    ) {
15609        self.unfold_ranges(
15610            std::slice::from_ref(&range),
15611            false,
15612            auto_scroll.is_some(),
15613            cx,
15614        );
15615        let effects = if let Some(scroll) = auto_scroll {
15616            SelectionEffects::scroll(scroll)
15617        } else {
15618            SelectionEffects::no_scroll()
15619        };
15620        self.change_selections(effects, window, cx, |s| {
15621            if replace_newest {
15622                s.delete(s.newest_anchor().id);
15623            }
15624            if reversed {
15625                s.insert_range(range.end..range.start);
15626            } else {
15627                s.insert_range(range);
15628            }
15629        });
15630    }
15631
15632    pub fn select_next_match_internal(
15633        &mut self,
15634        display_map: &DisplaySnapshot,
15635        replace_newest: bool,
15636        autoscroll: Option<Autoscroll>,
15637        window: &mut Window,
15638        cx: &mut Context<Self>,
15639    ) -> Result<()> {
15640        let buffer = display_map.buffer_snapshot();
15641        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15642        if let Some(mut select_next_state) = self.select_next_state.take() {
15643            let query = &select_next_state.query;
15644            if !select_next_state.done {
15645                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15646                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15647                let mut next_selected_range = None;
15648
15649                let bytes_after_last_selection =
15650                    buffer.bytes_in_range(last_selection.end..buffer.len());
15651                let bytes_before_first_selection =
15652                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15653                let query_matches = query
15654                    .stream_find_iter(bytes_after_last_selection)
15655                    .map(|result| (last_selection.end, result))
15656                    .chain(
15657                        query
15658                            .stream_find_iter(bytes_before_first_selection)
15659                            .map(|result| (MultiBufferOffset(0), result)),
15660                    );
15661
15662                for (start_offset, query_match) in query_matches {
15663                    let query_match = query_match.unwrap(); // can only fail due to I/O
15664                    let offset_range =
15665                        start_offset + query_match.start()..start_offset + query_match.end();
15666
15667                    if !select_next_state.wordwise
15668                        || (!buffer.is_inside_word(offset_range.start, None)
15669                            && !buffer.is_inside_word(offset_range.end, None))
15670                    {
15671                        let idx = selections
15672                            .partition_point(|selection| selection.end <= offset_range.start);
15673                        let overlaps = selections
15674                            .get(idx)
15675                            .map_or(false, |selection| selection.start < offset_range.end);
15676
15677                        if !overlaps {
15678                            next_selected_range = Some(offset_range);
15679                            break;
15680                        }
15681                    }
15682                }
15683
15684                if let Some(next_selected_range) = next_selected_range {
15685                    self.select_match_ranges(
15686                        next_selected_range,
15687                        last_selection.reversed,
15688                        replace_newest,
15689                        autoscroll,
15690                        window,
15691                        cx,
15692                    );
15693                } else {
15694                    select_next_state.done = true;
15695                }
15696            }
15697
15698            self.select_next_state = Some(select_next_state);
15699        } else {
15700            let mut only_carets = true;
15701            let mut same_text_selected = true;
15702            let mut selected_text = None;
15703
15704            let mut selections_iter = selections.iter().peekable();
15705            while let Some(selection) = selections_iter.next() {
15706                if selection.start != selection.end {
15707                    only_carets = false;
15708                }
15709
15710                if same_text_selected {
15711                    if selected_text.is_none() {
15712                        selected_text =
15713                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15714                    }
15715
15716                    if let Some(next_selection) = selections_iter.peek() {
15717                        if next_selection.len() == selection.len() {
15718                            let next_selected_text = buffer
15719                                .text_for_range(next_selection.range())
15720                                .collect::<String>();
15721                            if Some(next_selected_text) != selected_text {
15722                                same_text_selected = false;
15723                                selected_text = None;
15724                            }
15725                        } else {
15726                            same_text_selected = false;
15727                            selected_text = None;
15728                        }
15729                    }
15730                }
15731            }
15732
15733            if only_carets {
15734                for selection in &mut selections {
15735                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15736                    selection.start = word_range.start;
15737                    selection.end = word_range.end;
15738                    selection.goal = SelectionGoal::None;
15739                    selection.reversed = false;
15740                    self.select_match_ranges(
15741                        selection.start..selection.end,
15742                        selection.reversed,
15743                        replace_newest,
15744                        autoscroll,
15745                        window,
15746                        cx,
15747                    );
15748                }
15749
15750                if selections.len() == 1 {
15751                    let selection = selections
15752                        .last()
15753                        .expect("ensured that there's only one selection");
15754                    let query = buffer
15755                        .text_for_range(selection.start..selection.end)
15756                        .collect::<String>();
15757                    let is_empty = query.is_empty();
15758                    let select_state = SelectNextState {
15759                        query: self.build_query(&[query], cx)?,
15760                        wordwise: true,
15761                        done: is_empty,
15762                    };
15763                    self.select_next_state = Some(select_state);
15764                } else {
15765                    self.select_next_state = None;
15766                }
15767            } else if let Some(selected_text) = selected_text {
15768                self.select_next_state = Some(SelectNextState {
15769                    query: self.build_query(&[selected_text], cx)?,
15770                    wordwise: false,
15771                    done: false,
15772                });
15773                self.select_next_match_internal(
15774                    display_map,
15775                    replace_newest,
15776                    autoscroll,
15777                    window,
15778                    cx,
15779                )?;
15780            }
15781        }
15782        Ok(())
15783    }
15784
15785    pub fn select_all_matches(
15786        &mut self,
15787        _action: &SelectAllMatches,
15788        window: &mut Window,
15789        cx: &mut Context<Self>,
15790    ) -> Result<()> {
15791        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15792
15793        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15794
15795        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15796        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15797        else {
15798            return Ok(());
15799        };
15800
15801        let mut new_selections = Vec::new();
15802
15803        let reversed = self
15804            .selections
15805            .oldest::<MultiBufferOffset>(&display_map)
15806            .reversed;
15807        let buffer = display_map.buffer_snapshot();
15808        let query_matches = select_next_state
15809            .query
15810            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15811
15812        for query_match in query_matches.into_iter() {
15813            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15814            let offset_range = if reversed {
15815                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15816            } else {
15817                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15818            };
15819
15820            if !select_next_state.wordwise
15821                || (!buffer.is_inside_word(offset_range.start, None)
15822                    && !buffer.is_inside_word(offset_range.end, None))
15823            {
15824                new_selections.push(offset_range.start..offset_range.end);
15825            }
15826        }
15827
15828        select_next_state.done = true;
15829
15830        if new_selections.is_empty() {
15831            log::error!("bug: new_selections is empty in select_all_matches");
15832            return Ok(());
15833        }
15834
15835        self.unfold_ranges(&new_selections, false, false, cx);
15836        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15837            selections.select_ranges(new_selections)
15838        });
15839
15840        Ok(())
15841    }
15842
15843    pub fn select_next(
15844        &mut self,
15845        action: &SelectNext,
15846        window: &mut Window,
15847        cx: &mut Context<Self>,
15848    ) -> Result<()> {
15849        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15850        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15851        self.select_next_match_internal(
15852            &display_map,
15853            action.replace_newest,
15854            Some(Autoscroll::newest()),
15855            window,
15856            cx,
15857        )
15858    }
15859
15860    pub fn select_previous(
15861        &mut self,
15862        action: &SelectPrevious,
15863        window: &mut Window,
15864        cx: &mut Context<Self>,
15865    ) -> Result<()> {
15866        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15867        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15868        let buffer = display_map.buffer_snapshot();
15869        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15870        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15871            let query = &select_prev_state.query;
15872            if !select_prev_state.done {
15873                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15874                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15875                let mut next_selected_range = None;
15876                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15877                let bytes_before_last_selection =
15878                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15879                let bytes_after_first_selection =
15880                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15881                let query_matches = query
15882                    .stream_find_iter(bytes_before_last_selection)
15883                    .map(|result| (last_selection.start, result))
15884                    .chain(
15885                        query
15886                            .stream_find_iter(bytes_after_first_selection)
15887                            .map(|result| (buffer.len(), result)),
15888                    );
15889                for (end_offset, query_match) in query_matches {
15890                    let query_match = query_match.unwrap(); // can only fail due to I/O
15891                    let offset_range =
15892                        end_offset - query_match.end()..end_offset - query_match.start();
15893
15894                    if !select_prev_state.wordwise
15895                        || (!buffer.is_inside_word(offset_range.start, None)
15896                            && !buffer.is_inside_word(offset_range.end, None))
15897                    {
15898                        next_selected_range = Some(offset_range);
15899                        break;
15900                    }
15901                }
15902
15903                if let Some(next_selected_range) = next_selected_range {
15904                    self.select_match_ranges(
15905                        next_selected_range,
15906                        last_selection.reversed,
15907                        action.replace_newest,
15908                        Some(Autoscroll::newest()),
15909                        window,
15910                        cx,
15911                    );
15912                } else {
15913                    select_prev_state.done = true;
15914                }
15915            }
15916
15917            self.select_prev_state = Some(select_prev_state);
15918        } else {
15919            let mut only_carets = true;
15920            let mut same_text_selected = true;
15921            let mut selected_text = None;
15922
15923            let mut selections_iter = selections.iter().peekable();
15924            while let Some(selection) = selections_iter.next() {
15925                if selection.start != selection.end {
15926                    only_carets = false;
15927                }
15928
15929                if same_text_selected {
15930                    if selected_text.is_none() {
15931                        selected_text =
15932                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15933                    }
15934
15935                    if let Some(next_selection) = selections_iter.peek() {
15936                        if next_selection.len() == selection.len() {
15937                            let next_selected_text = buffer
15938                                .text_for_range(next_selection.range())
15939                                .collect::<String>();
15940                            if Some(next_selected_text) != selected_text {
15941                                same_text_selected = false;
15942                                selected_text = None;
15943                            }
15944                        } else {
15945                            same_text_selected = false;
15946                            selected_text = None;
15947                        }
15948                    }
15949                }
15950            }
15951
15952            if only_carets {
15953                for selection in &mut selections {
15954                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15955                    selection.start = word_range.start;
15956                    selection.end = word_range.end;
15957                    selection.goal = SelectionGoal::None;
15958                    selection.reversed = false;
15959                    self.select_match_ranges(
15960                        selection.start..selection.end,
15961                        selection.reversed,
15962                        action.replace_newest,
15963                        Some(Autoscroll::newest()),
15964                        window,
15965                        cx,
15966                    );
15967                }
15968                if selections.len() == 1 {
15969                    let selection = selections
15970                        .last()
15971                        .expect("ensured that there's only one selection");
15972                    let query = buffer
15973                        .text_for_range(selection.start..selection.end)
15974                        .collect::<String>();
15975                    let is_empty = query.is_empty();
15976                    let select_state = SelectNextState {
15977                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
15978                        wordwise: true,
15979                        done: is_empty,
15980                    };
15981                    self.select_prev_state = Some(select_state);
15982                } else {
15983                    self.select_prev_state = None;
15984                }
15985            } else if let Some(selected_text) = selected_text {
15986                self.select_prev_state = Some(SelectNextState {
15987                    query: self
15988                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
15989                    wordwise: false,
15990                    done: false,
15991                });
15992                self.select_previous(action, window, cx)?;
15993            }
15994        }
15995        Ok(())
15996    }
15997
15998    /// Builds an `AhoCorasick` automaton from the provided patterns, while
15999    /// setting the case sensitivity based on the global
16000    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16001    /// editor's settings.
16002    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16003    where
16004        I: IntoIterator<Item = P>,
16005        P: AsRef<[u8]>,
16006    {
16007        let case_sensitive = self
16008            .select_next_is_case_sensitive
16009            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16010
16011        let mut builder = AhoCorasickBuilder::new();
16012        builder.ascii_case_insensitive(!case_sensitive);
16013        builder.build(patterns)
16014    }
16015
16016    pub fn find_next_match(
16017        &mut self,
16018        _: &FindNextMatch,
16019        window: &mut Window,
16020        cx: &mut Context<Self>,
16021    ) -> Result<()> {
16022        let selections = self.selections.disjoint_anchors_arc();
16023        match selections.first() {
16024            Some(first) if selections.len() >= 2 => {
16025                self.change_selections(Default::default(), window, cx, |s| {
16026                    s.select_ranges([first.range()]);
16027                });
16028            }
16029            _ => self.select_next(
16030                &SelectNext {
16031                    replace_newest: true,
16032                },
16033                window,
16034                cx,
16035            )?,
16036        }
16037        Ok(())
16038    }
16039
16040    pub fn find_previous_match(
16041        &mut self,
16042        _: &FindPreviousMatch,
16043        window: &mut Window,
16044        cx: &mut Context<Self>,
16045    ) -> Result<()> {
16046        let selections = self.selections.disjoint_anchors_arc();
16047        match selections.last() {
16048            Some(last) if selections.len() >= 2 => {
16049                self.change_selections(Default::default(), window, cx, |s| {
16050                    s.select_ranges([last.range()]);
16051                });
16052            }
16053            _ => self.select_previous(
16054                &SelectPrevious {
16055                    replace_newest: true,
16056                },
16057                window,
16058                cx,
16059            )?,
16060        }
16061        Ok(())
16062    }
16063
16064    pub fn toggle_comments(
16065        &mut self,
16066        action: &ToggleComments,
16067        window: &mut Window,
16068        cx: &mut Context<Self>,
16069    ) {
16070        if self.read_only(cx) {
16071            return;
16072        }
16073        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16074        let text_layout_details = &self.text_layout_details(window, cx);
16075        self.transact(window, cx, |this, window, cx| {
16076            let mut selections = this
16077                .selections
16078                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16079            let mut edits = Vec::new();
16080            let mut selection_edit_ranges = Vec::new();
16081            let mut last_toggled_row = None;
16082            let snapshot = this.buffer.read(cx).read(cx);
16083            let empty_str: Arc<str> = Arc::default();
16084            let mut suffixes_inserted = Vec::new();
16085            let ignore_indent = action.ignore_indent;
16086
16087            fn comment_prefix_range(
16088                snapshot: &MultiBufferSnapshot,
16089                row: MultiBufferRow,
16090                comment_prefix: &str,
16091                comment_prefix_whitespace: &str,
16092                ignore_indent: bool,
16093            ) -> Range<Point> {
16094                let indent_size = if ignore_indent {
16095                    0
16096                } else {
16097                    snapshot.indent_size_for_line(row).len
16098                };
16099
16100                let start = Point::new(row.0, indent_size);
16101
16102                let mut line_bytes = snapshot
16103                    .bytes_in_range(start..snapshot.max_point())
16104                    .flatten()
16105                    .copied();
16106
16107                // If this line currently begins with the line comment prefix, then record
16108                // the range containing the prefix.
16109                if line_bytes
16110                    .by_ref()
16111                    .take(comment_prefix.len())
16112                    .eq(comment_prefix.bytes())
16113                {
16114                    // Include any whitespace that matches the comment prefix.
16115                    let matching_whitespace_len = line_bytes
16116                        .zip(comment_prefix_whitespace.bytes())
16117                        .take_while(|(a, b)| a == b)
16118                        .count() as u32;
16119                    let end = Point::new(
16120                        start.row,
16121                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16122                    );
16123                    start..end
16124                } else {
16125                    start..start
16126                }
16127            }
16128
16129            fn comment_suffix_range(
16130                snapshot: &MultiBufferSnapshot,
16131                row: MultiBufferRow,
16132                comment_suffix: &str,
16133                comment_suffix_has_leading_space: bool,
16134            ) -> Range<Point> {
16135                let end = Point::new(row.0, snapshot.line_len(row));
16136                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16137
16138                let mut line_end_bytes = snapshot
16139                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16140                    .flatten()
16141                    .copied();
16142
16143                let leading_space_len = if suffix_start_column > 0
16144                    && line_end_bytes.next() == Some(b' ')
16145                    && comment_suffix_has_leading_space
16146                {
16147                    1
16148                } else {
16149                    0
16150                };
16151
16152                // If this line currently begins with the line comment prefix, then record
16153                // the range containing the prefix.
16154                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16155                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16156                    start..end
16157                } else {
16158                    end..end
16159                }
16160            }
16161
16162            // TODO: Handle selections that cross excerpts
16163            for selection in &mut selections {
16164                let start_column = snapshot
16165                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16166                    .len;
16167                let language = if let Some(language) =
16168                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16169                {
16170                    language
16171                } else {
16172                    continue;
16173                };
16174
16175                selection_edit_ranges.clear();
16176
16177                // If multiple selections contain a given row, avoid processing that
16178                // row more than once.
16179                let mut start_row = MultiBufferRow(selection.start.row);
16180                if last_toggled_row == Some(start_row) {
16181                    start_row = start_row.next_row();
16182                }
16183                let end_row =
16184                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16185                        MultiBufferRow(selection.end.row - 1)
16186                    } else {
16187                        MultiBufferRow(selection.end.row)
16188                    };
16189                last_toggled_row = Some(end_row);
16190
16191                if start_row > end_row {
16192                    continue;
16193                }
16194
16195                // If the language has line comments, toggle those.
16196                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16197
16198                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16199                if ignore_indent {
16200                    full_comment_prefixes = full_comment_prefixes
16201                        .into_iter()
16202                        .map(|s| Arc::from(s.trim_end()))
16203                        .collect();
16204                }
16205
16206                if !full_comment_prefixes.is_empty() {
16207                    let first_prefix = full_comment_prefixes
16208                        .first()
16209                        .expect("prefixes is non-empty");
16210                    let prefix_trimmed_lengths = full_comment_prefixes
16211                        .iter()
16212                        .map(|p| p.trim_end_matches(' ').len())
16213                        .collect::<SmallVec<[usize; 4]>>();
16214
16215                    let mut all_selection_lines_are_comments = true;
16216
16217                    for row in start_row.0..=end_row.0 {
16218                        let row = MultiBufferRow(row);
16219                        if start_row < end_row && snapshot.is_line_blank(row) {
16220                            continue;
16221                        }
16222
16223                        let prefix_range = full_comment_prefixes
16224                            .iter()
16225                            .zip(prefix_trimmed_lengths.iter().copied())
16226                            .map(|(prefix, trimmed_prefix_len)| {
16227                                comment_prefix_range(
16228                                    snapshot.deref(),
16229                                    row,
16230                                    &prefix[..trimmed_prefix_len],
16231                                    &prefix[trimmed_prefix_len..],
16232                                    ignore_indent,
16233                                )
16234                            })
16235                            .max_by_key(|range| range.end.column - range.start.column)
16236                            .expect("prefixes is non-empty");
16237
16238                        if prefix_range.is_empty() {
16239                            all_selection_lines_are_comments = false;
16240                        }
16241
16242                        selection_edit_ranges.push(prefix_range);
16243                    }
16244
16245                    if all_selection_lines_are_comments {
16246                        edits.extend(
16247                            selection_edit_ranges
16248                                .iter()
16249                                .cloned()
16250                                .map(|range| (range, empty_str.clone())),
16251                        );
16252                    } else {
16253                        let min_column = selection_edit_ranges
16254                            .iter()
16255                            .map(|range| range.start.column)
16256                            .min()
16257                            .unwrap_or(0);
16258                        edits.extend(selection_edit_ranges.iter().map(|range| {
16259                            let position = Point::new(range.start.row, min_column);
16260                            (position..position, first_prefix.clone())
16261                        }));
16262                    }
16263                } else if let Some(BlockCommentConfig {
16264                    start: full_comment_prefix,
16265                    end: comment_suffix,
16266                    ..
16267                }) = language.block_comment()
16268                {
16269                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16270                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16271                    let prefix_range = comment_prefix_range(
16272                        snapshot.deref(),
16273                        start_row,
16274                        comment_prefix,
16275                        comment_prefix_whitespace,
16276                        ignore_indent,
16277                    );
16278                    let suffix_range = comment_suffix_range(
16279                        snapshot.deref(),
16280                        end_row,
16281                        comment_suffix.trim_start_matches(' '),
16282                        comment_suffix.starts_with(' '),
16283                    );
16284
16285                    if prefix_range.is_empty() || suffix_range.is_empty() {
16286                        edits.push((
16287                            prefix_range.start..prefix_range.start,
16288                            full_comment_prefix.clone(),
16289                        ));
16290                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16291                        suffixes_inserted.push((end_row, comment_suffix.len()));
16292                    } else {
16293                        edits.push((prefix_range, empty_str.clone()));
16294                        edits.push((suffix_range, empty_str.clone()));
16295                    }
16296                } else {
16297                    continue;
16298                }
16299            }
16300
16301            drop(snapshot);
16302            this.buffer.update(cx, |buffer, cx| {
16303                buffer.edit(edits, None, cx);
16304            });
16305
16306            // Adjust selections so that they end before any comment suffixes that
16307            // were inserted.
16308            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16309            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16310            let snapshot = this.buffer.read(cx).read(cx);
16311            for selection in &mut selections {
16312                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16313                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16314                        Ordering::Less => {
16315                            suffixes_inserted.next();
16316                            continue;
16317                        }
16318                        Ordering::Greater => break,
16319                        Ordering::Equal => {
16320                            if selection.end.column == snapshot.line_len(row) {
16321                                if selection.is_empty() {
16322                                    selection.start.column -= suffix_len as u32;
16323                                }
16324                                selection.end.column -= suffix_len as u32;
16325                            }
16326                            break;
16327                        }
16328                    }
16329                }
16330            }
16331
16332            drop(snapshot);
16333            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16334
16335            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16336            let selections_on_single_row = selections.windows(2).all(|selections| {
16337                selections[0].start.row == selections[1].start.row
16338                    && selections[0].end.row == selections[1].end.row
16339                    && selections[0].start.row == selections[0].end.row
16340            });
16341            let selections_selecting = selections
16342                .iter()
16343                .any(|selection| selection.start != selection.end);
16344            let advance_downwards = action.advance_downwards
16345                && selections_on_single_row
16346                && !selections_selecting
16347                && !matches!(this.mode, EditorMode::SingleLine);
16348
16349            if advance_downwards {
16350                let snapshot = this.buffer.read(cx).snapshot(cx);
16351
16352                this.change_selections(Default::default(), window, cx, |s| {
16353                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16354                        let mut point = display_point.to_point(display_snapshot);
16355                        point.row += 1;
16356                        point = snapshot.clip_point(point, Bias::Left);
16357                        let display_point = point.to_display_point(display_snapshot);
16358                        let goal = SelectionGoal::HorizontalPosition(
16359                            display_snapshot
16360                                .x_for_display_point(display_point, text_layout_details)
16361                                .into(),
16362                        );
16363                        (display_point, goal)
16364                    })
16365                });
16366            }
16367        });
16368    }
16369
16370    pub fn select_enclosing_symbol(
16371        &mut self,
16372        _: &SelectEnclosingSymbol,
16373        window: &mut Window,
16374        cx: &mut Context<Self>,
16375    ) {
16376        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16377
16378        let buffer = self.buffer.read(cx).snapshot(cx);
16379        let old_selections = self
16380            .selections
16381            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16382            .into_boxed_slice();
16383
16384        fn update_selection(
16385            selection: &Selection<MultiBufferOffset>,
16386            buffer_snap: &MultiBufferSnapshot,
16387        ) -> Option<Selection<MultiBufferOffset>> {
16388            let cursor = selection.head();
16389            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16390            for symbol in symbols.iter().rev() {
16391                let start = symbol.range.start.to_offset(buffer_snap);
16392                let end = symbol.range.end.to_offset(buffer_snap);
16393                let new_range = start..end;
16394                if start < selection.start || end > selection.end {
16395                    return Some(Selection {
16396                        id: selection.id,
16397                        start: new_range.start,
16398                        end: new_range.end,
16399                        goal: SelectionGoal::None,
16400                        reversed: selection.reversed,
16401                    });
16402                }
16403            }
16404            None
16405        }
16406
16407        let mut selected_larger_symbol = false;
16408        let new_selections = old_selections
16409            .iter()
16410            .map(|selection| match update_selection(selection, &buffer) {
16411                Some(new_selection) => {
16412                    if new_selection.range() != selection.range() {
16413                        selected_larger_symbol = true;
16414                    }
16415                    new_selection
16416                }
16417                None => selection.clone(),
16418            })
16419            .collect::<Vec<_>>();
16420
16421        if selected_larger_symbol {
16422            self.change_selections(Default::default(), window, cx, |s| {
16423                s.select(new_selections);
16424            });
16425        }
16426    }
16427
16428    pub fn select_larger_syntax_node(
16429        &mut self,
16430        _: &SelectLargerSyntaxNode,
16431        window: &mut Window,
16432        cx: &mut Context<Self>,
16433    ) {
16434        let Some(visible_row_count) = self.visible_row_count() else {
16435            return;
16436        };
16437        let old_selections: Box<[_]> = self
16438            .selections
16439            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16440            .into();
16441        if old_selections.is_empty() {
16442            return;
16443        }
16444
16445        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16446
16447        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16448        let buffer = self.buffer.read(cx).snapshot(cx);
16449
16450        let mut selected_larger_node = false;
16451        let mut new_selections = old_selections
16452            .iter()
16453            .map(|selection| {
16454                let old_range = selection.start..selection.end;
16455
16456                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16457                    // manually select word at selection
16458                    if ["string_content", "inline"].contains(&node.kind()) {
16459                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16460                        // ignore if word is already selected
16461                        if !word_range.is_empty() && old_range != word_range {
16462                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16463                            // only select word if start and end point belongs to same word
16464                            if word_range == last_word_range {
16465                                selected_larger_node = true;
16466                                return Selection {
16467                                    id: selection.id,
16468                                    start: word_range.start,
16469                                    end: word_range.end,
16470                                    goal: SelectionGoal::None,
16471                                    reversed: selection.reversed,
16472                                };
16473                            }
16474                        }
16475                    }
16476                }
16477
16478                let mut new_range = old_range.clone();
16479                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16480                    new_range = range;
16481                    if !node.is_named() {
16482                        continue;
16483                    }
16484                    if !display_map.intersects_fold(new_range.start)
16485                        && !display_map.intersects_fold(new_range.end)
16486                    {
16487                        break;
16488                    }
16489                }
16490
16491                selected_larger_node |= new_range != old_range;
16492                Selection {
16493                    id: selection.id,
16494                    start: new_range.start,
16495                    end: new_range.end,
16496                    goal: SelectionGoal::None,
16497                    reversed: selection.reversed,
16498                }
16499            })
16500            .collect::<Vec<_>>();
16501
16502        if !selected_larger_node {
16503            return; // don't put this call in the history
16504        }
16505
16506        // scroll based on transformation done to the last selection created by the user
16507        let (last_old, last_new) = old_selections
16508            .last()
16509            .zip(new_selections.last().cloned())
16510            .expect("old_selections isn't empty");
16511
16512        let is_selection_reversed = if new_selections.len() == 1 {
16513            let should_be_reversed = last_old.start != last_new.start;
16514            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16515            should_be_reversed
16516        } else {
16517            last_new.reversed
16518        };
16519
16520        if selected_larger_node {
16521            self.select_syntax_node_history.disable_clearing = true;
16522            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16523                s.select(new_selections.clone());
16524            });
16525            self.select_syntax_node_history.disable_clearing = false;
16526        }
16527
16528        let start_row = last_new.start.to_display_point(&display_map).row().0;
16529        let end_row = last_new.end.to_display_point(&display_map).row().0;
16530        let selection_height = end_row - start_row + 1;
16531        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16532
16533        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16534        let scroll_behavior = if fits_on_the_screen {
16535            self.request_autoscroll(Autoscroll::fit(), cx);
16536            SelectSyntaxNodeScrollBehavior::FitSelection
16537        } else if is_selection_reversed {
16538            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16539            SelectSyntaxNodeScrollBehavior::CursorTop
16540        } else {
16541            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16542            SelectSyntaxNodeScrollBehavior::CursorBottom
16543        };
16544
16545        let old_selections: Box<[Selection<Anchor>]> = old_selections
16546            .iter()
16547            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16548            .collect();
16549        self.select_syntax_node_history.push((
16550            old_selections,
16551            scroll_behavior,
16552            is_selection_reversed,
16553        ));
16554    }
16555
16556    pub fn select_smaller_syntax_node(
16557        &mut self,
16558        _: &SelectSmallerSyntaxNode,
16559        window: &mut Window,
16560        cx: &mut Context<Self>,
16561    ) {
16562        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16563
16564        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16565            self.select_syntax_node_history.pop()
16566        {
16567            if let Some(selection) = selections.last_mut() {
16568                selection.reversed = is_selection_reversed;
16569            }
16570
16571            let snapshot = self.buffer.read(cx).snapshot(cx);
16572            let selections: Vec<Selection<MultiBufferOffset>> = selections
16573                .iter()
16574                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16575                .collect();
16576
16577            self.select_syntax_node_history.disable_clearing = true;
16578            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16579                s.select(selections);
16580            });
16581            self.select_syntax_node_history.disable_clearing = false;
16582
16583            match scroll_behavior {
16584                SelectSyntaxNodeScrollBehavior::CursorTop => {
16585                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16586                }
16587                SelectSyntaxNodeScrollBehavior::FitSelection => {
16588                    self.request_autoscroll(Autoscroll::fit(), cx);
16589                }
16590                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16591                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16592                }
16593            }
16594        }
16595    }
16596
16597    pub fn unwrap_syntax_node(
16598        &mut self,
16599        _: &UnwrapSyntaxNode,
16600        window: &mut Window,
16601        cx: &mut Context<Self>,
16602    ) {
16603        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16604
16605        let buffer = self.buffer.read(cx).snapshot(cx);
16606        let selections = self
16607            .selections
16608            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16609            .into_iter()
16610            // subtracting the offset requires sorting
16611            .sorted_by_key(|i| i.start);
16612
16613        let full_edits = selections
16614            .into_iter()
16615            .filter_map(|selection| {
16616                let child = if selection.is_empty()
16617                    && let Some((_, ancestor_range)) =
16618                        buffer.syntax_ancestor(selection.start..selection.end)
16619                {
16620                    ancestor_range
16621                } else {
16622                    selection.range()
16623                };
16624
16625                let mut parent = child.clone();
16626                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16627                    parent = ancestor_range;
16628                    if parent.start < child.start || parent.end > child.end {
16629                        break;
16630                    }
16631                }
16632
16633                if parent == child {
16634                    return None;
16635                }
16636                let text = buffer.text_for_range(child).collect::<String>();
16637                Some((selection.id, parent, text))
16638            })
16639            .collect::<Vec<_>>();
16640        if full_edits.is_empty() {
16641            return;
16642        }
16643
16644        self.transact(window, cx, |this, window, cx| {
16645            this.buffer.update(cx, |buffer, cx| {
16646                buffer.edit(
16647                    full_edits
16648                        .iter()
16649                        .map(|(_, p, t)| (p.clone(), t.clone()))
16650                        .collect::<Vec<_>>(),
16651                    None,
16652                    cx,
16653                );
16654            });
16655            this.change_selections(Default::default(), window, cx, |s| {
16656                let mut offset = 0;
16657                let mut selections = vec![];
16658                for (id, parent, text) in full_edits {
16659                    let start = parent.start - offset;
16660                    offset += (parent.end - parent.start) - text.len();
16661                    selections.push(Selection {
16662                        id,
16663                        start,
16664                        end: start + text.len(),
16665                        reversed: false,
16666                        goal: Default::default(),
16667                    });
16668                }
16669                s.select(selections);
16670            });
16671        });
16672    }
16673
16674    pub fn select_next_syntax_node(
16675        &mut self,
16676        _: &SelectNextSyntaxNode,
16677        window: &mut Window,
16678        cx: &mut Context<Self>,
16679    ) {
16680        let old_selections: Box<[_]> = self
16681            .selections
16682            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16683            .into();
16684        if old_selections.is_empty() {
16685            return;
16686        }
16687
16688        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16689
16690        let buffer = self.buffer.read(cx).snapshot(cx);
16691        let mut selected_sibling = false;
16692
16693        let new_selections = old_selections
16694            .iter()
16695            .map(|selection| {
16696                let old_range = selection.start..selection.end;
16697
16698                let old_range =
16699                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16700                let excerpt = buffer.excerpt_containing(old_range.clone());
16701
16702                if let Some(mut excerpt) = excerpt
16703                    && let Some(node) = excerpt
16704                        .buffer()
16705                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16706                {
16707                    let new_range = excerpt.map_range_from_buffer(
16708                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16709                    );
16710                    selected_sibling = true;
16711                    Selection {
16712                        id: selection.id,
16713                        start: new_range.start,
16714                        end: new_range.end,
16715                        goal: SelectionGoal::None,
16716                        reversed: selection.reversed,
16717                    }
16718                } else {
16719                    selection.clone()
16720                }
16721            })
16722            .collect::<Vec<_>>();
16723
16724        if selected_sibling {
16725            self.change_selections(
16726                SelectionEffects::scroll(Autoscroll::fit()),
16727                window,
16728                cx,
16729                |s| {
16730                    s.select(new_selections);
16731                },
16732            );
16733        }
16734    }
16735
16736    pub fn select_prev_syntax_node(
16737        &mut self,
16738        _: &SelectPreviousSyntaxNode,
16739        window: &mut Window,
16740        cx: &mut Context<Self>,
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 buffer = self.buffer.read(cx).snapshot(cx);
16753        let mut selected_sibling = false;
16754
16755        let new_selections = old_selections
16756            .iter()
16757            .map(|selection| {
16758                let old_range = selection.start..selection.end;
16759                let old_range =
16760                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16761                let excerpt = buffer.excerpt_containing(old_range.clone());
16762
16763                if let Some(mut excerpt) = excerpt
16764                    && let Some(node) = excerpt
16765                        .buffer()
16766                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16767                {
16768                    let new_range = excerpt.map_range_from_buffer(
16769                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16770                    );
16771                    selected_sibling = true;
16772                    Selection {
16773                        id: selection.id,
16774                        start: new_range.start,
16775                        end: new_range.end,
16776                        goal: SelectionGoal::None,
16777                        reversed: selection.reversed,
16778                    }
16779                } else {
16780                    selection.clone()
16781                }
16782            })
16783            .collect::<Vec<_>>();
16784
16785        if selected_sibling {
16786            self.change_selections(
16787                SelectionEffects::scroll(Autoscroll::fit()),
16788                window,
16789                cx,
16790                |s| {
16791                    s.select(new_selections);
16792                },
16793            );
16794        }
16795    }
16796
16797    pub fn move_to_start_of_larger_syntax_node(
16798        &mut self,
16799        _: &MoveToStartOfLargerSyntaxNode,
16800        window: &mut Window,
16801        cx: &mut Context<Self>,
16802    ) {
16803        self.move_cursors_to_syntax_nodes(window, cx, false);
16804    }
16805
16806    pub fn move_to_end_of_larger_syntax_node(
16807        &mut self,
16808        _: &MoveToEndOfLargerSyntaxNode,
16809        window: &mut Window,
16810        cx: &mut Context<Self>,
16811    ) {
16812        self.move_cursors_to_syntax_nodes(window, cx, true);
16813    }
16814
16815    fn find_syntax_node_boundary(
16816        &self,
16817        selection_pos: MultiBufferOffset,
16818        move_to_end: bool,
16819        display_map: &DisplaySnapshot,
16820        buffer: &MultiBufferSnapshot,
16821    ) -> MultiBufferOffset {
16822        let old_range = selection_pos..selection_pos;
16823        let mut new_pos = selection_pos;
16824        let mut search_range = old_range;
16825        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16826            search_range = range.clone();
16827            if !node.is_named()
16828                || display_map.intersects_fold(range.start)
16829                || display_map.intersects_fold(range.end)
16830                // If cursor is already at the end of the syntax node, continue searching
16831                || (move_to_end && range.end == selection_pos)
16832                // If cursor is already at the start of the syntax node, continue searching
16833                || (!move_to_end && range.start == selection_pos)
16834            {
16835                continue;
16836            }
16837
16838            // If we found a string_content node, find the largest parent that is still string_content
16839            // Enables us to skip to the end of strings without taking multiple steps inside the string
16840            let (_, final_range) = if node.kind() == "string_content" {
16841                let mut current_node = node;
16842                let mut current_range = range;
16843                while let Some((parent, parent_range)) =
16844                    buffer.syntax_ancestor(current_range.clone())
16845                {
16846                    if parent.kind() == "string_content" {
16847                        current_node = parent;
16848                        current_range = parent_range;
16849                    } else {
16850                        break;
16851                    }
16852                }
16853
16854                (current_node, current_range)
16855            } else {
16856                (node, range)
16857            };
16858
16859            new_pos = if move_to_end {
16860                final_range.end
16861            } else {
16862                final_range.start
16863            };
16864
16865            break;
16866        }
16867
16868        new_pos
16869    }
16870
16871    fn move_cursors_to_syntax_nodes(
16872        &mut self,
16873        window: &mut Window,
16874        cx: &mut Context<Self>,
16875        move_to_end: bool,
16876    ) -> bool {
16877        let old_selections: Box<[_]> = self
16878            .selections
16879            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16880            .into();
16881        if old_selections.is_empty() {
16882            return false;
16883        }
16884
16885        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16886
16887        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16888        let buffer = self.buffer.read(cx).snapshot(cx);
16889
16890        let mut any_cursor_moved = false;
16891        let new_selections = old_selections
16892            .iter()
16893            .map(|selection| {
16894                if !selection.is_empty() {
16895                    return selection.clone();
16896                }
16897
16898                let selection_pos = selection.head();
16899                let new_pos = self.find_syntax_node_boundary(
16900                    selection_pos,
16901                    move_to_end,
16902                    &display_map,
16903                    &buffer,
16904                );
16905
16906                any_cursor_moved |= new_pos != selection_pos;
16907
16908                Selection {
16909                    id: selection.id,
16910                    start: new_pos,
16911                    end: new_pos,
16912                    goal: SelectionGoal::None,
16913                    reversed: false,
16914                }
16915            })
16916            .collect::<Vec<_>>();
16917
16918        self.change_selections(Default::default(), window, cx, |s| {
16919            s.select(new_selections);
16920        });
16921        self.request_autoscroll(Autoscroll::newest(), cx);
16922
16923        any_cursor_moved
16924    }
16925
16926    pub fn select_to_start_of_larger_syntax_node(
16927        &mut self,
16928        _: &SelectToStartOfLargerSyntaxNode,
16929        window: &mut Window,
16930        cx: &mut Context<Self>,
16931    ) {
16932        self.select_to_syntax_nodes(window, cx, false);
16933    }
16934
16935    pub fn select_to_end_of_larger_syntax_node(
16936        &mut self,
16937        _: &SelectToEndOfLargerSyntaxNode,
16938        window: &mut Window,
16939        cx: &mut Context<Self>,
16940    ) {
16941        self.select_to_syntax_nodes(window, cx, true);
16942    }
16943
16944    fn select_to_syntax_nodes(
16945        &mut self,
16946        window: &mut Window,
16947        cx: &mut Context<Self>,
16948        move_to_end: bool,
16949    ) {
16950        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16951
16952        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16953        let buffer = self.buffer.read(cx).snapshot(cx);
16954        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
16955
16956        let new_selections = old_selections
16957            .iter()
16958            .map(|selection| {
16959                let new_pos = self.find_syntax_node_boundary(
16960                    selection.head(),
16961                    move_to_end,
16962                    &display_map,
16963                    &buffer,
16964                );
16965
16966                let mut new_selection = selection.clone();
16967                new_selection.set_head(new_pos, SelectionGoal::None);
16968                new_selection
16969            })
16970            .collect::<Vec<_>>();
16971
16972        self.change_selections(Default::default(), window, cx, |s| {
16973            s.select(new_selections);
16974        });
16975    }
16976
16977    pub fn move_to_enclosing_bracket(
16978        &mut self,
16979        _: &MoveToEnclosingBracket,
16980        window: &mut Window,
16981        cx: &mut Context<Self>,
16982    ) {
16983        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16984        self.change_selections(Default::default(), window, cx, |s| {
16985            s.move_offsets_with(&mut |snapshot, selection| {
16986                let Some(enclosing_bracket_ranges) =
16987                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
16988                else {
16989                    return;
16990                };
16991
16992                let mut best_length = usize::MAX;
16993                let mut best_inside = false;
16994                let mut best_in_bracket_range = false;
16995                let mut best_destination = None;
16996                for (open, close) in enclosing_bracket_ranges {
16997                    let close = close.to_inclusive();
16998                    let length = *close.end() - open.start;
16999                    let inside = selection.start >= open.end && selection.end <= *close.start();
17000                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17001                        || close.contains(&selection.head());
17002
17003                    // If best is next to a bracket and current isn't, skip
17004                    if !in_bracket_range && best_in_bracket_range {
17005                        continue;
17006                    }
17007
17008                    // Prefer smaller lengths unless best is inside and current isn't
17009                    if length > best_length && (best_inside || !inside) {
17010                        continue;
17011                    }
17012
17013                    best_length = length;
17014                    best_inside = inside;
17015                    best_in_bracket_range = in_bracket_range;
17016                    best_destination = Some(
17017                        if close.contains(&selection.start) && close.contains(&selection.end) {
17018                            if inside { open.end } else { open.start }
17019                        } else if inside {
17020                            *close.start()
17021                        } else {
17022                            *close.end()
17023                        },
17024                    );
17025                }
17026
17027                if let Some(destination) = best_destination {
17028                    selection.collapse_to(destination, SelectionGoal::None);
17029                }
17030            })
17031        });
17032    }
17033
17034    pub fn undo_selection(
17035        &mut self,
17036        _: &UndoSelection,
17037        window: &mut Window,
17038        cx: &mut Context<Self>,
17039    ) {
17040        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17041        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17042            self.selection_history.mode = SelectionHistoryMode::Undoing;
17043            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17044                this.end_selection(window, cx);
17045                this.change_selections(
17046                    SelectionEffects::scroll(Autoscroll::newest()),
17047                    window,
17048                    cx,
17049                    |s| s.select_anchors(entry.selections.to_vec()),
17050                );
17051            });
17052            self.selection_history.mode = SelectionHistoryMode::Normal;
17053
17054            self.select_next_state = entry.select_next_state;
17055            self.select_prev_state = entry.select_prev_state;
17056            self.add_selections_state = entry.add_selections_state;
17057        }
17058    }
17059
17060    pub fn redo_selection(
17061        &mut self,
17062        _: &RedoSelection,
17063        window: &mut Window,
17064        cx: &mut Context<Self>,
17065    ) {
17066        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17067        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17068            self.selection_history.mode = SelectionHistoryMode::Redoing;
17069            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17070                this.end_selection(window, cx);
17071                this.change_selections(
17072                    SelectionEffects::scroll(Autoscroll::newest()),
17073                    window,
17074                    cx,
17075                    |s| s.select_anchors(entry.selections.to_vec()),
17076                );
17077            });
17078            self.selection_history.mode = SelectionHistoryMode::Normal;
17079
17080            self.select_next_state = entry.select_next_state;
17081            self.select_prev_state = entry.select_prev_state;
17082            self.add_selections_state = entry.add_selections_state;
17083        }
17084    }
17085
17086    pub fn expand_excerpts(
17087        &mut self,
17088        action: &ExpandExcerpts,
17089        _: &mut Window,
17090        cx: &mut Context<Self>,
17091    ) {
17092        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17093    }
17094
17095    pub fn expand_excerpts_down(
17096        &mut self,
17097        action: &ExpandExcerptsDown,
17098        _: &mut Window,
17099        cx: &mut Context<Self>,
17100    ) {
17101        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17102    }
17103
17104    pub fn expand_excerpts_up(
17105        &mut self,
17106        action: &ExpandExcerptsUp,
17107        _: &mut Window,
17108        cx: &mut Context<Self>,
17109    ) {
17110        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17111    }
17112
17113    pub fn expand_excerpts_for_direction(
17114        &mut self,
17115        lines: u32,
17116        direction: ExpandExcerptDirection,
17117        cx: &mut Context<Self>,
17118    ) {
17119        let selections = self.selections.disjoint_anchors_arc();
17120
17121        let lines = if lines == 0 {
17122            EditorSettings::get_global(cx).expand_excerpt_lines
17123        } else {
17124            lines
17125        };
17126
17127        let snapshot = self.buffer.read(cx).snapshot(cx);
17128        let excerpt_ids = selections
17129            .iter()
17130            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17131            .unique()
17132            .sorted()
17133            .collect::<Vec<_>>();
17134
17135        if self.delegate_expand_excerpts {
17136            cx.emit(EditorEvent::ExpandExcerptsRequested {
17137                excerpt_ids,
17138                lines,
17139                direction,
17140            });
17141            return;
17142        }
17143
17144        self.buffer.update(cx, |buffer, cx| {
17145            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17146        })
17147    }
17148
17149    pub fn expand_excerpt(
17150        &mut self,
17151        excerpt: ExcerptId,
17152        direction: ExpandExcerptDirection,
17153        window: &mut Window,
17154        cx: &mut Context<Self>,
17155    ) {
17156        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17157
17158        if self.delegate_expand_excerpts {
17159            cx.emit(EditorEvent::ExpandExcerptsRequested {
17160                excerpt_ids: vec![excerpt],
17161                lines: lines_to_expand,
17162                direction,
17163            });
17164            return;
17165        }
17166
17167        let current_scroll_position = self.scroll_position(cx);
17168        let mut scroll = None;
17169
17170        if direction == ExpandExcerptDirection::Down {
17171            let multi_buffer = self.buffer.read(cx);
17172            let snapshot = multi_buffer.snapshot(cx);
17173            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17174                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17175                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17176            {
17177                let buffer_snapshot = buffer.read(cx).snapshot();
17178                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17179                let last_row = buffer_snapshot.max_point().row;
17180                let lines_below = last_row.saturating_sub(excerpt_end_row);
17181                if lines_below >= lines_to_expand {
17182                    scroll = Some(
17183                        current_scroll_position
17184                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17185                    );
17186                }
17187            }
17188        }
17189        if direction == ExpandExcerptDirection::Up
17190            && self
17191                .buffer
17192                .read(cx)
17193                .snapshot(cx)
17194                .excerpt_before(excerpt)
17195                .is_none()
17196        {
17197            scroll = Some(current_scroll_position);
17198        }
17199
17200        self.buffer.update(cx, |buffer, cx| {
17201            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17202        });
17203
17204        if let Some(new_scroll_position) = scroll {
17205            self.set_scroll_position(new_scroll_position, window, cx);
17206        }
17207    }
17208
17209    pub fn go_to_singleton_buffer_point(
17210        &mut self,
17211        point: Point,
17212        window: &mut Window,
17213        cx: &mut Context<Self>,
17214    ) {
17215        self.go_to_singleton_buffer_range(point..point, window, cx);
17216    }
17217
17218    pub fn go_to_singleton_buffer_range(
17219        &mut self,
17220        range: Range<Point>,
17221        window: &mut Window,
17222        cx: &mut Context<Self>,
17223    ) {
17224        let multibuffer = self.buffer().read(cx);
17225        let Some(buffer) = multibuffer.as_singleton() else {
17226            return;
17227        };
17228        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17229            return;
17230        };
17231        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17232            return;
17233        };
17234        self.change_selections(
17235            SelectionEffects::default().nav_history(true),
17236            window,
17237            cx,
17238            |s| s.select_anchor_ranges([start..end]),
17239        );
17240    }
17241
17242    pub fn go_to_diagnostic(
17243        &mut self,
17244        action: &GoToDiagnostic,
17245        window: &mut Window,
17246        cx: &mut Context<Self>,
17247    ) {
17248        if !self.diagnostics_enabled() {
17249            return;
17250        }
17251        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17252        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17253    }
17254
17255    pub fn go_to_prev_diagnostic(
17256        &mut self,
17257        action: &GoToPreviousDiagnostic,
17258        window: &mut Window,
17259        cx: &mut Context<Self>,
17260    ) {
17261        if !self.diagnostics_enabled() {
17262            return;
17263        }
17264        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17265        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17266    }
17267
17268    pub fn go_to_diagnostic_impl(
17269        &mut self,
17270        direction: Direction,
17271        severity: GoToDiagnosticSeverityFilter,
17272        window: &mut Window,
17273        cx: &mut Context<Self>,
17274    ) {
17275        let buffer = self.buffer.read(cx).snapshot(cx);
17276        let selection = self
17277            .selections
17278            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17279
17280        let mut active_group_id = None;
17281        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17282            && active_group.active_range.start.to_offset(&buffer) == selection.start
17283        {
17284            active_group_id = Some(active_group.group_id);
17285        }
17286
17287        fn filtered<'a>(
17288            severity: GoToDiagnosticSeverityFilter,
17289            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17290        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17291            diagnostics
17292                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17293                .filter(|entry| entry.range.start != entry.range.end)
17294                .filter(|entry| !entry.diagnostic.is_unnecessary)
17295        }
17296
17297        let before = filtered(
17298            severity,
17299            buffer
17300                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17301                .filter(|entry| entry.range.start <= selection.start),
17302        );
17303        let after = filtered(
17304            severity,
17305            buffer
17306                .diagnostics_in_range(selection.start..buffer.len())
17307                .filter(|entry| entry.range.start >= selection.start),
17308        );
17309
17310        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17311        if direction == Direction::Prev {
17312            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17313            {
17314                for diagnostic in prev_diagnostics.into_iter().rev() {
17315                    if diagnostic.range.start != selection.start
17316                        || active_group_id
17317                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17318                    {
17319                        found = Some(diagnostic);
17320                        break 'outer;
17321                    }
17322                }
17323            }
17324        } else {
17325            for diagnostic in after.chain(before) {
17326                if diagnostic.range.start != selection.start
17327                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17328                {
17329                    found = Some(diagnostic);
17330                    break;
17331                }
17332            }
17333        }
17334        let Some(next_diagnostic) = found else {
17335            return;
17336        };
17337
17338        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17339        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17340            return;
17341        };
17342        let snapshot = self.snapshot(window, cx);
17343        if snapshot.intersects_fold(next_diagnostic.range.start) {
17344            self.unfold_ranges(
17345                std::slice::from_ref(&next_diagnostic.range),
17346                true,
17347                false,
17348                cx,
17349            );
17350        }
17351        self.change_selections(Default::default(), window, cx, |s| {
17352            s.select_ranges(vec![
17353                next_diagnostic.range.start..next_diagnostic.range.start,
17354            ])
17355        });
17356        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17357        self.refresh_edit_prediction(false, true, window, cx);
17358    }
17359
17360    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17361        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17362        let snapshot = self.snapshot(window, cx);
17363        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17364        self.go_to_hunk_before_or_after_position(
17365            &snapshot,
17366            selection.head(),
17367            Direction::Next,
17368            true,
17369            window,
17370            cx,
17371        );
17372    }
17373
17374    pub fn go_to_hunk_before_or_after_position(
17375        &mut self,
17376        snapshot: &EditorSnapshot,
17377        position: Point,
17378        direction: Direction,
17379        wrap_around: bool,
17380        window: &mut Window,
17381        cx: &mut Context<Editor>,
17382    ) {
17383        let row = if direction == Direction::Next {
17384            self.hunk_after_position(snapshot, position, wrap_around)
17385                .map(|hunk| hunk.row_range.start)
17386        } else {
17387            self.hunk_before_position(snapshot, position, wrap_around)
17388        };
17389
17390        if let Some(row) = row {
17391            let destination = Point::new(row.0, 0);
17392            let autoscroll = Autoscroll::center();
17393
17394            self.unfold_ranges(&[destination..destination], false, false, cx);
17395            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17396                s.select_ranges([destination..destination]);
17397            });
17398        }
17399    }
17400
17401    fn hunk_after_position(
17402        &mut self,
17403        snapshot: &EditorSnapshot,
17404        position: Point,
17405        wrap_around: bool,
17406    ) -> Option<MultiBufferDiffHunk> {
17407        let result = snapshot
17408            .buffer_snapshot()
17409            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17410            .find(|hunk| hunk.row_range.start.0 > position.row);
17411
17412        if wrap_around {
17413            result.or_else(|| {
17414                snapshot
17415                    .buffer_snapshot()
17416                    .diff_hunks_in_range(Point::zero()..position)
17417                    .find(|hunk| hunk.row_range.end.0 < position.row)
17418            })
17419        } else {
17420            result
17421        }
17422    }
17423
17424    fn go_to_prev_hunk(
17425        &mut self,
17426        _: &GoToPreviousHunk,
17427        window: &mut Window,
17428        cx: &mut Context<Self>,
17429    ) {
17430        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17431        let snapshot = self.snapshot(window, cx);
17432        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17433        self.go_to_hunk_before_or_after_position(
17434            &snapshot,
17435            selection.head(),
17436            Direction::Prev,
17437            true,
17438            window,
17439            cx,
17440        );
17441    }
17442
17443    fn hunk_before_position(
17444        &mut self,
17445        snapshot: &EditorSnapshot,
17446        position: Point,
17447        wrap_around: bool,
17448    ) -> Option<MultiBufferRow> {
17449        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17450
17451        if wrap_around {
17452            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17453        } else {
17454            result
17455        }
17456    }
17457
17458    fn go_to_next_change(
17459        &mut self,
17460        _: &GoToNextChange,
17461        window: &mut Window,
17462        cx: &mut Context<Self>,
17463    ) {
17464        if let Some(selections) = self
17465            .change_list
17466            .next_change(1, Direction::Next)
17467            .map(|s| s.to_vec())
17468        {
17469            self.change_selections(Default::default(), window, cx, |s| {
17470                let map = s.display_snapshot();
17471                s.select_display_ranges(selections.iter().map(|a| {
17472                    let point = a.to_display_point(&map);
17473                    point..point
17474                }))
17475            })
17476        }
17477    }
17478
17479    fn go_to_previous_change(
17480        &mut self,
17481        _: &GoToPreviousChange,
17482        window: &mut Window,
17483        cx: &mut Context<Self>,
17484    ) {
17485        if let Some(selections) = self
17486            .change_list
17487            .next_change(1, Direction::Prev)
17488            .map(|s| s.to_vec())
17489        {
17490            self.change_selections(Default::default(), window, cx, |s| {
17491                let map = s.display_snapshot();
17492                s.select_display_ranges(selections.iter().map(|a| {
17493                    let point = a.to_display_point(&map);
17494                    point..point
17495                }))
17496            })
17497        }
17498    }
17499
17500    pub fn go_to_next_document_highlight(
17501        &mut self,
17502        _: &GoToNextDocumentHighlight,
17503        window: &mut Window,
17504        cx: &mut Context<Self>,
17505    ) {
17506        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17507    }
17508
17509    pub fn go_to_prev_document_highlight(
17510        &mut self,
17511        _: &GoToPreviousDocumentHighlight,
17512        window: &mut Window,
17513        cx: &mut Context<Self>,
17514    ) {
17515        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17516    }
17517
17518    pub fn go_to_document_highlight_before_or_after_position(
17519        &mut self,
17520        direction: Direction,
17521        window: &mut Window,
17522        cx: &mut Context<Editor>,
17523    ) {
17524        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17525        let snapshot = self.snapshot(window, cx);
17526        let buffer = &snapshot.buffer_snapshot();
17527        let position = self
17528            .selections
17529            .newest::<Point>(&snapshot.display_snapshot)
17530            .head();
17531        let anchor_position = buffer.anchor_after(position);
17532
17533        // Get all document highlights (both read and write)
17534        let mut all_highlights = Vec::new();
17535
17536        if let Some((_, read_highlights)) = self
17537            .background_highlights
17538            .get(&HighlightKey::DocumentHighlightRead)
17539        {
17540            all_highlights.extend(read_highlights.iter());
17541        }
17542
17543        if let Some((_, write_highlights)) = self
17544            .background_highlights
17545            .get(&HighlightKey::DocumentHighlightWrite)
17546        {
17547            all_highlights.extend(write_highlights.iter());
17548        }
17549
17550        if all_highlights.is_empty() {
17551            return;
17552        }
17553
17554        // Sort highlights by position
17555        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17556
17557        let target_highlight = match direction {
17558            Direction::Next => {
17559                // Find the first highlight after the current position
17560                all_highlights
17561                    .iter()
17562                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17563            }
17564            Direction::Prev => {
17565                // Find the last highlight before the current position
17566                all_highlights
17567                    .iter()
17568                    .rev()
17569                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17570            }
17571        };
17572
17573        if let Some(highlight) = target_highlight {
17574            let destination = highlight.start.to_point(buffer);
17575            let autoscroll = Autoscroll::center();
17576
17577            self.unfold_ranges(&[destination..destination], false, false, cx);
17578            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17579                s.select_ranges([destination..destination]);
17580            });
17581        }
17582    }
17583
17584    fn go_to_line<T: 'static>(
17585        &mut self,
17586        position: Anchor,
17587        highlight_color: Option<Hsla>,
17588        window: &mut Window,
17589        cx: &mut Context<Self>,
17590    ) {
17591        let snapshot = self.snapshot(window, cx).display_snapshot;
17592        let position = position.to_point(&snapshot.buffer_snapshot());
17593        let start = snapshot
17594            .buffer_snapshot()
17595            .clip_point(Point::new(position.row, 0), Bias::Left);
17596        let end = start + Point::new(1, 0);
17597        let start = snapshot.buffer_snapshot().anchor_before(start);
17598        let end = snapshot.buffer_snapshot().anchor_before(end);
17599
17600        self.highlight_rows::<T>(
17601            start..end,
17602            highlight_color
17603                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17604            Default::default(),
17605            cx,
17606        );
17607
17608        if self.buffer.read(cx).is_singleton() {
17609            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17610        }
17611    }
17612
17613    pub fn go_to_definition(
17614        &mut self,
17615        _: &GoToDefinition,
17616        window: &mut Window,
17617        cx: &mut Context<Self>,
17618    ) -> Task<Result<Navigated>> {
17619        let definition =
17620            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17621        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17622        cx.spawn_in(window, async move |editor, cx| {
17623            if definition.await? == Navigated::Yes {
17624                return Ok(Navigated::Yes);
17625            }
17626            match fallback_strategy {
17627                GoToDefinitionFallback::None => Ok(Navigated::No),
17628                GoToDefinitionFallback::FindAllReferences => {
17629                    match editor.update_in(cx, |editor, window, cx| {
17630                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17631                    })? {
17632                        Some(references) => references.await,
17633                        None => Ok(Navigated::No),
17634                    }
17635                }
17636            }
17637        })
17638    }
17639
17640    pub fn go_to_declaration(
17641        &mut self,
17642        _: &GoToDeclaration,
17643        window: &mut Window,
17644        cx: &mut Context<Self>,
17645    ) -> Task<Result<Navigated>> {
17646        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17647    }
17648
17649    pub fn go_to_declaration_split(
17650        &mut self,
17651        _: &GoToDeclaration,
17652        window: &mut Window,
17653        cx: &mut Context<Self>,
17654    ) -> Task<Result<Navigated>> {
17655        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17656    }
17657
17658    pub fn go_to_implementation(
17659        &mut self,
17660        _: &GoToImplementation,
17661        window: &mut Window,
17662        cx: &mut Context<Self>,
17663    ) -> Task<Result<Navigated>> {
17664        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17665    }
17666
17667    pub fn go_to_implementation_split(
17668        &mut self,
17669        _: &GoToImplementationSplit,
17670        window: &mut Window,
17671        cx: &mut Context<Self>,
17672    ) -> Task<Result<Navigated>> {
17673        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17674    }
17675
17676    pub fn go_to_type_definition(
17677        &mut self,
17678        _: &GoToTypeDefinition,
17679        window: &mut Window,
17680        cx: &mut Context<Self>,
17681    ) -> Task<Result<Navigated>> {
17682        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17683    }
17684
17685    pub fn go_to_definition_split(
17686        &mut self,
17687        _: &GoToDefinitionSplit,
17688        window: &mut Window,
17689        cx: &mut Context<Self>,
17690    ) -> Task<Result<Navigated>> {
17691        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17692    }
17693
17694    pub fn go_to_type_definition_split(
17695        &mut self,
17696        _: &GoToTypeDefinitionSplit,
17697        window: &mut Window,
17698        cx: &mut Context<Self>,
17699    ) -> Task<Result<Navigated>> {
17700        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17701    }
17702
17703    fn go_to_definition_of_kind(
17704        &mut self,
17705        kind: GotoDefinitionKind,
17706        split: bool,
17707        window: &mut Window,
17708        cx: &mut Context<Self>,
17709    ) -> Task<Result<Navigated>> {
17710        let Some(provider) = self.semantics_provider.clone() else {
17711            return Task::ready(Ok(Navigated::No));
17712        };
17713        let head = self
17714            .selections
17715            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17716            .head();
17717        let buffer = self.buffer.read(cx);
17718        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17719            return Task::ready(Ok(Navigated::No));
17720        };
17721        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17722            return Task::ready(Ok(Navigated::No));
17723        };
17724
17725        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17726
17727        cx.spawn_in(window, async move |editor, cx| {
17728            let Some(definitions) = definitions.await? else {
17729                return Ok(Navigated::No);
17730            };
17731            let navigated = editor
17732                .update_in(cx, |editor, window, cx| {
17733                    editor.navigate_to_hover_links(
17734                        Some(kind),
17735                        definitions
17736                            .into_iter()
17737                            .filter(|location| {
17738                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17739                            })
17740                            .map(HoverLink::Text)
17741                            .collect::<Vec<_>>(),
17742                        nav_entry,
17743                        split,
17744                        window,
17745                        cx,
17746                    )
17747                })?
17748                .await?;
17749            anyhow::Ok(navigated)
17750        })
17751    }
17752
17753    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
17754        let selection = self.selections.newest_anchor();
17755        let head = selection.head();
17756        let tail = selection.tail();
17757
17758        let Some((buffer, start_position)) =
17759            self.buffer.read(cx).text_anchor_for_position(head, cx)
17760        else {
17761            return;
17762        };
17763
17764        let end_position = if head != tail {
17765            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
17766                return;
17767            };
17768            Some(pos)
17769        } else {
17770            None
17771        };
17772
17773        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
17774            let url = if let Some(end_pos) = end_position {
17775                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
17776            } else {
17777                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
17778            };
17779
17780            if let Some(url) = url {
17781                cx.update(|window, cx| {
17782                    if parse_zed_link(&url, cx).is_some() {
17783                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17784                    } else {
17785                        cx.open_url(&url);
17786                    }
17787                })?;
17788            }
17789
17790            anyhow::Ok(())
17791        });
17792
17793        url_finder.detach();
17794    }
17795
17796    pub fn open_selected_filename(
17797        &mut self,
17798        _: &OpenSelectedFilename,
17799        window: &mut Window,
17800        cx: &mut Context<Self>,
17801    ) {
17802        let Some(workspace) = self.workspace() else {
17803            return;
17804        };
17805
17806        let position = self.selections.newest_anchor().head();
17807
17808        let Some((buffer, buffer_position)) =
17809            self.buffer.read(cx).text_anchor_for_position(position, cx)
17810        else {
17811            return;
17812        };
17813
17814        let project = self.project.clone();
17815
17816        cx.spawn_in(window, async move |_, cx| {
17817            let result = find_file(&buffer, project, buffer_position, cx).await;
17818
17819            if let Some((_, path)) = result {
17820                workspace
17821                    .update_in(cx, |workspace, window, cx| {
17822                        workspace.open_resolved_path(path, window, cx)
17823                    })?
17824                    .await?;
17825            }
17826            anyhow::Ok(())
17827        })
17828        .detach();
17829    }
17830
17831    pub(crate) fn navigate_to_hover_links(
17832        &mut self,
17833        kind: Option<GotoDefinitionKind>,
17834        definitions: Vec<HoverLink>,
17835        origin: Option<NavigationEntry>,
17836        split: bool,
17837        window: &mut Window,
17838        cx: &mut Context<Editor>,
17839    ) -> Task<Result<Navigated>> {
17840        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
17841        let mut first_url_or_file = None;
17842        let definitions: Vec<_> = definitions
17843            .into_iter()
17844            .filter_map(|def| match def {
17845                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
17846                HoverLink::InlayHint(lsp_location, server_id) => {
17847                    let computation =
17848                        self.compute_target_location(lsp_location, server_id, window, cx);
17849                    Some(cx.background_spawn(computation))
17850                }
17851                HoverLink::Url(url) => {
17852                    first_url_or_file = Some(Either::Left(url));
17853                    None
17854                }
17855                HoverLink::File(path) => {
17856                    first_url_or_file = Some(Either::Right(path));
17857                    None
17858                }
17859            })
17860            .collect();
17861
17862        let workspace = self.workspace();
17863
17864        cx.spawn_in(window, async move |editor, cx| {
17865            let locations: Vec<Location> = future::join_all(definitions)
17866                .await
17867                .into_iter()
17868                .filter_map(|location| location.transpose())
17869                .collect::<Result<_>>()
17870                .context("location tasks")?;
17871            let mut locations = cx.update(|_, cx| {
17872                locations
17873                    .into_iter()
17874                    .map(|location| {
17875                        let buffer = location.buffer.read(cx);
17876                        (location.buffer, location.range.to_point(buffer))
17877                    })
17878                    .into_group_map()
17879            })?;
17880            let mut num_locations = 0;
17881            for ranges in locations.values_mut() {
17882                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
17883                ranges.dedup();
17884                num_locations += ranges.len();
17885            }
17886
17887            if num_locations > 1 {
17888                let tab_kind = match kind {
17889                    Some(GotoDefinitionKind::Implementation) => "Implementations",
17890                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
17891                    Some(GotoDefinitionKind::Declaration) => "Declarations",
17892                    Some(GotoDefinitionKind::Type) => "Types",
17893                };
17894                let title = editor
17895                    .update_in(cx, |_, _, cx| {
17896                        let target = locations
17897                            .iter()
17898                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
17899                            .map(|(buffer, location)| {
17900                                buffer
17901                                    .read(cx)
17902                                    .text_for_range(location.clone())
17903                                    .collect::<String>()
17904                            })
17905                            .filter(|text| !text.contains('\n'))
17906                            .unique()
17907                            .take(3)
17908                            .join(", ");
17909                        if target.is_empty() {
17910                            tab_kind.to_owned()
17911                        } else {
17912                            format!("{tab_kind} for {target}")
17913                        }
17914                    })
17915                    .context("buffer title")?;
17916
17917                let Some(workspace) = workspace else {
17918                    return Ok(Navigated::No);
17919                };
17920
17921                let opened = workspace
17922                    .update_in(cx, |workspace, window, cx| {
17923                        let allow_preview = PreviewTabsSettings::get_global(cx)
17924                            .enable_preview_multibuffer_from_code_navigation;
17925                        if let Some((target_editor, target_pane)) =
17926                            Self::open_locations_in_multibuffer(
17927                                workspace,
17928                                locations,
17929                                title,
17930                                split,
17931                                allow_preview,
17932                                MultibufferSelectionMode::First,
17933                                window,
17934                                cx,
17935                            )
17936                        {
17937                            // We create our own nav history instead of using
17938                            // `target_editor.nav_history` because `nav_history`
17939                            // seems to be populated asynchronously when an item
17940                            // is added to a pane
17941                            let mut nav_history = target_pane
17942                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
17943                            target_editor.update(cx, |editor, cx| {
17944                                let nav_data = editor
17945                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
17946                                let target =
17947                                    Some(nav_history.navigation_entry(Some(
17948                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
17949                                    )));
17950                                nav_history.push_tag(origin, target);
17951                            })
17952                        }
17953                    })
17954                    .is_ok();
17955
17956                anyhow::Ok(Navigated::from_bool(opened))
17957            } else if num_locations == 0 {
17958                // If there is one url or file, open it directly
17959                match first_url_or_file {
17960                    Some(Either::Left(url)) => {
17961                        cx.update(|window, cx| {
17962                            if parse_zed_link(&url, cx).is_some() {
17963                                window
17964                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17965                            } else {
17966                                cx.open_url(&url);
17967                            }
17968                        })?;
17969                        Ok(Navigated::Yes)
17970                    }
17971                    Some(Either::Right(path)) => {
17972                        // TODO(andrew): respect preview tab settings
17973                        //               `enable_keep_preview_on_code_navigation` and
17974                        //               `enable_preview_file_from_code_navigation`
17975                        let Some(workspace) = workspace else {
17976                            return Ok(Navigated::No);
17977                        };
17978                        workspace
17979                            .update_in(cx, |workspace, window, cx| {
17980                                workspace.open_resolved_path(path, window, cx)
17981                            })?
17982                            .await?;
17983                        Ok(Navigated::Yes)
17984                    }
17985                    None => Ok(Navigated::No),
17986                }
17987            } else {
17988                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
17989                let target_range = target_ranges.first().unwrap().clone();
17990
17991                editor.update_in(cx, |editor, window, cx| {
17992                    let range = editor.range_for_match(&target_range);
17993                    let range = collapse_multiline_range(range);
17994
17995                    if !split
17996                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
17997                    {
17998                        editor.go_to_singleton_buffer_range(range, window, cx);
17999
18000                        let target =
18001                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18002                        if let Some(mut nav_history) = editor.nav_history.clone() {
18003                            nav_history.push_tag(origin, target);
18004                        }
18005                    } else {
18006                        let Some(workspace) = workspace else {
18007                            return Navigated::No;
18008                        };
18009                        let pane = workspace.read(cx).active_pane().clone();
18010                        window.defer(cx, move |window, cx| {
18011                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18012                                workspace.update(cx, |workspace, cx| {
18013                                    let pane = if split {
18014                                        workspace.adjacent_pane(window, cx)
18015                                    } else {
18016                                        workspace.active_pane().clone()
18017                                    };
18018
18019                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18020                                    let keep_old_preview = preview_tabs_settings
18021                                        .enable_keep_preview_on_code_navigation;
18022                                    let allow_new_preview = preview_tabs_settings
18023                                        .enable_preview_file_from_code_navigation;
18024
18025                                    let editor = workspace.open_project_item(
18026                                        pane.clone(),
18027                                        target_buffer.clone(),
18028                                        true,
18029                                        true,
18030                                        keep_old_preview,
18031                                        allow_new_preview,
18032                                        window,
18033                                        cx,
18034                                    );
18035                                    (editor, pane)
18036                                });
18037                            // We create our own nav history instead of using
18038                            // `target_editor.nav_history` because `nav_history`
18039                            // seems to be populated asynchronously when an item
18040                            // is added to a pane
18041                            let mut nav_history = target_pane
18042                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18043                            target_editor.update(cx, |target_editor, cx| {
18044                                // When selecting a definition in a different buffer, disable the nav history
18045                                // to avoid creating a history entry at the previous cursor location.
18046                                pane.update(cx, |pane, _| pane.disable_history());
18047                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18048
18049                                let nav_data = target_editor.navigation_data(
18050                                    target_editor.selections.newest_anchor().head(),
18051                                    cx,
18052                                );
18053                                let target =
18054                                    Some(nav_history.navigation_entry(Some(
18055                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18056                                    )));
18057                                nav_history.push_tag(origin, target);
18058                                pane.update(cx, |pane, _| pane.enable_history());
18059                            });
18060                        });
18061                    }
18062                    Navigated::Yes
18063                })
18064            }
18065        })
18066    }
18067
18068    fn compute_target_location(
18069        &self,
18070        lsp_location: lsp::Location,
18071        server_id: LanguageServerId,
18072        window: &mut Window,
18073        cx: &mut Context<Self>,
18074    ) -> Task<anyhow::Result<Option<Location>>> {
18075        let Some(project) = self.project.clone() else {
18076            return Task::ready(Ok(None));
18077        };
18078
18079        cx.spawn_in(window, async move |editor, cx| {
18080            let location_task = editor.update(cx, |_, cx| {
18081                project.update(cx, |project, cx| {
18082                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18083                })
18084            })?;
18085            let location = Some({
18086                let target_buffer_handle = location_task.await.context("open local buffer")?;
18087                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18088                    let target_start = target_buffer
18089                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18090                    let target_end = target_buffer
18091                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18092                    target_buffer.anchor_after(target_start)
18093                        ..target_buffer.anchor_before(target_end)
18094                });
18095                Location {
18096                    buffer: target_buffer_handle,
18097                    range,
18098                }
18099            });
18100            Ok(location)
18101        })
18102    }
18103
18104    fn go_to_next_reference(
18105        &mut self,
18106        _: &GoToNextReference,
18107        window: &mut Window,
18108        cx: &mut Context<Self>,
18109    ) {
18110        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18111        if let Some(task) = task {
18112            task.detach();
18113        };
18114    }
18115
18116    fn go_to_prev_reference(
18117        &mut self,
18118        _: &GoToPreviousReference,
18119        window: &mut Window,
18120        cx: &mut Context<Self>,
18121    ) {
18122        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18123        if let Some(task) = task {
18124            task.detach();
18125        };
18126    }
18127
18128    fn go_to_symbol_by_offset(
18129        &mut self,
18130        window: &mut Window,
18131        cx: &mut Context<Self>,
18132        offset: i8,
18133    ) -> Task<Result<()>> {
18134        let editor_snapshot = self.snapshot(window, cx);
18135
18136        // We don't care about multi-buffer symbols
18137        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18138            return Task::ready(Ok(()));
18139        };
18140
18141        let cursor_offset = self
18142            .selections
18143            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18144            .head();
18145
18146        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18147            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18148                let buffer = ed.buffer.read(cx).as_singleton()?;
18149                Some(buffer.read(cx).remote_id())
18150            }) else {
18151                return Ok(());
18152            };
18153
18154            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18155            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18156
18157            let multi_snapshot = editor_snapshot.buffer();
18158            let buffer_range = |range: &Range<_>| {
18159                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18160            };
18161
18162            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18163                let current_idx = outline_items
18164                    .iter()
18165                    .enumerate()
18166                    .filter_map(|(idx, item)| {
18167                        // Find the closest outline item by distance between outline text and cursor location
18168                        let source_range = buffer_range(&item.source_range_for_text);
18169                        let distance_to_closest_endpoint = cmp::min(
18170                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18171                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18172                        );
18173
18174                        let item_towards_offset =
18175                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18176                                == (offset as isize).signum();
18177
18178                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18179
18180                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18181                        // we should not already be within the outline's source range. We then pick the closest outline
18182                        // item.
18183                        (item_towards_offset && !source_range_contains_cursor)
18184                            .then_some((distance_to_closest_endpoint, idx))
18185                    })
18186                    .min()
18187                    .map(|(_, idx)| idx);
18188
18189                let Some(idx) = current_idx else {
18190                    return;
18191                };
18192
18193                let range = buffer_range(&outline_items[idx].source_range_for_text);
18194                let selection = [range.start..range.start];
18195
18196                let _ = editor
18197                    .update(acx, |editor, ecx| {
18198                        editor.change_selections(
18199                            SelectionEffects::scroll(Autoscroll::newest()),
18200                            window,
18201                            ecx,
18202                            |s| s.select_ranges(selection),
18203                        );
18204                    })
18205                    .ok();
18206            })?;
18207
18208            Ok(())
18209        })
18210    }
18211
18212    fn go_to_next_symbol(
18213        &mut self,
18214        _: &GoToNextSymbol,
18215        window: &mut Window,
18216        cx: &mut Context<Self>,
18217    ) {
18218        self.go_to_symbol_by_offset(window, cx, 1).detach();
18219    }
18220
18221    fn go_to_previous_symbol(
18222        &mut self,
18223        _: &GoToPreviousSymbol,
18224        window: &mut Window,
18225        cx: &mut Context<Self>,
18226    ) {
18227        self.go_to_symbol_by_offset(window, cx, -1).detach();
18228    }
18229
18230    pub fn go_to_reference_before_or_after_position(
18231        &mut self,
18232        direction: Direction,
18233        count: usize,
18234        window: &mut Window,
18235        cx: &mut Context<Self>,
18236    ) -> Option<Task<Result<()>>> {
18237        let selection = self.selections.newest_anchor();
18238        let head = selection.head();
18239
18240        let multi_buffer = self.buffer.read(cx);
18241
18242        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18243        let workspace = self.workspace()?;
18244        let project = workspace.read(cx).project().clone();
18245        let references =
18246            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18247        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18248            let Some(locations) = references.await? else {
18249                return Ok(());
18250            };
18251
18252            if locations.is_empty() {
18253                // totally normal - the cursor may be on something which is not
18254                // a symbol (e.g. a keyword)
18255                log::info!("no references found under cursor");
18256                return Ok(());
18257            }
18258
18259            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18260
18261            let (locations, current_location_index) =
18262                multi_buffer.update(cx, |multi_buffer, cx| {
18263                    let mut locations = locations
18264                        .into_iter()
18265                        .filter_map(|loc| {
18266                            let start = multi_buffer.buffer_anchor_to_anchor(
18267                                &loc.buffer,
18268                                loc.range.start,
18269                                cx,
18270                            )?;
18271                            let end = multi_buffer.buffer_anchor_to_anchor(
18272                                &loc.buffer,
18273                                loc.range.end,
18274                                cx,
18275                            )?;
18276                            Some(start..end)
18277                        })
18278                        .collect::<Vec<_>>();
18279
18280                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18281                    // There is an O(n) implementation, but given this list will be
18282                    // small (usually <100 items), the extra O(log(n)) factor isn't
18283                    // worth the (surprisingly large amount of) extra complexity.
18284                    locations
18285                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18286
18287                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18288
18289                    let current_location_index = locations.iter().position(|loc| {
18290                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18291                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18292                    });
18293
18294                    (locations, current_location_index)
18295                });
18296
18297            let Some(current_location_index) = current_location_index else {
18298                // This indicates something has gone wrong, because we already
18299                // handle the "no references" case above
18300                log::error!(
18301                    "failed to find current reference under cursor. Total references: {}",
18302                    locations.len()
18303                );
18304                return Ok(());
18305            };
18306
18307            let destination_location_index = match direction {
18308                Direction::Next => (current_location_index + count) % locations.len(),
18309                Direction::Prev => {
18310                    (current_location_index + locations.len() - count % locations.len())
18311                        % locations.len()
18312                }
18313            };
18314
18315            // TODO(cameron): is this needed?
18316            // the thinking is to avoid "jumping to the current location" (avoid
18317            // polluting "jumplist" in vim terms)
18318            if current_location_index == destination_location_index {
18319                return Ok(());
18320            }
18321
18322            let Range { start, end } = locations[destination_location_index];
18323
18324            editor.update_in(cx, |editor, window, cx| {
18325                let effects = SelectionEffects::default();
18326
18327                editor.unfold_ranges(&[start..end], false, false, cx);
18328                editor.change_selections(effects, window, cx, |s| {
18329                    s.select_ranges([start..start]);
18330                });
18331            })?;
18332
18333            Ok(())
18334        }))
18335    }
18336
18337    pub fn find_all_references(
18338        &mut self,
18339        action: &FindAllReferences,
18340        window: &mut Window,
18341        cx: &mut Context<Self>,
18342    ) -> Option<Task<Result<Navigated>>> {
18343        let always_open_multibuffer = action.always_open_multibuffer;
18344        let selection = self.selections.newest_anchor();
18345        let multi_buffer = self.buffer.read(cx);
18346        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18347        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18348        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18349        let head = selection_offset.head();
18350
18351        let head_anchor = multi_buffer_snapshot.anchor_at(
18352            head,
18353            if head < selection_offset.tail() {
18354                Bias::Right
18355            } else {
18356                Bias::Left
18357            },
18358        );
18359
18360        match self
18361            .find_all_references_task_sources
18362            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18363        {
18364            Ok(_) => {
18365                log::info!(
18366                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18367                );
18368                return None;
18369            }
18370            Err(i) => {
18371                self.find_all_references_task_sources.insert(i, head_anchor);
18372            }
18373        }
18374
18375        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18376        let workspace = self.workspace()?;
18377        let project = workspace.read(cx).project().clone();
18378        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18379        Some(cx.spawn_in(window, async move |editor, cx| {
18380            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18381                if let Ok(i) = editor
18382                    .find_all_references_task_sources
18383                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18384                {
18385                    editor.find_all_references_task_sources.remove(i);
18386                }
18387            });
18388
18389            let Some(locations) = references.await? else {
18390                return anyhow::Ok(Navigated::No);
18391            };
18392            let mut locations = cx.update(|_, cx| {
18393                locations
18394                    .into_iter()
18395                    .map(|location| {
18396                        let buffer = location.buffer.read(cx);
18397                        (location.buffer, location.range.to_point(buffer))
18398                    })
18399                    // if special-casing the single-match case, remove ranges
18400                    // that intersect current selection
18401                    .filter(|(location_buffer, location)| {
18402                        if always_open_multibuffer || &buffer != location_buffer {
18403                            return true;
18404                        }
18405
18406                        !location.contains_inclusive(&selection_point.range())
18407                    })
18408                    .into_group_map()
18409            })?;
18410            if locations.is_empty() {
18411                return anyhow::Ok(Navigated::No);
18412            }
18413            for ranges in locations.values_mut() {
18414                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18415                ranges.dedup();
18416            }
18417            let mut num_locations = 0;
18418            for ranges in locations.values_mut() {
18419                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18420                ranges.dedup();
18421                num_locations += ranges.len();
18422            }
18423
18424            if num_locations == 1 && !always_open_multibuffer {
18425                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18426                let target_range = target_ranges.first().unwrap().clone();
18427
18428                return editor.update_in(cx, |editor, window, cx| {
18429                    let range = target_range.to_point(target_buffer.read(cx));
18430                    let range = editor.range_for_match(&range);
18431                    let range = range.start..range.start;
18432
18433                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18434                        editor.go_to_singleton_buffer_range(range, window, cx);
18435                    } else {
18436                        let pane = workspace.read(cx).active_pane().clone();
18437                        window.defer(cx, move |window, cx| {
18438                            let target_editor: Entity<Self> =
18439                                workspace.update(cx, |workspace, cx| {
18440                                    let pane = workspace.active_pane().clone();
18441
18442                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18443                                    let keep_old_preview = preview_tabs_settings
18444                                        .enable_keep_preview_on_code_navigation;
18445                                    let allow_new_preview = preview_tabs_settings
18446                                        .enable_preview_file_from_code_navigation;
18447
18448                                    workspace.open_project_item(
18449                                        pane,
18450                                        target_buffer.clone(),
18451                                        true,
18452                                        true,
18453                                        keep_old_preview,
18454                                        allow_new_preview,
18455                                        window,
18456                                        cx,
18457                                    )
18458                                });
18459                            target_editor.update(cx, |target_editor, cx| {
18460                                // When selecting a definition in a different buffer, disable the nav history
18461                                // to avoid creating a history entry at the previous cursor location.
18462                                pane.update(cx, |pane, _| pane.disable_history());
18463                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18464                                pane.update(cx, |pane, _| pane.enable_history());
18465                            });
18466                        });
18467                    }
18468                    Navigated::No
18469                });
18470            }
18471
18472            workspace.update_in(cx, |workspace, window, cx| {
18473                let target = locations
18474                    .iter()
18475                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18476                    .map(|(buffer, location)| {
18477                        buffer
18478                            .read(cx)
18479                            .text_for_range(location.clone())
18480                            .collect::<String>()
18481                    })
18482                    .filter(|text| !text.contains('\n'))
18483                    .unique()
18484                    .take(3)
18485                    .join(", ");
18486                let title = if target.is_empty() {
18487                    "References".to_owned()
18488                } else {
18489                    format!("References to {target}")
18490                };
18491                let allow_preview = PreviewTabsSettings::get_global(cx)
18492                    .enable_preview_multibuffer_from_code_navigation;
18493                Self::open_locations_in_multibuffer(
18494                    workspace,
18495                    locations,
18496                    title,
18497                    false,
18498                    allow_preview,
18499                    MultibufferSelectionMode::First,
18500                    window,
18501                    cx,
18502                );
18503                Navigated::Yes
18504            })
18505        }))
18506    }
18507
18508    /// Opens a multibuffer with the given project locations in it.
18509    pub fn open_locations_in_multibuffer(
18510        workspace: &mut Workspace,
18511        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18512        title: String,
18513        split: bool,
18514        allow_preview: bool,
18515        multibuffer_selection_mode: MultibufferSelectionMode,
18516        window: &mut Window,
18517        cx: &mut Context<Workspace>,
18518    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18519        if locations.is_empty() {
18520            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18521            return None;
18522        }
18523
18524        let capability = workspace.project().read(cx).capability();
18525        let mut ranges = <Vec<Range<Anchor>>>::new();
18526
18527        // a key to find existing multibuffer editors with the same set of locations
18528        // to prevent us from opening more and more multibuffer tabs for searches and the like
18529        let mut key = (title.clone(), vec![]);
18530        let excerpt_buffer = cx.new(|cx| {
18531            let key = &mut key.1;
18532            let mut multibuffer = MultiBuffer::new(capability);
18533            for (buffer, mut ranges_for_buffer) in locations {
18534                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18535                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18536                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18537                    PathKey::for_buffer(&buffer, cx),
18538                    buffer.clone(),
18539                    ranges_for_buffer,
18540                    multibuffer_context_lines(cx),
18541                    cx,
18542                );
18543                ranges.extend(new_ranges)
18544            }
18545
18546            multibuffer.with_title(title)
18547        });
18548        let existing = workspace.active_pane().update(cx, |pane, cx| {
18549            pane.items()
18550                .filter_map(|item| item.downcast::<Editor>())
18551                .find(|editor| {
18552                    editor
18553                        .read(cx)
18554                        .lookup_key
18555                        .as_ref()
18556                        .and_then(|it| {
18557                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18558                        })
18559                        .is_some_and(|it| *it == key)
18560                })
18561        });
18562        let was_existing = existing.is_some();
18563        let editor = existing.unwrap_or_else(|| {
18564            cx.new(|cx| {
18565                let mut editor = Editor::for_multibuffer(
18566                    excerpt_buffer,
18567                    Some(workspace.project().clone()),
18568                    window,
18569                    cx,
18570                );
18571                editor.lookup_key = Some(Box::new(key));
18572                editor
18573            })
18574        });
18575        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18576            MultibufferSelectionMode::First => {
18577                if let Some(first_range) = ranges.first() {
18578                    editor.change_selections(
18579                        SelectionEffects::no_scroll(),
18580                        window,
18581                        cx,
18582                        |selections| {
18583                            selections.clear_disjoint();
18584                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18585                        },
18586                    );
18587                }
18588                editor.highlight_background(
18589                    HighlightKey::Editor,
18590                    &ranges,
18591                    |_, theme| theme.colors().editor_highlighted_line_background,
18592                    cx,
18593                );
18594            }
18595            MultibufferSelectionMode::All => {
18596                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18597                    selections.clear_disjoint();
18598                    selections.select_anchor_ranges(ranges);
18599                });
18600            }
18601        });
18602
18603        let item = Box::new(editor.clone());
18604
18605        let pane = if split {
18606            workspace.adjacent_pane(window, cx)
18607        } else {
18608            workspace.active_pane().clone()
18609        };
18610        let activate_pane = split;
18611
18612        let mut destination_index = None;
18613        pane.update(cx, |pane, cx| {
18614            if allow_preview && !was_existing {
18615                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18616            }
18617            if was_existing && !allow_preview {
18618                pane.unpreview_item_if_preview(item.item_id());
18619            }
18620            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18621        });
18622
18623        Some((editor, pane))
18624    }
18625
18626    pub fn rename(
18627        &mut self,
18628        _: &Rename,
18629        window: &mut Window,
18630        cx: &mut Context<Self>,
18631    ) -> Option<Task<Result<()>>> {
18632        use language::ToOffset as _;
18633
18634        let provider = self.semantics_provider.clone()?;
18635        let selection = self.selections.newest_anchor().clone();
18636        let (cursor_buffer, cursor_buffer_position) = self
18637            .buffer
18638            .read(cx)
18639            .text_anchor_for_position(selection.head(), cx)?;
18640        let (tail_buffer, cursor_buffer_position_end) = self
18641            .buffer
18642            .read(cx)
18643            .text_anchor_for_position(selection.tail(), cx)?;
18644        if tail_buffer != cursor_buffer {
18645            return None;
18646        }
18647
18648        let snapshot = cursor_buffer.read(cx).snapshot();
18649        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18650        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18651        let prepare_rename = provider
18652            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18653            .unwrap_or_else(|| Task::ready(Ok(None)));
18654        drop(snapshot);
18655
18656        Some(cx.spawn_in(window, async move |this, cx| {
18657            let rename_range = if let Some(range) = prepare_rename.await? {
18658                Some(range)
18659            } else {
18660                this.update(cx, |this, cx| {
18661                    let buffer = this.buffer.read(cx).snapshot(cx);
18662                    let mut buffer_highlights = this
18663                        .document_highlights_for_position(selection.head(), &buffer)
18664                        .filter(|highlight| {
18665                            highlight.start.excerpt_id == selection.head().excerpt_id
18666                                && highlight.end.excerpt_id == selection.head().excerpt_id
18667                        });
18668                    buffer_highlights
18669                        .next()
18670                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18671                })?
18672            };
18673            if let Some(rename_range) = rename_range {
18674                this.update_in(cx, |this, window, cx| {
18675                    let snapshot = cursor_buffer.read(cx).snapshot();
18676                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18677                    let cursor_offset_in_rename_range =
18678                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18679                    let cursor_offset_in_rename_range_end =
18680                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18681
18682                    this.take_rename(false, window, cx);
18683                    let buffer = this.buffer.read(cx).read(cx);
18684                    let cursor_offset = selection.head().to_offset(&buffer);
18685                    let rename_start =
18686                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18687                    let rename_end = rename_start + rename_buffer_range.len();
18688                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18689                    let mut old_highlight_id = None;
18690                    let old_name: Arc<str> = buffer
18691                        .chunks(rename_start..rename_end, true)
18692                        .map(|chunk| {
18693                            if old_highlight_id.is_none() {
18694                                old_highlight_id = chunk.syntax_highlight_id;
18695                            }
18696                            chunk.text
18697                        })
18698                        .collect::<String>()
18699                        .into();
18700
18701                    drop(buffer);
18702
18703                    // Position the selection in the rename editor so that it matches the current selection.
18704                    this.show_local_selections = false;
18705                    let rename_editor = cx.new(|cx| {
18706                        let mut editor = Editor::single_line(window, cx);
18707                        editor.buffer.update(cx, |buffer, cx| {
18708                            buffer.edit(
18709                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18710                                None,
18711                                cx,
18712                            )
18713                        });
18714                        let cursor_offset_in_rename_range =
18715                            MultiBufferOffset(cursor_offset_in_rename_range);
18716                        let cursor_offset_in_rename_range_end =
18717                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18718                        let rename_selection_range = match cursor_offset_in_rename_range
18719                            .cmp(&cursor_offset_in_rename_range_end)
18720                        {
18721                            Ordering::Equal => {
18722                                editor.select_all(&SelectAll, window, cx);
18723                                return editor;
18724                            }
18725                            Ordering::Less => {
18726                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18727                            }
18728                            Ordering::Greater => {
18729                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18730                            }
18731                        };
18732                        if rename_selection_range.end.0 > old_name.len() {
18733                            editor.select_all(&SelectAll, window, cx);
18734                        } else {
18735                            editor.change_selections(Default::default(), window, cx, |s| {
18736                                s.select_ranges([rename_selection_range]);
18737                            });
18738                        }
18739                        editor
18740                    });
18741                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18742                        if e == &EditorEvent::Focused {
18743                            cx.emit(EditorEvent::FocusedIn)
18744                        }
18745                    })
18746                    .detach();
18747
18748                    let write_highlights =
18749                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18750                    let read_highlights =
18751                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18752                    let ranges = write_highlights
18753                        .iter()
18754                        .flat_map(|(_, ranges)| ranges.iter())
18755                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18756                        .cloned()
18757                        .collect();
18758
18759                    this.highlight_text(
18760                        HighlightKey::Rename,
18761                        ranges,
18762                        HighlightStyle {
18763                            fade_out: Some(0.6),
18764                            ..Default::default()
18765                        },
18766                        cx,
18767                    );
18768                    let rename_focus_handle = rename_editor.focus_handle(cx);
18769                    window.focus(&rename_focus_handle, cx);
18770                    let block_id = this.insert_blocks(
18771                        [BlockProperties {
18772                            style: BlockStyle::Flex,
18773                            placement: BlockPlacement::Below(range.start),
18774                            height: Some(1),
18775                            render: Arc::new({
18776                                let rename_editor = rename_editor.clone();
18777                                move |cx: &mut BlockContext| {
18778                                    let mut text_style = cx.editor_style.text.clone();
18779                                    if let Some(highlight_style) = old_highlight_id
18780                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18781                                    {
18782                                        text_style = text_style.highlight(highlight_style);
18783                                    }
18784                                    div()
18785                                        .block_mouse_except_scroll()
18786                                        .pl(cx.anchor_x)
18787                                        .child(EditorElement::new(
18788                                            &rename_editor,
18789                                            EditorStyle {
18790                                                background: cx.theme().system().transparent,
18791                                                local_player: cx.editor_style.local_player,
18792                                                text: text_style,
18793                                                scrollbar_width: cx.editor_style.scrollbar_width,
18794                                                syntax: cx.editor_style.syntax.clone(),
18795                                                status: cx.editor_style.status.clone(),
18796                                                inlay_hints_style: HighlightStyle {
18797                                                    font_weight: Some(FontWeight::BOLD),
18798                                                    ..make_inlay_hints_style(cx.app)
18799                                                },
18800                                                edit_prediction_styles: make_suggestion_styles(
18801                                                    cx.app,
18802                                                ),
18803                                                ..EditorStyle::default()
18804                                            },
18805                                        ))
18806                                        .into_any_element()
18807                                }
18808                            }),
18809                            priority: 0,
18810                        }],
18811                        Some(Autoscroll::fit()),
18812                        cx,
18813                    )[0];
18814                    this.pending_rename = Some(RenameState {
18815                        range,
18816                        old_name,
18817                        editor: rename_editor,
18818                        block_id,
18819                    });
18820                })?;
18821            }
18822
18823            Ok(())
18824        }))
18825    }
18826
18827    pub fn confirm_rename(
18828        &mut self,
18829        _: &ConfirmRename,
18830        window: &mut Window,
18831        cx: &mut Context<Self>,
18832    ) -> Option<Task<Result<()>>> {
18833        let rename = self.take_rename(false, window, cx)?;
18834        let workspace = self.workspace()?.downgrade();
18835        let (buffer, start) = self
18836            .buffer
18837            .read(cx)
18838            .text_anchor_for_position(rename.range.start, cx)?;
18839        let (end_buffer, _) = self
18840            .buffer
18841            .read(cx)
18842            .text_anchor_for_position(rename.range.end, cx)?;
18843        if buffer != end_buffer {
18844            return None;
18845        }
18846
18847        let old_name = rename.old_name;
18848        let new_name = rename.editor.read(cx).text(cx);
18849
18850        let rename = self.semantics_provider.as_ref()?.perform_rename(
18851            &buffer,
18852            start,
18853            new_name.clone(),
18854            cx,
18855        )?;
18856
18857        Some(cx.spawn_in(window, async move |editor, cx| {
18858            let project_transaction = rename.await?;
18859            Self::open_project_transaction(
18860                &editor,
18861                workspace,
18862                project_transaction,
18863                format!("Rename: {}{}", old_name, new_name),
18864                cx,
18865            )
18866            .await?;
18867
18868            editor.update(cx, |editor, cx| {
18869                editor.refresh_document_highlights(cx);
18870            })?;
18871            Ok(())
18872        }))
18873    }
18874
18875    fn take_rename(
18876        &mut self,
18877        moving_cursor: bool,
18878        window: &mut Window,
18879        cx: &mut Context<Self>,
18880    ) -> Option<RenameState> {
18881        let rename = self.pending_rename.take()?;
18882        if rename.editor.focus_handle(cx).is_focused(window) {
18883            window.focus(&self.focus_handle, cx);
18884        }
18885
18886        self.remove_blocks(
18887            [rename.block_id].into_iter().collect(),
18888            Some(Autoscroll::fit()),
18889            cx,
18890        );
18891        self.clear_highlights(HighlightKey::Rename, cx);
18892        self.show_local_selections = true;
18893
18894        if moving_cursor {
18895            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
18896                editor
18897                    .selections
18898                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
18899                    .head()
18900            });
18901
18902            // Update the selection to match the position of the selection inside
18903            // the rename editor.
18904            let snapshot = self.buffer.read(cx).read(cx);
18905            let rename_range = rename.range.to_offset(&snapshot);
18906            let cursor_in_editor = snapshot
18907                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
18908                .min(rename_range.end);
18909            drop(snapshot);
18910
18911            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
18912                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
18913            });
18914        } else {
18915            self.refresh_document_highlights(cx);
18916        }
18917
18918        Some(rename)
18919    }
18920
18921    pub fn pending_rename(&self) -> Option<&RenameState> {
18922        self.pending_rename.as_ref()
18923    }
18924
18925    fn format(
18926        &mut self,
18927        _: &Format,
18928        window: &mut Window,
18929        cx: &mut Context<Self>,
18930    ) -> Option<Task<Result<()>>> {
18931        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18932
18933        let project = match &self.project {
18934            Some(project) => project.clone(),
18935            None => return None,
18936        };
18937
18938        Some(self.perform_format(
18939            project,
18940            FormatTrigger::Manual,
18941            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
18942            window,
18943            cx,
18944        ))
18945    }
18946
18947    fn format_selections(
18948        &mut self,
18949        _: &FormatSelections,
18950        window: &mut Window,
18951        cx: &mut Context<Self>,
18952    ) -> Option<Task<Result<()>>> {
18953        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18954
18955        let project = match &self.project {
18956            Some(project) => project.clone(),
18957            None => return None,
18958        };
18959
18960        let ranges = self
18961            .selections
18962            .all_adjusted(&self.display_snapshot(cx))
18963            .into_iter()
18964            .map(|selection| selection.range())
18965            .collect_vec();
18966
18967        Some(self.perform_format(
18968            project,
18969            FormatTrigger::Manual,
18970            FormatTarget::Ranges(ranges),
18971            window,
18972            cx,
18973        ))
18974    }
18975
18976    fn perform_format(
18977        &mut self,
18978        project: Entity<Project>,
18979        trigger: FormatTrigger,
18980        target: FormatTarget,
18981        window: &mut Window,
18982        cx: &mut Context<Self>,
18983    ) -> Task<Result<()>> {
18984        let buffer = self.buffer.clone();
18985        let (buffers, target) = match target {
18986            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
18987            FormatTarget::Ranges(selection_ranges) => {
18988                let multi_buffer = buffer.read(cx);
18989                let snapshot = multi_buffer.read(cx);
18990                let mut buffers = HashSet::default();
18991                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
18992                    BTreeMap::new();
18993                for selection_range in selection_ranges {
18994                    for (buffer, buffer_range, _) in
18995                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
18996                    {
18997                        let buffer_id = buffer.remote_id();
18998                        let start = buffer.anchor_before(buffer_range.start);
18999                        let end = buffer.anchor_after(buffer_range.end);
19000                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19001                        buffer_id_to_ranges
19002                            .entry(buffer_id)
19003                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19004                            .or_insert_with(|| vec![start..end]);
19005                    }
19006                }
19007                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19008            }
19009        };
19010
19011        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19012        let selections_prev = transaction_id_prev
19013            .and_then(|transaction_id_prev| {
19014                // default to selections as they were after the last edit, if we have them,
19015                // instead of how they are now.
19016                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19017                // will take you back to where you made the last edit, instead of staying where you scrolled
19018                self.selection_history
19019                    .transaction(transaction_id_prev)
19020                    .map(|t| t.0.clone())
19021            })
19022            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19023
19024        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19025        let format = project.update(cx, |project, cx| {
19026            project.format(buffers, target, true, trigger, cx)
19027        });
19028
19029        cx.spawn_in(window, async move |editor, cx| {
19030            let transaction = futures::select_biased! {
19031                transaction = format.log_err().fuse() => transaction,
19032                () = timeout => {
19033                    log::warn!("timed out waiting for formatting");
19034                    None
19035                }
19036            };
19037
19038            buffer.update(cx, |buffer, cx| {
19039                if let Some(transaction) = transaction
19040                    && !buffer.is_singleton()
19041                {
19042                    buffer.push_transaction(&transaction.0, cx);
19043                }
19044                cx.notify();
19045            });
19046
19047            if let Some(transaction_id_now) =
19048                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19049            {
19050                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19051                if has_new_transaction {
19052                    editor
19053                        .update(cx, |editor, _| {
19054                            editor
19055                                .selection_history
19056                                .insert_transaction(transaction_id_now, selections_prev);
19057                        })
19058                        .ok();
19059                }
19060            }
19061
19062            Ok(())
19063        })
19064    }
19065
19066    fn organize_imports(
19067        &mut self,
19068        _: &OrganizeImports,
19069        window: &mut Window,
19070        cx: &mut Context<Self>,
19071    ) -> Option<Task<Result<()>>> {
19072        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19073        let project = match &self.project {
19074            Some(project) => project.clone(),
19075            None => return None,
19076        };
19077        Some(self.perform_code_action_kind(
19078            project,
19079            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19080            window,
19081            cx,
19082        ))
19083    }
19084
19085    fn perform_code_action_kind(
19086        &mut self,
19087        project: Entity<Project>,
19088        kind: CodeActionKind,
19089        window: &mut Window,
19090        cx: &mut Context<Self>,
19091    ) -> Task<Result<()>> {
19092        let buffer = self.buffer.clone();
19093        let buffers = buffer.read(cx).all_buffers();
19094        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19095        let apply_action = project.update(cx, |project, cx| {
19096            project.apply_code_action_kind(buffers, kind, true, cx)
19097        });
19098        cx.spawn_in(window, async move |_, cx| {
19099            let transaction = futures::select_biased! {
19100                () = timeout => {
19101                    log::warn!("timed out waiting for executing code action");
19102                    None
19103                }
19104                transaction = apply_action.log_err().fuse() => transaction,
19105            };
19106            buffer.update(cx, |buffer, cx| {
19107                // check if we need this
19108                if let Some(transaction) = transaction
19109                    && !buffer.is_singleton()
19110                {
19111                    buffer.push_transaction(&transaction.0, cx);
19112                }
19113                cx.notify();
19114            });
19115            Ok(())
19116        })
19117    }
19118
19119    pub fn restart_language_server(
19120        &mut self,
19121        _: &RestartLanguageServer,
19122        _: &mut Window,
19123        cx: &mut Context<Self>,
19124    ) {
19125        if let Some(project) = self.project.clone() {
19126            self.buffer.update(cx, |multi_buffer, cx| {
19127                project.update(cx, |project, cx| {
19128                    project.restart_language_servers_for_buffers(
19129                        multi_buffer.all_buffers().into_iter().collect(),
19130                        HashSet::default(),
19131                        cx,
19132                    );
19133                });
19134            })
19135        }
19136    }
19137
19138    pub fn stop_language_server(
19139        &mut self,
19140        _: &StopLanguageServer,
19141        _: &mut Window,
19142        cx: &mut Context<Self>,
19143    ) {
19144        if let Some(project) = self.project.clone() {
19145            self.buffer.update(cx, |multi_buffer, cx| {
19146                project.update(cx, |project, cx| {
19147                    project.stop_language_servers_for_buffers(
19148                        multi_buffer.all_buffers().into_iter().collect(),
19149                        HashSet::default(),
19150                        cx,
19151                    );
19152                });
19153            });
19154        }
19155    }
19156
19157    fn cancel_language_server_work(
19158        workspace: &mut Workspace,
19159        _: &actions::CancelLanguageServerWork,
19160        _: &mut Window,
19161        cx: &mut Context<Workspace>,
19162    ) {
19163        let project = workspace.project();
19164        let buffers = workspace
19165            .active_item(cx)
19166            .and_then(|item| item.act_as::<Editor>(cx))
19167            .map_or(HashSet::default(), |editor| {
19168                editor.read(cx).buffer.read(cx).all_buffers()
19169            });
19170        project.update(cx, |project, cx| {
19171            project.cancel_language_server_work_for_buffers(buffers, cx);
19172        });
19173    }
19174
19175    fn show_character_palette(
19176        &mut self,
19177        _: &ShowCharacterPalette,
19178        window: &mut Window,
19179        _: &mut Context<Self>,
19180    ) {
19181        window.show_character_palette();
19182    }
19183
19184    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19185        if !self.diagnostics_enabled() {
19186            return;
19187        }
19188
19189        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19190            let buffer = self.buffer.read(cx).snapshot(cx);
19191            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19192            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19193            let is_valid = buffer
19194                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19195                .any(|entry| {
19196                    entry.diagnostic.is_primary
19197                        && !entry.range.is_empty()
19198                        && entry.range.start == primary_range_start
19199                        && entry.diagnostic.message == active_diagnostics.active_message
19200                });
19201
19202            if !is_valid {
19203                self.dismiss_diagnostics(cx);
19204            }
19205        }
19206    }
19207
19208    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19209        match &self.active_diagnostics {
19210            ActiveDiagnostic::Group(group) => Some(group),
19211            _ => None,
19212        }
19213    }
19214
19215    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19216        if !self.diagnostics_enabled() {
19217            return;
19218        }
19219        self.dismiss_diagnostics(cx);
19220        self.active_diagnostics = ActiveDiagnostic::All;
19221    }
19222
19223    fn activate_diagnostics(
19224        &mut self,
19225        buffer_id: BufferId,
19226        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19227        window: &mut Window,
19228        cx: &mut Context<Self>,
19229    ) {
19230        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19231            return;
19232        }
19233        self.dismiss_diagnostics(cx);
19234        let snapshot = self.snapshot(window, cx);
19235        let buffer = self.buffer.read(cx).snapshot(cx);
19236        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19237            return;
19238        };
19239
19240        let diagnostic_group = buffer
19241            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19242            .collect::<Vec<_>>();
19243
19244        let language_registry = self
19245            .project()
19246            .map(|project| project.read(cx).languages().clone());
19247
19248        let blocks = renderer.render_group(
19249            diagnostic_group,
19250            buffer_id,
19251            snapshot,
19252            cx.weak_entity(),
19253            language_registry,
19254            cx,
19255        );
19256
19257        let blocks = self.display_map.update(cx, |display_map, cx| {
19258            display_map.insert_blocks(blocks, cx).into_iter().collect()
19259        });
19260        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19261            active_range: buffer.anchor_before(diagnostic.range.start)
19262                ..buffer.anchor_after(diagnostic.range.end),
19263            active_message: diagnostic.diagnostic.message.clone(),
19264            group_id: diagnostic.diagnostic.group_id,
19265            blocks,
19266        });
19267        cx.notify();
19268    }
19269
19270    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19271        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19272            return;
19273        };
19274
19275        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19276        if let ActiveDiagnostic::Group(group) = prev {
19277            self.display_map.update(cx, |display_map, cx| {
19278                display_map.remove_blocks(group.blocks, cx);
19279            });
19280            cx.notify();
19281        }
19282    }
19283
19284    /// Disable inline diagnostics rendering for this editor.
19285    pub fn disable_inline_diagnostics(&mut self) {
19286        self.inline_diagnostics_enabled = false;
19287        self.inline_diagnostics_update = Task::ready(());
19288        self.inline_diagnostics.clear();
19289    }
19290
19291    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19292        self.diagnostics_enabled = false;
19293        self.dismiss_diagnostics(cx);
19294        self.inline_diagnostics_update = Task::ready(());
19295        self.inline_diagnostics.clear();
19296    }
19297
19298    pub fn disable_word_completions(&mut self) {
19299        self.word_completions_enabled = false;
19300    }
19301
19302    pub fn diagnostics_enabled(&self) -> bool {
19303        self.diagnostics_enabled && self.lsp_data_enabled()
19304    }
19305
19306    pub fn inline_diagnostics_enabled(&self) -> bool {
19307        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19308    }
19309
19310    pub fn show_inline_diagnostics(&self) -> bool {
19311        self.show_inline_diagnostics
19312    }
19313
19314    pub fn toggle_inline_diagnostics(
19315        &mut self,
19316        _: &ToggleInlineDiagnostics,
19317        window: &mut Window,
19318        cx: &mut Context<Editor>,
19319    ) {
19320        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19321        self.refresh_inline_diagnostics(false, window, cx);
19322    }
19323
19324    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19325        self.diagnostics_max_severity = severity;
19326        self.display_map.update(cx, |display_map, _| {
19327            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19328        });
19329    }
19330
19331    pub fn toggle_diagnostics(
19332        &mut self,
19333        _: &ToggleDiagnostics,
19334        window: &mut Window,
19335        cx: &mut Context<Editor>,
19336    ) {
19337        if !self.diagnostics_enabled() {
19338            return;
19339        }
19340
19341        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19342            EditorSettings::get_global(cx)
19343                .diagnostics_max_severity
19344                .filter(|severity| severity != &DiagnosticSeverity::Off)
19345                .unwrap_or(DiagnosticSeverity::Hint)
19346        } else {
19347            DiagnosticSeverity::Off
19348        };
19349        self.set_max_diagnostics_severity(new_severity, cx);
19350        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19351            self.active_diagnostics = ActiveDiagnostic::None;
19352            self.inline_diagnostics_update = Task::ready(());
19353            self.inline_diagnostics.clear();
19354        } else {
19355            self.refresh_inline_diagnostics(false, window, cx);
19356        }
19357
19358        cx.notify();
19359    }
19360
19361    pub fn toggle_minimap(
19362        &mut self,
19363        _: &ToggleMinimap,
19364        window: &mut Window,
19365        cx: &mut Context<Editor>,
19366    ) {
19367        if self.supports_minimap(cx) {
19368            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19369        }
19370    }
19371
19372    fn refresh_inline_diagnostics(
19373        &mut self,
19374        debounce: bool,
19375        window: &mut Window,
19376        cx: &mut Context<Self>,
19377    ) {
19378        let max_severity = ProjectSettings::get_global(cx)
19379            .diagnostics
19380            .inline
19381            .max_severity
19382            .unwrap_or(self.diagnostics_max_severity);
19383
19384        if !self.inline_diagnostics_enabled()
19385            || !self.diagnostics_enabled()
19386            || !self.show_inline_diagnostics
19387            || max_severity == DiagnosticSeverity::Off
19388        {
19389            self.inline_diagnostics_update = Task::ready(());
19390            self.inline_diagnostics.clear();
19391            return;
19392        }
19393
19394        let debounce_ms = ProjectSettings::get_global(cx)
19395            .diagnostics
19396            .inline
19397            .update_debounce_ms;
19398        let debounce = if debounce && debounce_ms > 0 {
19399            Some(Duration::from_millis(debounce_ms))
19400        } else {
19401            None
19402        };
19403        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19404            if let Some(debounce) = debounce {
19405                cx.background_executor().timer(debounce).await;
19406            }
19407            let Some(snapshot) = editor.upgrade().map(|editor| {
19408                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19409            }) else {
19410                return;
19411            };
19412
19413            let new_inline_diagnostics = cx
19414                .background_spawn(async move {
19415                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19416                    for diagnostic_entry in
19417                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19418                    {
19419                        let message = diagnostic_entry
19420                            .diagnostic
19421                            .message
19422                            .split_once('\n')
19423                            .map(|(line, _)| line)
19424                            .map(SharedString::new)
19425                            .unwrap_or_else(|| {
19426                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19427                            });
19428                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19429                        let (Ok(i) | Err(i)) = inline_diagnostics
19430                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19431                        inline_diagnostics.insert(
19432                            i,
19433                            (
19434                                start_anchor,
19435                                InlineDiagnostic {
19436                                    message,
19437                                    group_id: diagnostic_entry.diagnostic.group_id,
19438                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19439                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19440                                    severity: diagnostic_entry.diagnostic.severity,
19441                                },
19442                            ),
19443                        );
19444                    }
19445                    inline_diagnostics
19446                })
19447                .await;
19448
19449            editor
19450                .update(cx, |editor, cx| {
19451                    editor.inline_diagnostics = new_inline_diagnostics;
19452                    cx.notify();
19453                })
19454                .ok();
19455        });
19456    }
19457
19458    fn pull_diagnostics(
19459        &mut self,
19460        buffer_id: BufferId,
19461        _window: &Window,
19462        cx: &mut Context<Self>,
19463    ) -> Option<()> {
19464        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19465        // skip any LSP updates for it.
19466
19467        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19468            return None;
19469        }
19470        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19471            .diagnostics
19472            .lsp_pull_diagnostics;
19473        if !pull_diagnostics_settings.enabled {
19474            return None;
19475        }
19476        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19477        let project = self.project()?.downgrade();
19478        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19479
19480        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19481            cx.background_executor().timer(debounce).await;
19482            if let Ok(task) = project.update(cx, |project, cx| {
19483                project.lsp_store().update(cx, |lsp_store, cx| {
19484                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19485                })
19486            }) {
19487                task.await.log_err();
19488            }
19489            project
19490                .update(cx, |project, cx| {
19491                    project.lsp_store().update(cx, |lsp_store, cx| {
19492                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19493                    })
19494                })
19495                .log_err();
19496        });
19497
19498        Some(())
19499    }
19500
19501    pub fn set_selections_from_remote(
19502        &mut self,
19503        selections: Vec<Selection<Anchor>>,
19504        pending_selection: Option<Selection<Anchor>>,
19505        window: &mut Window,
19506        cx: &mut Context<Self>,
19507    ) {
19508        let old_cursor_position = self.selections.newest_anchor().head();
19509        self.selections
19510            .change_with(&self.display_snapshot(cx), |s| {
19511                s.select_anchors(selections);
19512                if let Some(pending_selection) = pending_selection {
19513                    s.set_pending(pending_selection, SelectMode::Character);
19514                } else {
19515                    s.clear_pending();
19516                }
19517            });
19518        self.selections_did_change(
19519            false,
19520            &old_cursor_position,
19521            SelectionEffects::default(),
19522            window,
19523            cx,
19524        );
19525    }
19526
19527    pub fn transact(
19528        &mut self,
19529        window: &mut Window,
19530        cx: &mut Context<Self>,
19531        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19532    ) -> Option<TransactionId> {
19533        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19534            this.start_transaction_at(Instant::now(), window, cx);
19535            update(this, window, cx);
19536            this.end_transaction_at(Instant::now(), cx)
19537        })
19538    }
19539
19540    pub fn start_transaction_at(
19541        &mut self,
19542        now: Instant,
19543        window: &mut Window,
19544        cx: &mut Context<Self>,
19545    ) -> Option<TransactionId> {
19546        self.end_selection(window, cx);
19547        if let Some(tx_id) = self
19548            .buffer
19549            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19550        {
19551            self.selection_history
19552                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19553            cx.emit(EditorEvent::TransactionBegun {
19554                transaction_id: tx_id,
19555            });
19556            Some(tx_id)
19557        } else {
19558            None
19559        }
19560    }
19561
19562    pub fn end_transaction_at(
19563        &mut self,
19564        now: Instant,
19565        cx: &mut Context<Self>,
19566    ) -> Option<TransactionId> {
19567        if let Some(transaction_id) = self
19568            .buffer
19569            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19570        {
19571            if let Some((_, end_selections)) =
19572                self.selection_history.transaction_mut(transaction_id)
19573            {
19574                *end_selections = Some(self.selections.disjoint_anchors_arc());
19575            } else {
19576                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19577            }
19578
19579            cx.emit(EditorEvent::Edited { transaction_id });
19580            Some(transaction_id)
19581        } else {
19582            None
19583        }
19584    }
19585
19586    pub fn modify_transaction_selection_history(
19587        &mut self,
19588        transaction_id: TransactionId,
19589        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19590    ) -> bool {
19591        self.selection_history
19592            .transaction_mut(transaction_id)
19593            .map(modify)
19594            .is_some()
19595    }
19596
19597    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19598        if self.selection_mark_mode {
19599            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19600                s.move_with(&mut |_, sel| {
19601                    sel.collapse_to(sel.head(), SelectionGoal::None);
19602                });
19603            })
19604        }
19605        self.selection_mark_mode = true;
19606        cx.notify();
19607    }
19608
19609    pub fn swap_selection_ends(
19610        &mut self,
19611        _: &actions::SwapSelectionEnds,
19612        window: &mut Window,
19613        cx: &mut Context<Self>,
19614    ) {
19615        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19616            s.move_with(&mut |_, sel| {
19617                if sel.start != sel.end {
19618                    sel.reversed = !sel.reversed
19619                }
19620            });
19621        });
19622        self.request_autoscroll(Autoscroll::newest(), cx);
19623        cx.notify();
19624    }
19625
19626    pub fn toggle_focus(
19627        workspace: &mut Workspace,
19628        _: &actions::ToggleFocus,
19629        window: &mut Window,
19630        cx: &mut Context<Workspace>,
19631    ) {
19632        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19633            return;
19634        };
19635        workspace.activate_item(&item, true, true, window, cx);
19636    }
19637
19638    pub fn toggle_fold(
19639        &mut self,
19640        _: &actions::ToggleFold,
19641        window: &mut Window,
19642        cx: &mut Context<Self>,
19643    ) {
19644        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19645            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19646            let selection = self.selections.newest::<Point>(&display_map);
19647
19648            let range = if selection.is_empty() {
19649                let point = selection.head().to_display_point(&display_map);
19650                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19651                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19652                    .to_point(&display_map);
19653                start..end
19654            } else {
19655                selection.range()
19656            };
19657            if display_map.folds_in_range(range).next().is_some() {
19658                self.unfold_lines(&Default::default(), window, cx)
19659            } else {
19660                self.fold(&Default::default(), window, cx)
19661            }
19662        } else {
19663            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19664            let buffer_ids: HashSet<_> = self
19665                .selections
19666                .disjoint_anchor_ranges()
19667                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19668                .collect();
19669
19670            let should_unfold = buffer_ids
19671                .iter()
19672                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19673
19674            for buffer_id in buffer_ids {
19675                if should_unfold {
19676                    self.unfold_buffer(buffer_id, cx);
19677                } else {
19678                    self.fold_buffer(buffer_id, cx);
19679                }
19680            }
19681        }
19682    }
19683
19684    pub fn toggle_fold_recursive(
19685        &mut self,
19686        _: &actions::ToggleFoldRecursive,
19687        window: &mut Window,
19688        cx: &mut Context<Self>,
19689    ) {
19690        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19691
19692        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19693        let range = if selection.is_empty() {
19694            let point = selection.head().to_display_point(&display_map);
19695            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19696            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19697                .to_point(&display_map);
19698            start..end
19699        } else {
19700            selection.range()
19701        };
19702        if display_map.folds_in_range(range).next().is_some() {
19703            self.unfold_recursive(&Default::default(), window, cx)
19704        } else {
19705            self.fold_recursive(&Default::default(), window, cx)
19706        }
19707    }
19708
19709    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19710        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19711            let mut to_fold = Vec::new();
19712            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19713            let selections = self.selections.all_adjusted(&display_map);
19714
19715            for selection in selections {
19716                let range = selection.range().sorted();
19717                let buffer_start_row = range.start.row;
19718
19719                if range.start.row != range.end.row {
19720                    let mut found = false;
19721                    let mut row = range.start.row;
19722                    while row <= range.end.row {
19723                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19724                        {
19725                            found = true;
19726                            row = crease.range().end.row + 1;
19727                            to_fold.push(crease);
19728                        } else {
19729                            row += 1
19730                        }
19731                    }
19732                    if found {
19733                        continue;
19734                    }
19735                }
19736
19737                for row in (0..=range.start.row).rev() {
19738                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19739                        && crease.range().end.row >= buffer_start_row
19740                    {
19741                        to_fold.push(crease);
19742                        if row <= range.start.row {
19743                            break;
19744                        }
19745                    }
19746                }
19747            }
19748
19749            self.fold_creases(to_fold, true, window, cx);
19750        } else {
19751            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19752            let buffer_ids = self
19753                .selections
19754                .disjoint_anchor_ranges()
19755                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19756                .collect::<HashSet<_>>();
19757            for buffer_id in buffer_ids {
19758                self.fold_buffer(buffer_id, cx);
19759            }
19760        }
19761    }
19762
19763    pub fn toggle_fold_all(
19764        &mut self,
19765        _: &actions::ToggleFoldAll,
19766        window: &mut Window,
19767        cx: &mut Context<Self>,
19768    ) {
19769        let has_folds = if self.buffer.read(cx).is_singleton() {
19770            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19771            let has_folds = display_map
19772                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19773                .next()
19774                .is_some();
19775            has_folds
19776        } else {
19777            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19778            let has_folds = buffer_ids
19779                .iter()
19780                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19781            has_folds
19782        };
19783
19784        if has_folds {
19785            self.unfold_all(&actions::UnfoldAll, window, cx);
19786        } else {
19787            self.fold_all(&actions::FoldAll, window, cx);
19788        }
19789    }
19790
19791    fn fold_at_level(
19792        &mut self,
19793        fold_at: &FoldAtLevel,
19794        window: &mut Window,
19795        cx: &mut Context<Self>,
19796    ) {
19797        if !self.buffer.read(cx).is_singleton() {
19798            return;
19799        }
19800
19801        let fold_at_level = fold_at.0;
19802        let snapshot = self.buffer.read(cx).snapshot(cx);
19803        let mut to_fold = Vec::new();
19804        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19805
19806        let row_ranges_to_keep: Vec<Range<u32>> = self
19807            .selections
19808            .all::<Point>(&self.display_snapshot(cx))
19809            .into_iter()
19810            .map(|sel| sel.start.row..sel.end.row)
19811            .collect();
19812
19813        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19814            while start_row < end_row {
19815                match self
19816                    .snapshot(window, cx)
19817                    .crease_for_buffer_row(MultiBufferRow(start_row))
19818                {
19819                    Some(crease) => {
19820                        let nested_start_row = crease.range().start.row + 1;
19821                        let nested_end_row = crease.range().end.row;
19822
19823                        if current_level < fold_at_level {
19824                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19825                        } else if current_level == fold_at_level {
19826                            // Fold iff there is no selection completely contained within the fold region
19827                            if !row_ranges_to_keep.iter().any(|selection| {
19828                                selection.end >= nested_start_row
19829                                    && selection.start <= nested_end_row
19830                            }) {
19831                                to_fold.push(crease);
19832                            }
19833                        }
19834
19835                        start_row = nested_end_row + 1;
19836                    }
19837                    None => start_row += 1,
19838                }
19839            }
19840        }
19841
19842        self.fold_creases(to_fold, true, window, cx);
19843    }
19844
19845    pub fn fold_at_level_1(
19846        &mut self,
19847        _: &actions::FoldAtLevel1,
19848        window: &mut Window,
19849        cx: &mut Context<Self>,
19850    ) {
19851        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
19852    }
19853
19854    pub fn fold_at_level_2(
19855        &mut self,
19856        _: &actions::FoldAtLevel2,
19857        window: &mut Window,
19858        cx: &mut Context<Self>,
19859    ) {
19860        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
19861    }
19862
19863    pub fn fold_at_level_3(
19864        &mut self,
19865        _: &actions::FoldAtLevel3,
19866        window: &mut Window,
19867        cx: &mut Context<Self>,
19868    ) {
19869        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
19870    }
19871
19872    pub fn fold_at_level_4(
19873        &mut self,
19874        _: &actions::FoldAtLevel4,
19875        window: &mut Window,
19876        cx: &mut Context<Self>,
19877    ) {
19878        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
19879    }
19880
19881    pub fn fold_at_level_5(
19882        &mut self,
19883        _: &actions::FoldAtLevel5,
19884        window: &mut Window,
19885        cx: &mut Context<Self>,
19886    ) {
19887        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
19888    }
19889
19890    pub fn fold_at_level_6(
19891        &mut self,
19892        _: &actions::FoldAtLevel6,
19893        window: &mut Window,
19894        cx: &mut Context<Self>,
19895    ) {
19896        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
19897    }
19898
19899    pub fn fold_at_level_7(
19900        &mut self,
19901        _: &actions::FoldAtLevel7,
19902        window: &mut Window,
19903        cx: &mut Context<Self>,
19904    ) {
19905        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
19906    }
19907
19908    pub fn fold_at_level_8(
19909        &mut self,
19910        _: &actions::FoldAtLevel8,
19911        window: &mut Window,
19912        cx: &mut Context<Self>,
19913    ) {
19914        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
19915    }
19916
19917    pub fn fold_at_level_9(
19918        &mut self,
19919        _: &actions::FoldAtLevel9,
19920        window: &mut Window,
19921        cx: &mut Context<Self>,
19922    ) {
19923        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
19924    }
19925
19926    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
19927        if self.buffer.read(cx).is_singleton() {
19928            let mut fold_ranges = Vec::new();
19929            let snapshot = self.buffer.read(cx).snapshot(cx);
19930
19931            for row in 0..snapshot.max_row().0 {
19932                if let Some(foldable_range) = self
19933                    .snapshot(window, cx)
19934                    .crease_for_buffer_row(MultiBufferRow(row))
19935                {
19936                    fold_ranges.push(foldable_range);
19937                }
19938            }
19939
19940            self.fold_creases(fold_ranges, true, window, cx);
19941        } else {
19942            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
19943                editor
19944                    .update_in(cx, |editor, _, cx| {
19945                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19946                            editor.fold_buffer(buffer_id, cx);
19947                        }
19948                    })
19949                    .ok();
19950            });
19951        }
19952    }
19953
19954    pub fn fold_function_bodies(
19955        &mut self,
19956        _: &actions::FoldFunctionBodies,
19957        window: &mut Window,
19958        cx: &mut Context<Self>,
19959    ) {
19960        let snapshot = self.buffer.read(cx).snapshot(cx);
19961
19962        let ranges = snapshot
19963            .text_object_ranges(
19964                MultiBufferOffset(0)..snapshot.len(),
19965                TreeSitterOptions::default(),
19966            )
19967            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
19968            .collect::<Vec<_>>();
19969
19970        let creases = ranges
19971            .into_iter()
19972            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
19973            .collect();
19974
19975        self.fold_creases(creases, true, window, cx);
19976    }
19977
19978    pub fn fold_recursive(
19979        &mut self,
19980        _: &actions::FoldRecursive,
19981        window: &mut Window,
19982        cx: &mut Context<Self>,
19983    ) {
19984        let mut to_fold = Vec::new();
19985        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19986        let selections = self.selections.all_adjusted(&display_map);
19987
19988        for selection in selections {
19989            let range = selection.range().sorted();
19990            let buffer_start_row = range.start.row;
19991
19992            if range.start.row != range.end.row {
19993                let mut found = false;
19994                for row in range.start.row..=range.end.row {
19995                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19996                        found = true;
19997                        to_fold.push(crease);
19998                    }
19999                }
20000                if found {
20001                    continue;
20002                }
20003            }
20004
20005            for row in (0..=range.start.row).rev() {
20006                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20007                    if crease.range().end.row >= buffer_start_row {
20008                        to_fold.push(crease);
20009                    } else {
20010                        break;
20011                    }
20012                }
20013            }
20014        }
20015
20016        self.fold_creases(to_fold, true, window, cx);
20017    }
20018
20019    pub fn fold_at(
20020        &mut self,
20021        buffer_row: MultiBufferRow,
20022        window: &mut Window,
20023        cx: &mut Context<Self>,
20024    ) {
20025        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20026
20027        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20028            let autoscroll = self
20029                .selections
20030                .all::<Point>(&display_map)
20031                .iter()
20032                .any(|selection| crease.range().overlaps(&selection.range()));
20033
20034            self.fold_creases(vec![crease], autoscroll, window, cx);
20035        }
20036    }
20037
20038    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20039        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20040            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20041            let buffer = display_map.buffer_snapshot();
20042            let selections = self.selections.all::<Point>(&display_map);
20043            let ranges = selections
20044                .iter()
20045                .map(|s| {
20046                    let range = s.display_range(&display_map).sorted();
20047                    let mut start = range.start.to_point(&display_map);
20048                    let mut end = range.end.to_point(&display_map);
20049                    start.column = 0;
20050                    end.column = buffer.line_len(MultiBufferRow(end.row));
20051                    start..end
20052                })
20053                .collect::<Vec<_>>();
20054
20055            self.unfold_ranges(&ranges, true, true, cx);
20056        } else {
20057            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20058            let buffer_ids = self
20059                .selections
20060                .disjoint_anchor_ranges()
20061                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20062                .collect::<HashSet<_>>();
20063            for buffer_id in buffer_ids {
20064                self.unfold_buffer(buffer_id, cx);
20065            }
20066        }
20067    }
20068
20069    pub fn unfold_recursive(
20070        &mut self,
20071        _: &UnfoldRecursive,
20072        _window: &mut Window,
20073        cx: &mut Context<Self>,
20074    ) {
20075        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20076        let selections = self.selections.all::<Point>(&display_map);
20077        let ranges = selections
20078            .iter()
20079            .map(|s| {
20080                let mut range = s.display_range(&display_map).sorted();
20081                *range.start.column_mut() = 0;
20082                *range.end.column_mut() = display_map.line_len(range.end.row());
20083                let start = range.start.to_point(&display_map);
20084                let end = range.end.to_point(&display_map);
20085                start..end
20086            })
20087            .collect::<Vec<_>>();
20088
20089        self.unfold_ranges(&ranges, true, true, cx);
20090    }
20091
20092    pub fn unfold_at(
20093        &mut self,
20094        buffer_row: MultiBufferRow,
20095        _window: &mut Window,
20096        cx: &mut Context<Self>,
20097    ) {
20098        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20099
20100        let intersection_range = Point::new(buffer_row.0, 0)
20101            ..Point::new(
20102                buffer_row.0,
20103                display_map.buffer_snapshot().line_len(buffer_row),
20104            );
20105
20106        let autoscroll = self
20107            .selections
20108            .all::<Point>(&display_map)
20109            .iter()
20110            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20111
20112        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20113    }
20114
20115    pub fn unfold_all(
20116        &mut self,
20117        _: &actions::UnfoldAll,
20118        _window: &mut Window,
20119        cx: &mut Context<Self>,
20120    ) {
20121        if self.buffer.read(cx).is_singleton() {
20122            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20123            self.unfold_ranges(
20124                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20125                true,
20126                true,
20127                cx,
20128            );
20129        } else {
20130            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20131                editor
20132                    .update(cx, |editor, cx| {
20133                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20134                            editor.unfold_buffer(buffer_id, cx);
20135                        }
20136                    })
20137                    .ok();
20138            });
20139        }
20140    }
20141
20142    pub fn fold_selected_ranges(
20143        &mut self,
20144        _: &FoldSelectedRanges,
20145        window: &mut Window,
20146        cx: &mut Context<Self>,
20147    ) {
20148        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20149        let selections = self.selections.all_adjusted(&display_map);
20150        let ranges = selections
20151            .into_iter()
20152            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20153            .collect::<Vec<_>>();
20154        self.fold_creases(ranges, true, window, cx);
20155    }
20156
20157    pub fn fold_ranges<T: ToOffset + Clone>(
20158        &mut self,
20159        ranges: Vec<Range<T>>,
20160        auto_scroll: bool,
20161        window: &mut Window,
20162        cx: &mut Context<Self>,
20163    ) {
20164        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20165        let ranges = ranges
20166            .into_iter()
20167            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20168            .collect::<Vec<_>>();
20169        self.fold_creases(ranges, auto_scroll, window, cx);
20170    }
20171
20172    pub fn fold_creases<T: ToOffset + Clone>(
20173        &mut self,
20174        creases: Vec<Crease<T>>,
20175        auto_scroll: bool,
20176        window: &mut Window,
20177        cx: &mut Context<Self>,
20178    ) {
20179        if creases.is_empty() {
20180            return;
20181        }
20182
20183        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20184
20185        if auto_scroll {
20186            self.request_autoscroll(Autoscroll::fit(), cx);
20187        }
20188
20189        cx.notify();
20190
20191        self.scrollbar_marker_state.dirty = true;
20192        self.update_data_on_scroll(window, cx);
20193        self.folds_did_change(cx);
20194    }
20195
20196    /// Removes any folds whose ranges intersect any of the given ranges.
20197    pub fn unfold_ranges<T: ToOffset + Clone>(
20198        &mut self,
20199        ranges: &[Range<T>],
20200        inclusive: bool,
20201        auto_scroll: bool,
20202        cx: &mut Context<Self>,
20203    ) {
20204        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20205            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20206        });
20207        self.folds_did_change(cx);
20208    }
20209
20210    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20211        self.fold_buffers([buffer_id], cx);
20212    }
20213
20214    pub fn fold_buffers(
20215        &mut self,
20216        buffer_ids: impl IntoIterator<Item = BufferId>,
20217        cx: &mut Context<Self>,
20218    ) {
20219        if self.buffer().read(cx).is_singleton() {
20220            return;
20221        }
20222
20223        let ids_to_fold: Vec<BufferId> = buffer_ids
20224            .into_iter()
20225            .filter(|id| !self.is_buffer_folded(*id, cx))
20226            .collect();
20227
20228        if ids_to_fold.is_empty() {
20229            return;
20230        }
20231
20232        let mut all_folded_excerpt_ids = Vec::new();
20233        for buffer_id in &ids_to_fold {
20234            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20235            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20236        }
20237
20238        self.display_map.update(cx, |display_map, cx| {
20239            display_map.fold_buffers(ids_to_fold.clone(), cx)
20240        });
20241
20242        let snapshot = self.display_snapshot(cx);
20243        self.selections.change_with(&snapshot, |selections| {
20244            for buffer_id in ids_to_fold {
20245                selections.remove_selections_from_buffer(buffer_id);
20246            }
20247        });
20248
20249        cx.emit(EditorEvent::BufferFoldToggled {
20250            ids: all_folded_excerpt_ids,
20251            folded: true,
20252        });
20253        cx.notify();
20254    }
20255
20256    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20257        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20258            return;
20259        }
20260        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20261        self.display_map.update(cx, |display_map, cx| {
20262            display_map.unfold_buffers([buffer_id], cx);
20263        });
20264        cx.emit(EditorEvent::BufferFoldToggled {
20265            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20266            folded: false,
20267        });
20268        cx.notify();
20269    }
20270
20271    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20272        self.display_map.read(cx).is_buffer_folded(buffer)
20273    }
20274
20275    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20276        if self.buffer().read(cx).is_singleton() {
20277            return false;
20278        }
20279        !self.folded_buffers(cx).is_empty()
20280    }
20281
20282    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20283        self.display_map.read(cx).folded_buffers()
20284    }
20285
20286    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20287        self.display_map.update(cx, |display_map, cx| {
20288            display_map.disable_header_for_buffer(buffer_id, cx);
20289        });
20290        cx.notify();
20291    }
20292
20293    /// Removes any folds with the given ranges.
20294    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20295        &mut self,
20296        ranges: &[Range<T>],
20297        type_id: TypeId,
20298        auto_scroll: bool,
20299        cx: &mut Context<Self>,
20300    ) {
20301        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20302            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20303        });
20304        self.folds_did_change(cx);
20305    }
20306
20307    fn remove_folds_with<T: ToOffset + Clone>(
20308        &mut self,
20309        ranges: &[Range<T>],
20310        auto_scroll: bool,
20311        cx: &mut Context<Self>,
20312        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20313    ) {
20314        if ranges.is_empty() {
20315            return;
20316        }
20317
20318        let mut buffers_affected = HashSet::default();
20319        let multi_buffer = self.buffer().read(cx);
20320        for range in ranges {
20321            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20322                buffers_affected.insert(buffer.read(cx).remote_id());
20323            };
20324        }
20325
20326        self.display_map.update(cx, update);
20327
20328        if auto_scroll {
20329            self.request_autoscroll(Autoscroll::fit(), cx);
20330        }
20331
20332        cx.notify();
20333        self.scrollbar_marker_state.dirty = true;
20334        self.active_indent_guides_state.dirty = true;
20335    }
20336
20337    pub fn update_renderer_widths(
20338        &mut self,
20339        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20340        cx: &mut Context<Self>,
20341    ) -> bool {
20342        self.display_map
20343            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20344    }
20345
20346    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20347        self.display_map.read(cx).fold_placeholder.clone()
20348    }
20349
20350    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20351        self.buffer.update(cx, |buffer, cx| {
20352            buffer.set_all_diff_hunks_expanded(cx);
20353        });
20354    }
20355
20356    pub fn expand_all_diff_hunks(
20357        &mut self,
20358        _: &ExpandAllDiffHunks,
20359        _window: &mut Window,
20360        cx: &mut Context<Self>,
20361    ) {
20362        self.buffer.update(cx, |buffer, cx| {
20363            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20364        });
20365    }
20366
20367    pub fn collapse_all_diff_hunks(
20368        &mut self,
20369        _: &CollapseAllDiffHunks,
20370        _window: &mut Window,
20371        cx: &mut Context<Self>,
20372    ) {
20373        self.buffer.update(cx, |buffer, cx| {
20374            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20375        });
20376    }
20377
20378    pub fn toggle_selected_diff_hunks(
20379        &mut self,
20380        _: &ToggleSelectedDiffHunks,
20381        _window: &mut Window,
20382        cx: &mut Context<Self>,
20383    ) {
20384        let ranges: Vec<_> = self
20385            .selections
20386            .disjoint_anchors()
20387            .iter()
20388            .map(|s| s.range())
20389            .collect();
20390        self.toggle_diff_hunks_in_ranges(ranges, cx);
20391    }
20392
20393    pub fn diff_hunks_in_ranges<'a>(
20394        &'a self,
20395        ranges: &'a [Range<Anchor>],
20396        buffer: &'a MultiBufferSnapshot,
20397    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20398        ranges.iter().flat_map(move |range| {
20399            let end_excerpt_id = range.end.excerpt_id;
20400            let range = range.to_point(buffer);
20401            let mut peek_end = range.end;
20402            if range.end.row < buffer.max_row().0 {
20403                peek_end = Point::new(range.end.row + 1, 0);
20404            }
20405            buffer
20406                .diff_hunks_in_range(range.start..peek_end)
20407                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20408        })
20409    }
20410
20411    pub fn has_stageable_diff_hunks_in_ranges(
20412        &self,
20413        ranges: &[Range<Anchor>],
20414        snapshot: &MultiBufferSnapshot,
20415    ) -> bool {
20416        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20417        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20418    }
20419
20420    pub fn toggle_staged_selected_diff_hunks(
20421        &mut self,
20422        _: &::git::ToggleStaged,
20423        _: &mut Window,
20424        cx: &mut Context<Self>,
20425    ) {
20426        let snapshot = self.buffer.read(cx).snapshot(cx);
20427        let ranges: Vec<_> = self
20428            .selections
20429            .disjoint_anchors()
20430            .iter()
20431            .map(|s| s.range())
20432            .collect();
20433        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20434        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20435    }
20436
20437    pub fn set_render_diff_hunk_controls(
20438        &mut self,
20439        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20440        cx: &mut Context<Self>,
20441    ) {
20442        self.render_diff_hunk_controls = render_diff_hunk_controls;
20443        cx.notify();
20444    }
20445
20446    pub fn stage_and_next(
20447        &mut self,
20448        _: &::git::StageAndNext,
20449        window: &mut Window,
20450        cx: &mut Context<Self>,
20451    ) {
20452        self.do_stage_or_unstage_and_next(true, window, cx);
20453    }
20454
20455    pub fn unstage_and_next(
20456        &mut self,
20457        _: &::git::UnstageAndNext,
20458        window: &mut Window,
20459        cx: &mut Context<Self>,
20460    ) {
20461        self.do_stage_or_unstage_and_next(false, window, cx);
20462    }
20463
20464    pub fn stage_or_unstage_diff_hunks(
20465        &mut self,
20466        stage: bool,
20467        ranges: Vec<Range<Anchor>>,
20468        cx: &mut Context<Self>,
20469    ) {
20470        if self.delegate_stage_and_restore {
20471            let snapshot = self.buffer.read(cx).snapshot(cx);
20472            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20473            if !hunks.is_empty() {
20474                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20475            }
20476            return;
20477        }
20478        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20479        cx.spawn(async move |this, cx| {
20480            task.await?;
20481            this.update(cx, |this, cx| {
20482                let snapshot = this.buffer.read(cx).snapshot(cx);
20483                let chunk_by = this
20484                    .diff_hunks_in_ranges(&ranges, &snapshot)
20485                    .chunk_by(|hunk| hunk.buffer_id);
20486                for (buffer_id, hunks) in &chunk_by {
20487                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20488                }
20489            })
20490        })
20491        .detach_and_log_err(cx);
20492    }
20493
20494    fn save_buffers_for_ranges_if_needed(
20495        &mut self,
20496        ranges: &[Range<Anchor>],
20497        cx: &mut Context<Editor>,
20498    ) -> Task<Result<()>> {
20499        let multibuffer = self.buffer.read(cx);
20500        let snapshot = multibuffer.read(cx);
20501        let buffer_ids: HashSet<_> = ranges
20502            .iter()
20503            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20504            .collect();
20505        drop(snapshot);
20506
20507        let mut buffers = HashSet::default();
20508        for buffer_id in buffer_ids {
20509            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20510                let buffer = buffer_entity.read(cx);
20511                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20512                {
20513                    buffers.insert(buffer_entity);
20514                }
20515            }
20516        }
20517
20518        if let Some(project) = &self.project {
20519            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20520        } else {
20521            Task::ready(Ok(()))
20522        }
20523    }
20524
20525    fn do_stage_or_unstage_and_next(
20526        &mut self,
20527        stage: bool,
20528        window: &mut Window,
20529        cx: &mut Context<Self>,
20530    ) {
20531        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20532
20533        if ranges.iter().any(|range| range.start != range.end) {
20534            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20535            return;
20536        }
20537
20538        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20539
20540        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20541        let wrap_around = !all_diff_hunks_expanded;
20542        let snapshot = self.snapshot(window, cx);
20543        let position = self
20544            .selections
20545            .newest::<Point>(&snapshot.display_snapshot)
20546            .head();
20547
20548        self.go_to_hunk_before_or_after_position(
20549            &snapshot,
20550            position,
20551            Direction::Next,
20552            wrap_around,
20553            window,
20554            cx,
20555        );
20556    }
20557
20558    pub(crate) fn do_stage_or_unstage(
20559        &self,
20560        stage: bool,
20561        buffer_id: BufferId,
20562        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20563        cx: &mut App,
20564    ) -> Option<()> {
20565        let project = self.project()?;
20566        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20567        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20568        let buffer_snapshot = buffer.read(cx).snapshot();
20569        let file_exists = buffer_snapshot
20570            .file()
20571            .is_some_and(|file| file.disk_state().exists());
20572        diff.update(cx, |diff, cx| {
20573            diff.stage_or_unstage_hunks(
20574                stage,
20575                &hunks
20576                    .map(|hunk| buffer_diff::DiffHunk {
20577                        buffer_range: hunk.buffer_range,
20578                        // We don't need to pass in word diffs here because they're only used for rendering and
20579                        // this function changes internal state
20580                        base_word_diffs: Vec::default(),
20581                        buffer_word_diffs: Vec::default(),
20582                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20583                            ..hunk.diff_base_byte_range.end.0,
20584                        secondary_status: hunk.status.secondary,
20585                        range: Point::zero()..Point::zero(), // unused
20586                    })
20587                    .collect::<Vec<_>>(),
20588                &buffer_snapshot,
20589                file_exists,
20590                cx,
20591            )
20592        });
20593        None
20594    }
20595
20596    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20597        let ranges: Vec<_> = self
20598            .selections
20599            .disjoint_anchors()
20600            .iter()
20601            .map(|s| s.range())
20602            .collect();
20603        self.buffer
20604            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20605    }
20606
20607    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20608        self.buffer.update(cx, |buffer, cx| {
20609            let ranges = vec![Anchor::min()..Anchor::max()];
20610            if !buffer.all_diff_hunks_expanded()
20611                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20612            {
20613                buffer.collapse_diff_hunks(ranges, cx);
20614                true
20615            } else {
20616                false
20617            }
20618        })
20619    }
20620
20621    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20622        if self.buffer.read(cx).all_diff_hunks_expanded() {
20623            return true;
20624        }
20625        let ranges = vec![Anchor::min()..Anchor::max()];
20626        self.buffer
20627            .read(cx)
20628            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20629    }
20630
20631    fn toggle_diff_hunks_in_ranges(
20632        &mut self,
20633        ranges: Vec<Range<Anchor>>,
20634        cx: &mut Context<Editor>,
20635    ) {
20636        self.buffer.update(cx, |buffer, cx| {
20637            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20638            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20639        })
20640    }
20641
20642    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20643        self.buffer.update(cx, |buffer, cx| {
20644            buffer.toggle_single_diff_hunk(range, cx);
20645        })
20646    }
20647
20648    pub(crate) fn apply_all_diff_hunks(
20649        &mut self,
20650        _: &ApplyAllDiffHunks,
20651        window: &mut Window,
20652        cx: &mut Context<Self>,
20653    ) {
20654        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20655
20656        let buffers = self.buffer.read(cx).all_buffers();
20657        for branch_buffer in buffers {
20658            branch_buffer.update(cx, |branch_buffer, cx| {
20659                branch_buffer.merge_into_base(Vec::new(), cx);
20660            });
20661        }
20662
20663        if let Some(project) = self.project.clone() {
20664            self.save(
20665                SaveOptions {
20666                    format: true,
20667                    autosave: false,
20668                },
20669                project,
20670                window,
20671                cx,
20672            )
20673            .detach_and_log_err(cx);
20674        }
20675    }
20676
20677    pub(crate) fn apply_selected_diff_hunks(
20678        &mut self,
20679        _: &ApplyDiffHunk,
20680        window: &mut Window,
20681        cx: &mut Context<Self>,
20682    ) {
20683        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20684        let snapshot = self.snapshot(window, cx);
20685        let hunks = snapshot.hunks_for_ranges(
20686            self.selections
20687                .all(&snapshot.display_snapshot)
20688                .into_iter()
20689                .map(|selection| selection.range()),
20690        );
20691        let mut ranges_by_buffer = HashMap::default();
20692        self.transact(window, cx, |editor, _window, cx| {
20693            for hunk in hunks {
20694                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20695                    ranges_by_buffer
20696                        .entry(buffer.clone())
20697                        .or_insert_with(Vec::new)
20698                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20699                }
20700            }
20701
20702            for (buffer, ranges) in ranges_by_buffer {
20703                buffer.update(cx, |buffer, cx| {
20704                    buffer.merge_into_base(ranges, cx);
20705                });
20706            }
20707        });
20708
20709        if let Some(project) = self.project.clone() {
20710            self.save(
20711                SaveOptions {
20712                    format: true,
20713                    autosave: false,
20714                },
20715                project,
20716                window,
20717                cx,
20718            )
20719            .detach_and_log_err(cx);
20720        }
20721    }
20722
20723    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20724        if hovered != self.gutter_hovered {
20725            self.gutter_hovered = hovered;
20726            cx.notify();
20727        }
20728    }
20729
20730    pub fn insert_blocks(
20731        &mut self,
20732        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20733        autoscroll: Option<Autoscroll>,
20734        cx: &mut Context<Self>,
20735    ) -> Vec<CustomBlockId> {
20736        let blocks = self
20737            .display_map
20738            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20739        if let Some(autoscroll) = autoscroll {
20740            self.request_autoscroll(autoscroll, cx);
20741        }
20742        cx.notify();
20743        blocks
20744    }
20745
20746    pub fn resize_blocks(
20747        &mut self,
20748        heights: HashMap<CustomBlockId, u32>,
20749        autoscroll: Option<Autoscroll>,
20750        cx: &mut Context<Self>,
20751    ) {
20752        self.display_map
20753            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20754        if let Some(autoscroll) = autoscroll {
20755            self.request_autoscroll(autoscroll, cx);
20756        }
20757        cx.notify();
20758    }
20759
20760    pub fn replace_blocks(
20761        &mut self,
20762        renderers: HashMap<CustomBlockId, RenderBlock>,
20763        autoscroll: Option<Autoscroll>,
20764        cx: &mut Context<Self>,
20765    ) {
20766        self.display_map
20767            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20768        if let Some(autoscroll) = autoscroll {
20769            self.request_autoscroll(autoscroll, cx);
20770        }
20771        cx.notify();
20772    }
20773
20774    pub fn remove_blocks(
20775        &mut self,
20776        block_ids: HashSet<CustomBlockId>,
20777        autoscroll: Option<Autoscroll>,
20778        cx: &mut Context<Self>,
20779    ) {
20780        self.display_map.update(cx, |display_map, cx| {
20781            display_map.remove_blocks(block_ids, cx)
20782        });
20783        if let Some(autoscroll) = autoscroll {
20784            self.request_autoscroll(autoscroll, cx);
20785        }
20786        cx.notify();
20787    }
20788
20789    pub fn row_for_block(
20790        &self,
20791        block_id: CustomBlockId,
20792        cx: &mut Context<Self>,
20793    ) -> Option<DisplayRow> {
20794        self.display_map
20795            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20796    }
20797
20798    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20799        self.focused_block = Some(focused_block);
20800    }
20801
20802    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20803        self.focused_block.take()
20804    }
20805
20806    pub fn insert_creases(
20807        &mut self,
20808        creases: impl IntoIterator<Item = Crease<Anchor>>,
20809        cx: &mut Context<Self>,
20810    ) -> Vec<CreaseId> {
20811        self.display_map
20812            .update(cx, |map, cx| map.insert_creases(creases, cx))
20813    }
20814
20815    pub fn remove_creases(
20816        &mut self,
20817        ids: impl IntoIterator<Item = CreaseId>,
20818        cx: &mut Context<Self>,
20819    ) -> Vec<(CreaseId, Range<Anchor>)> {
20820        self.display_map
20821            .update(cx, |map, cx| map.remove_creases(ids, cx))
20822    }
20823
20824    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
20825        self.display_map
20826            .update(cx, |map, cx| map.snapshot(cx))
20827            .longest_row()
20828    }
20829
20830    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
20831        self.display_map
20832            .update(cx, |map, cx| map.snapshot(cx))
20833            .max_point()
20834    }
20835
20836    pub fn text(&self, cx: &App) -> String {
20837        self.buffer.read(cx).read(cx).text()
20838    }
20839
20840    pub fn is_empty(&self, cx: &App) -> bool {
20841        self.buffer.read(cx).read(cx).is_empty()
20842    }
20843
20844    pub fn text_option(&self, cx: &App) -> Option<String> {
20845        let text = self.text(cx);
20846        let text = text.trim();
20847
20848        if text.is_empty() {
20849            return None;
20850        }
20851
20852        Some(text.to_string())
20853    }
20854
20855    pub fn set_text(
20856        &mut self,
20857        text: impl Into<Arc<str>>,
20858        window: &mut Window,
20859        cx: &mut Context<Self>,
20860    ) {
20861        self.transact(window, cx, |this, _, cx| {
20862            this.buffer
20863                .read(cx)
20864                .as_singleton()
20865                .expect("you can only call set_text on editors for singleton buffers")
20866                .update(cx, |buffer, cx| buffer.set_text(text, cx));
20867        });
20868    }
20869
20870    pub fn display_text(&self, cx: &mut App) -> String {
20871        self.display_map
20872            .update(cx, |map, cx| map.snapshot(cx))
20873            .text()
20874    }
20875
20876    fn create_minimap(
20877        &self,
20878        minimap_settings: MinimapSettings,
20879        window: &mut Window,
20880        cx: &mut Context<Self>,
20881    ) -> Option<Entity<Self>> {
20882        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
20883            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
20884    }
20885
20886    fn initialize_new_minimap(
20887        &self,
20888        minimap_settings: MinimapSettings,
20889        window: &mut Window,
20890        cx: &mut Context<Self>,
20891    ) -> Entity<Self> {
20892        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
20893        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
20894
20895        let mut minimap = Editor::new_internal(
20896            EditorMode::Minimap {
20897                parent: cx.weak_entity(),
20898            },
20899            self.buffer.clone(),
20900            None,
20901            Some(self.display_map.clone()),
20902            window,
20903            cx,
20904        );
20905        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20906        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
20907        minimap.scroll_manager.clone_state(
20908            &self.scroll_manager,
20909            &my_snapshot,
20910            &minimap_snapshot,
20911            cx,
20912        );
20913        minimap.set_text_style_refinement(TextStyleRefinement {
20914            font_size: Some(MINIMAP_FONT_SIZE),
20915            font_weight: Some(MINIMAP_FONT_WEIGHT),
20916            font_family: Some(MINIMAP_FONT_FAMILY),
20917            ..Default::default()
20918        });
20919        minimap.update_minimap_configuration(minimap_settings, cx);
20920        cx.new(|_| minimap)
20921    }
20922
20923    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
20924        let current_line_highlight = minimap_settings
20925            .current_line_highlight
20926            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
20927        self.set_current_line_highlight(Some(current_line_highlight));
20928    }
20929
20930    pub fn minimap(&self) -> Option<&Entity<Self>> {
20931        self.minimap
20932            .as_ref()
20933            .filter(|_| self.minimap_visibility.visible())
20934    }
20935
20936    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
20937        let mut wrap_guides = smallvec![];
20938
20939        if self.show_wrap_guides == Some(false) {
20940            return wrap_guides;
20941        }
20942
20943        let settings = self.buffer.read(cx).language_settings(cx);
20944        if settings.show_wrap_guides {
20945            match self.soft_wrap_mode(cx) {
20946                SoftWrap::Column(soft_wrap) => {
20947                    wrap_guides.push((soft_wrap as usize, true));
20948                }
20949                SoftWrap::Bounded(soft_wrap) => {
20950                    wrap_guides.push((soft_wrap as usize, true));
20951                }
20952                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
20953            }
20954            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
20955        }
20956
20957        wrap_guides
20958    }
20959
20960    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
20961        let settings = self.buffer.read(cx).language_settings(cx);
20962        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
20963        match mode {
20964            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
20965                SoftWrap::None
20966            }
20967            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
20968            language_settings::SoftWrap::PreferredLineLength => {
20969                SoftWrap::Column(settings.preferred_line_length)
20970            }
20971            language_settings::SoftWrap::Bounded => {
20972                SoftWrap::Bounded(settings.preferred_line_length)
20973            }
20974        }
20975    }
20976
20977    pub fn set_soft_wrap_mode(
20978        &mut self,
20979        mode: language_settings::SoftWrap,
20980        cx: &mut Context<Self>,
20981    ) {
20982        self.soft_wrap_mode_override = Some(mode);
20983        cx.notify();
20984    }
20985
20986    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
20987        self.hard_wrap = hard_wrap;
20988        cx.notify();
20989    }
20990
20991    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
20992        self.text_style_refinement = Some(style);
20993    }
20994
20995    /// called by the Element so we know what style we were most recently rendered with.
20996    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
20997        // We intentionally do not inform the display map about the minimap style
20998        // so that wrapping is not recalculated and stays consistent for the editor
20999        // and its linked minimap.
21000        if !self.mode.is_minimap() {
21001            let font = style.text.font();
21002            let font_size = style.text.font_size.to_pixels(window.rem_size());
21003            let display_map = self
21004                .placeholder_display_map
21005                .as_ref()
21006                .filter(|_| self.is_empty(cx))
21007                .unwrap_or(&self.display_map);
21008
21009            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21010        }
21011        self.style = Some(style);
21012    }
21013
21014    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21015        if self.style.is_none() {
21016            self.style = Some(self.create_style(cx));
21017        }
21018        self.style.as_ref().unwrap()
21019    }
21020
21021    // Called by the element. This method is not designed to be called outside of the editor
21022    // element's layout code because it does not notify when rewrapping is computed synchronously.
21023    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21024        if self.is_empty(cx) {
21025            self.placeholder_display_map
21026                .as_ref()
21027                .map_or(false, |display_map| {
21028                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21029                })
21030        } else {
21031            self.display_map
21032                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21033        }
21034    }
21035
21036    pub fn set_soft_wrap(&mut self) {
21037        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21038    }
21039
21040    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21041        if self.soft_wrap_mode_override.is_some() {
21042            self.soft_wrap_mode_override.take();
21043        } else {
21044            let soft_wrap = match self.soft_wrap_mode(cx) {
21045                SoftWrap::GitDiff => return,
21046                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21047                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21048                    language_settings::SoftWrap::None
21049                }
21050            };
21051            self.soft_wrap_mode_override = Some(soft_wrap);
21052        }
21053        cx.notify();
21054    }
21055
21056    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21057        let Some(workspace) = self.workspace() else {
21058            return;
21059        };
21060        let fs = workspace.read(cx).app_state().fs.clone();
21061        let current_show = TabBarSettings::get_global(cx).show;
21062        update_settings_file(fs, cx, move |setting, _| {
21063            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21064        });
21065    }
21066
21067    pub fn toggle_indent_guides(
21068        &mut self,
21069        _: &ToggleIndentGuides,
21070        _: &mut Window,
21071        cx: &mut Context<Self>,
21072    ) {
21073        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21074            self.buffer
21075                .read(cx)
21076                .language_settings(cx)
21077                .indent_guides
21078                .enabled
21079        });
21080        self.show_indent_guides = Some(!currently_enabled);
21081        cx.notify();
21082    }
21083
21084    fn should_show_indent_guides(&self) -> Option<bool> {
21085        self.show_indent_guides
21086    }
21087
21088    pub fn disable_indent_guides_for_buffer(
21089        &mut self,
21090        buffer_id: BufferId,
21091        cx: &mut Context<Self>,
21092    ) {
21093        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21094        cx.notify();
21095    }
21096
21097    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21098        self.buffers_with_disabled_indent_guides
21099            .contains(&buffer_id)
21100    }
21101
21102    pub fn toggle_line_numbers(
21103        &mut self,
21104        _: &ToggleLineNumbers,
21105        _: &mut Window,
21106        cx: &mut Context<Self>,
21107    ) {
21108        let mut editor_settings = EditorSettings::get_global(cx).clone();
21109        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21110        EditorSettings::override_global(editor_settings, cx);
21111    }
21112
21113    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21114        if let Some(show_line_numbers) = self.show_line_numbers {
21115            return show_line_numbers;
21116        }
21117        EditorSettings::get_global(cx).gutter.line_numbers
21118    }
21119
21120    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21121        match (
21122            self.use_relative_line_numbers,
21123            EditorSettings::get_global(cx).relative_line_numbers,
21124        ) {
21125            (None, setting) => setting,
21126            (Some(false), _) => RelativeLineNumbers::Disabled,
21127            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21128            (Some(true), _) => RelativeLineNumbers::Enabled,
21129        }
21130    }
21131
21132    pub fn toggle_relative_line_numbers(
21133        &mut self,
21134        _: &ToggleRelativeLineNumbers,
21135        _: &mut Window,
21136        cx: &mut Context<Self>,
21137    ) {
21138        let is_relative = self.relative_line_numbers(cx);
21139        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21140    }
21141
21142    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21143        self.use_relative_line_numbers = is_relative;
21144        cx.notify();
21145    }
21146
21147    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21148        self.show_gutter = show_gutter;
21149        cx.notify();
21150    }
21151
21152    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21153        self.show_scrollbars = ScrollbarAxes {
21154            horizontal: show,
21155            vertical: show,
21156        };
21157        cx.notify();
21158    }
21159
21160    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21161        self.show_scrollbars.vertical = show;
21162        cx.notify();
21163    }
21164
21165    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21166        self.show_scrollbars.horizontal = show;
21167        cx.notify();
21168    }
21169
21170    pub fn set_minimap_visibility(
21171        &mut self,
21172        minimap_visibility: MinimapVisibility,
21173        window: &mut Window,
21174        cx: &mut Context<Self>,
21175    ) {
21176        if self.minimap_visibility != minimap_visibility {
21177            if minimap_visibility.visible() && self.minimap.is_none() {
21178                let minimap_settings = EditorSettings::get_global(cx).minimap;
21179                self.minimap =
21180                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21181            }
21182            self.minimap_visibility = minimap_visibility;
21183            cx.notify();
21184        }
21185    }
21186
21187    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21188        self.set_show_scrollbars(false, cx);
21189        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21190    }
21191
21192    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21193        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21194    }
21195
21196    /// Normally the text in full mode and auto height editors is padded on the
21197    /// left side by roughly half a character width for improved hit testing.
21198    ///
21199    /// Use this method to disable this for cases where this is not wanted (e.g.
21200    /// if you want to align the editor text with some other text above or below)
21201    /// or if you want to add this padding to single-line editors.
21202    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21203        self.offset_content = offset_content;
21204        cx.notify();
21205    }
21206
21207    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21208        self.show_line_numbers = Some(show_line_numbers);
21209        cx.notify();
21210    }
21211
21212    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21213        self.disable_expand_excerpt_buttons = true;
21214        cx.notify();
21215    }
21216
21217    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21218        self.number_deleted_lines = number;
21219        cx.notify();
21220    }
21221
21222    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21223        self.delegate_expand_excerpts = delegate;
21224    }
21225
21226    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21227        self.delegate_stage_and_restore = delegate;
21228    }
21229
21230    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21231        self.delegate_open_excerpts = delegate;
21232    }
21233
21234    pub fn set_on_local_selections_changed(
21235        &mut self,
21236        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21237    ) {
21238        self.on_local_selections_changed = callback;
21239    }
21240
21241    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21242        self.suppress_selection_callback = suppress;
21243    }
21244
21245    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21246        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21247        cx.notify();
21248    }
21249
21250    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21251        self.show_code_actions = Some(show_code_actions);
21252        cx.notify();
21253    }
21254
21255    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21256        self.show_runnables = Some(show_runnables);
21257        cx.notify();
21258    }
21259
21260    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21261        self.show_breakpoints = Some(show_breakpoints);
21262        cx.notify();
21263    }
21264
21265    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21266        self.show_diff_review_button = show;
21267        cx.notify();
21268    }
21269
21270    pub fn show_diff_review_button(&self) -> bool {
21271        self.show_diff_review_button
21272    }
21273
21274    pub fn render_diff_review_button(
21275        &self,
21276        display_row: DisplayRow,
21277        width: Pixels,
21278        cx: &mut Context<Self>,
21279    ) -> impl IntoElement {
21280        let text_color = cx.theme().colors().text;
21281        let icon_color = cx.theme().colors().icon_accent;
21282
21283        h_flex()
21284            .id("diff_review_button")
21285            .cursor_pointer()
21286            .w(width - px(1.))
21287            .h(relative(0.9))
21288            .justify_center()
21289            .rounded_sm()
21290            .border_1()
21291            .border_color(text_color.opacity(0.1))
21292            .bg(text_color.opacity(0.15))
21293            .hover(|s| {
21294                s.bg(icon_color.opacity(0.4))
21295                    .border_color(icon_color.opacity(0.5))
21296            })
21297            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21298            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21299            .on_mouse_down(
21300                gpui::MouseButton::Left,
21301                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21302                    editor.start_diff_review_drag(display_row, window, cx);
21303                }),
21304            )
21305    }
21306
21307    pub fn start_diff_review_drag(
21308        &mut self,
21309        display_row: DisplayRow,
21310        window: &mut Window,
21311        cx: &mut Context<Self>,
21312    ) {
21313        let snapshot = self.snapshot(window, cx);
21314        let point = snapshot
21315            .display_snapshot
21316            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21317        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21318        self.diff_review_drag_state = Some(DiffReviewDragState {
21319            start_anchor: anchor,
21320            current_anchor: anchor,
21321        });
21322        cx.notify();
21323    }
21324
21325    pub fn update_diff_review_drag(
21326        &mut self,
21327        display_row: DisplayRow,
21328        window: &mut Window,
21329        cx: &mut Context<Self>,
21330    ) {
21331        if self.diff_review_drag_state.is_none() {
21332            return;
21333        }
21334        let snapshot = self.snapshot(window, cx);
21335        let point = snapshot
21336            .display_snapshot
21337            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21338        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21339        if let Some(drag_state) = &mut self.diff_review_drag_state {
21340            drag_state.current_anchor = anchor;
21341            cx.notify();
21342        }
21343    }
21344
21345    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21346        if let Some(drag_state) = self.diff_review_drag_state.take() {
21347            let snapshot = self.snapshot(window, cx);
21348            let range = drag_state.row_range(&snapshot.display_snapshot);
21349            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21350        }
21351        cx.notify();
21352    }
21353
21354    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21355        self.diff_review_drag_state = None;
21356        cx.notify();
21357    }
21358
21359    /// Calculates the appropriate block height for the diff review overlay.
21360    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21361    /// and 2 lines per comment when expanded.
21362    fn calculate_overlay_height(
21363        &self,
21364        hunk_key: &DiffHunkKey,
21365        comments_expanded: bool,
21366        snapshot: &MultiBufferSnapshot,
21367    ) -> u32 {
21368        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21369        let base_height: u32 = 2; // Input row with avatar and buttons
21370
21371        if comment_count == 0 {
21372            base_height
21373        } else if comments_expanded {
21374            // Header (1 line) + 2 lines per comment
21375            base_height + 1 + (comment_count as u32 * 2)
21376        } else {
21377            // Just header when collapsed
21378            base_height + 1
21379        }
21380    }
21381
21382    pub fn show_diff_review_overlay(
21383        &mut self,
21384        display_range: Range<DisplayRow>,
21385        window: &mut Window,
21386        cx: &mut Context<Self>,
21387    ) {
21388        let Range { start, end } = display_range.sorted();
21389
21390        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21391        let editor_snapshot = self.snapshot(window, cx);
21392
21393        // Convert display rows to multibuffer points
21394        let start_point = editor_snapshot
21395            .display_snapshot
21396            .display_point_to_point(start.as_display_point(), Bias::Left);
21397        let end_point = editor_snapshot
21398            .display_snapshot
21399            .display_point_to_point(end.as_display_point(), Bias::Left);
21400        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21401
21402        // Create anchor range for the selected lines (start of first line to end of last line)
21403        let line_end = Point::new(
21404            end_point.row,
21405            buffer_snapshot.line_len(end_multi_buffer_row),
21406        );
21407        let anchor_range =
21408            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21409
21410        // Compute the hunk key for this display row
21411        let file_path = buffer_snapshot
21412            .file_at(start_point)
21413            .map(|file: &Arc<dyn language::File>| file.path().clone())
21414            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21415        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21416        let new_hunk_key = DiffHunkKey {
21417            file_path,
21418            hunk_start_anchor,
21419        };
21420
21421        // Check if we already have an overlay for this hunk
21422        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21423            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21424        }) {
21425            // Just focus the existing overlay's prompt editor
21426            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21427            window.focus(&focus_handle, cx);
21428            return;
21429        }
21430
21431        // Dismiss overlays that have no comments for their hunks
21432        self.dismiss_overlays_without_comments(cx);
21433
21434        // Get the current user's avatar URI from the project's user_store
21435        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21436            let user_store = project.read(cx).user_store();
21437            user_store
21438                .read(cx)
21439                .current_user()
21440                .map(|user| user.avatar_uri.clone())
21441        });
21442
21443        // Create anchor at the end of the last row so the block appears immediately below it
21444        // Use multibuffer coordinates for anchor creation
21445        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21446        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21447
21448        // Use the hunk key we already computed
21449        let hunk_key = new_hunk_key;
21450
21451        // Create the prompt editor for the review input
21452        let prompt_editor = cx.new(|cx| {
21453            let mut editor = Editor::single_line(window, cx);
21454            editor.set_placeholder_text("Add a review comment...", window, cx);
21455            editor
21456        });
21457
21458        // Register the Newline action on the prompt editor to submit the review
21459        let parent_editor = cx.entity().downgrade();
21460        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21461            prompt_editor.register_action({
21462                let parent_editor = parent_editor.clone();
21463                move |_: &crate::actions::Newline, window, cx| {
21464                    if let Some(editor) = parent_editor.upgrade() {
21465                        editor.update(cx, |editor, cx| {
21466                            editor.submit_diff_review_comment(window, cx);
21467                        });
21468                    }
21469                }
21470            })
21471        });
21472
21473        // Calculate initial height based on existing comments for this hunk
21474        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21475
21476        // Create the overlay block
21477        let prompt_editor_for_render = prompt_editor.clone();
21478        let hunk_key_for_render = hunk_key.clone();
21479        let editor_handle = cx.entity().downgrade();
21480        let block = BlockProperties {
21481            style: BlockStyle::Sticky,
21482            placement: BlockPlacement::Below(anchor),
21483            height: Some(initial_height),
21484            render: Arc::new(move |cx| {
21485                Self::render_diff_review_overlay(
21486                    &prompt_editor_for_render,
21487                    &hunk_key_for_render,
21488                    &editor_handle,
21489                    cx,
21490                )
21491            }),
21492            priority: 0,
21493        };
21494
21495        let block_ids = self.insert_blocks([block], None, cx);
21496        let Some(block_id) = block_ids.into_iter().next() else {
21497            log::error!("Failed to insert diff review overlay block");
21498            return;
21499        };
21500
21501        self.diff_review_overlays.push(DiffReviewOverlay {
21502            anchor_range,
21503            block_id,
21504            prompt_editor: prompt_editor.clone(),
21505            hunk_key,
21506            comments_expanded: true,
21507            inline_edit_editors: HashMap::default(),
21508            inline_edit_subscriptions: HashMap::default(),
21509            user_avatar_uri,
21510            _subscription: subscription,
21511        });
21512
21513        // Focus the prompt editor
21514        let focus_handle = prompt_editor.focus_handle(cx);
21515        window.focus(&focus_handle, cx);
21516
21517        cx.notify();
21518    }
21519
21520    /// Dismisses all diff review overlays.
21521    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21522        if self.diff_review_overlays.is_empty() {
21523            return;
21524        }
21525        let block_ids: HashSet<_> = self
21526            .diff_review_overlays
21527            .drain(..)
21528            .map(|overlay| overlay.block_id)
21529            .collect();
21530        self.remove_blocks(block_ids, None, cx);
21531        cx.notify();
21532    }
21533
21534    /// Dismisses overlays that have no comments stored for their hunks.
21535    /// Keeps overlays that have at least one comment.
21536    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21537        let snapshot = self.buffer.read(cx).snapshot(cx);
21538
21539        // First, compute which overlays have comments (to avoid borrow issues with retain)
21540        let overlays_with_comments: Vec<bool> = self
21541            .diff_review_overlays
21542            .iter()
21543            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21544            .collect();
21545
21546        // Now collect block IDs to remove and retain overlays
21547        let mut block_ids_to_remove = HashSet::default();
21548        let mut index = 0;
21549        self.diff_review_overlays.retain(|overlay| {
21550            let has_comments = overlays_with_comments[index];
21551            index += 1;
21552            if !has_comments {
21553                block_ids_to_remove.insert(overlay.block_id);
21554            }
21555            has_comments
21556        });
21557
21558        if !block_ids_to_remove.is_empty() {
21559            self.remove_blocks(block_ids_to_remove, None, cx);
21560            cx.notify();
21561        }
21562    }
21563
21564    /// Refreshes the diff review overlay block to update its height and render function.
21565    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21566    fn refresh_diff_review_overlay_height(
21567        &mut self,
21568        hunk_key: &DiffHunkKey,
21569        _window: &mut Window,
21570        cx: &mut Context<Self>,
21571    ) {
21572        // Extract all needed data from overlay first to avoid borrow conflicts
21573        let snapshot = self.buffer.read(cx).snapshot(cx);
21574        let (comments_expanded, block_id, prompt_editor) = {
21575            let Some(overlay) = self
21576                .diff_review_overlays
21577                .iter()
21578                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21579            else {
21580                return;
21581            };
21582
21583            (
21584                overlay.comments_expanded,
21585                overlay.block_id,
21586                overlay.prompt_editor.clone(),
21587            )
21588        };
21589
21590        // Calculate new height
21591        let snapshot = self.buffer.read(cx).snapshot(cx);
21592        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21593
21594        // Update the block height using resize_blocks (avoids flicker)
21595        let mut heights = HashMap::default();
21596        heights.insert(block_id, new_height);
21597        self.resize_blocks(heights, None, cx);
21598
21599        // Update the render function using replace_blocks (avoids flicker)
21600        let hunk_key_for_render = hunk_key.clone();
21601        let editor_handle = cx.entity().downgrade();
21602        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21603            Arc::new(move |cx| {
21604                Self::render_diff_review_overlay(
21605                    &prompt_editor,
21606                    &hunk_key_for_render,
21607                    &editor_handle,
21608                    cx,
21609                )
21610            });
21611
21612        let mut renderers = HashMap::default();
21613        renderers.insert(block_id, render);
21614        self.replace_blocks(renderers, None, cx);
21615    }
21616
21617    /// Action handler for SubmitDiffReviewComment.
21618    pub fn submit_diff_review_comment_action(
21619        &mut self,
21620        _: &SubmitDiffReviewComment,
21621        window: &mut Window,
21622        cx: &mut Context<Self>,
21623    ) {
21624        self.submit_diff_review_comment(window, cx);
21625    }
21626
21627    /// Stores the diff review comment locally.
21628    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21629    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21630        // Find the overlay that currently has focus
21631        let overlay_index = self
21632            .diff_review_overlays
21633            .iter()
21634            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21635        let Some(overlay_index) = overlay_index else {
21636            return;
21637        };
21638        let overlay = &self.diff_review_overlays[overlay_index];
21639
21640        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21641        if comment_text.is_empty() {
21642            return;
21643        }
21644
21645        let anchor_range = overlay.anchor_range.clone();
21646        let hunk_key = overlay.hunk_key.clone();
21647
21648        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21649
21650        // Clear the prompt editor but keep the overlay open
21651        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21652            overlay.prompt_editor.update(cx, |editor, cx| {
21653                editor.clear(window, cx);
21654            });
21655        }
21656
21657        // Refresh the overlay to update the block height for the new comment
21658        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21659
21660        cx.notify();
21661    }
21662
21663    /// Returns the prompt editor for the diff review overlay, if one is active.
21664    /// This is primarily used for testing.
21665    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21666        self.diff_review_overlays
21667            .first()
21668            .map(|overlay| &overlay.prompt_editor)
21669    }
21670
21671    /// Returns the line range for the first diff review overlay, if one is active.
21672    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21673    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21674        let overlay = self.diff_review_overlays.first()?;
21675        let snapshot = self.buffer.read(cx).snapshot(cx);
21676        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21677        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21678        let start_row = snapshot
21679            .point_to_buffer_point(start_point)
21680            .map(|(_, p, _)| p.row)
21681            .unwrap_or(start_point.row);
21682        let end_row = snapshot
21683            .point_to_buffer_point(end_point)
21684            .map(|(_, p, _)| p.row)
21685            .unwrap_or(end_point.row);
21686        Some((start_row, end_row))
21687    }
21688
21689    /// Sets whether the comments section is expanded in the diff review overlay.
21690    /// This is primarily used for testing.
21691    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21692        for overlay in &mut self.diff_review_overlays {
21693            overlay.comments_expanded = expanded;
21694        }
21695        cx.notify();
21696    }
21697
21698    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21699    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21700        a.file_path == b.file_path
21701            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21702    }
21703
21704    /// Returns comments for a specific hunk, ordered by creation time.
21705    pub fn comments_for_hunk<'a>(
21706        &'a self,
21707        key: &DiffHunkKey,
21708        snapshot: &MultiBufferSnapshot,
21709    ) -> &'a [StoredReviewComment] {
21710        let key_point = key.hunk_start_anchor.to_point(snapshot);
21711        self.stored_review_comments
21712            .iter()
21713            .find(|(k, _)| {
21714                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21715            })
21716            .map(|(_, comments)| comments.as_slice())
21717            .unwrap_or(&[])
21718    }
21719
21720    /// Returns the total count of stored review comments across all hunks.
21721    pub fn total_review_comment_count(&self) -> usize {
21722        self.stored_review_comments
21723            .iter()
21724            .map(|(_, v)| v.len())
21725            .sum()
21726    }
21727
21728    /// Returns the count of comments for a specific hunk.
21729    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21730        let key_point = key.hunk_start_anchor.to_point(snapshot);
21731        self.stored_review_comments
21732            .iter()
21733            .find(|(k, _)| {
21734                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21735            })
21736            .map(|(_, v)| v.len())
21737            .unwrap_or(0)
21738    }
21739
21740    /// Adds a new review comment to a specific hunk.
21741    pub fn add_review_comment(
21742        &mut self,
21743        hunk_key: DiffHunkKey,
21744        comment: String,
21745        anchor_range: Range<Anchor>,
21746        cx: &mut Context<Self>,
21747    ) -> usize {
21748        let id = self.next_review_comment_id;
21749        self.next_review_comment_id += 1;
21750
21751        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21752
21753        let snapshot = self.buffer.read(cx).snapshot(cx);
21754        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21755
21756        // Find existing entry for this hunk or add a new one
21757        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21758            k.file_path == hunk_key.file_path
21759                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21760        }) {
21761            comments.push(stored_comment);
21762        } else {
21763            self.stored_review_comments
21764                .push((hunk_key, vec![stored_comment]));
21765        }
21766
21767        cx.emit(EditorEvent::ReviewCommentsChanged {
21768            total_count: self.total_review_comment_count(),
21769        });
21770        cx.notify();
21771        id
21772    }
21773
21774    /// Removes a review comment by ID from any hunk.
21775    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21776        for (_, comments) in self.stored_review_comments.iter_mut() {
21777            if let Some(index) = comments.iter().position(|c| c.id == id) {
21778                comments.remove(index);
21779                cx.emit(EditorEvent::ReviewCommentsChanged {
21780                    total_count: self.total_review_comment_count(),
21781                });
21782                cx.notify();
21783                return true;
21784            }
21785        }
21786        false
21787    }
21788
21789    /// Updates a review comment's text by ID.
21790    pub fn update_review_comment(
21791        &mut self,
21792        id: usize,
21793        new_comment: String,
21794        cx: &mut Context<Self>,
21795    ) -> bool {
21796        for (_, comments) in self.stored_review_comments.iter_mut() {
21797            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21798                comment.comment = new_comment;
21799                comment.is_editing = false;
21800                cx.emit(EditorEvent::ReviewCommentsChanged {
21801                    total_count: self.total_review_comment_count(),
21802                });
21803                cx.notify();
21804                return true;
21805            }
21806        }
21807        false
21808    }
21809
21810    /// Sets a comment's editing state.
21811    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21812        for (_, comments) in self.stored_review_comments.iter_mut() {
21813            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21814                comment.is_editing = is_editing;
21815                cx.notify();
21816                return;
21817            }
21818        }
21819    }
21820
21821    /// Takes all stored comments from all hunks, clearing the storage.
21822    /// Returns a Vec of (hunk_key, comments) pairs.
21823    pub fn take_all_review_comments(
21824        &mut self,
21825        cx: &mut Context<Self>,
21826    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
21827        // Dismiss all overlays when taking comments (e.g., when sending to agent)
21828        self.dismiss_all_diff_review_overlays(cx);
21829        let comments = std::mem::take(&mut self.stored_review_comments);
21830        // Reset the ID counter since all comments have been taken
21831        self.next_review_comment_id = 0;
21832        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
21833        cx.notify();
21834        comments
21835    }
21836
21837    /// Removes review comments whose anchors are no longer valid or whose
21838    /// associated diff hunks no longer exist.
21839    ///
21840    /// This should be called when the buffer changes to prevent orphaned comments
21841    /// from accumulating.
21842    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
21843        let snapshot = self.buffer.read(cx).snapshot(cx);
21844        let original_count = self.total_review_comment_count();
21845
21846        // Remove comments with invalid hunk anchors
21847        self.stored_review_comments
21848            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
21849
21850        // Also clean up individual comments with invalid anchor ranges
21851        for (_, comments) in &mut self.stored_review_comments {
21852            comments.retain(|comment| {
21853                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
21854            });
21855        }
21856
21857        // Remove empty hunk entries
21858        self.stored_review_comments
21859            .retain(|(_, comments)| !comments.is_empty());
21860
21861        let new_count = self.total_review_comment_count();
21862        if new_count != original_count {
21863            cx.emit(EditorEvent::ReviewCommentsChanged {
21864                total_count: new_count,
21865            });
21866            cx.notify();
21867        }
21868    }
21869
21870    /// Toggles the expanded state of the comments section in the overlay.
21871    pub fn toggle_review_comments_expanded(
21872        &mut self,
21873        _: &ToggleReviewCommentsExpanded,
21874        window: &mut Window,
21875        cx: &mut Context<Self>,
21876    ) {
21877        // Find the overlay that currently has focus, or use the first one
21878        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
21879            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
21880                overlay.comments_expanded = !overlay.comments_expanded;
21881                Some(overlay.hunk_key.clone())
21882            } else {
21883                None
21884            }
21885        });
21886
21887        // If no focused overlay found, toggle the first one
21888        let hunk_key = overlay_info.or_else(|| {
21889            self.diff_review_overlays.first_mut().map(|overlay| {
21890                overlay.comments_expanded = !overlay.comments_expanded;
21891                overlay.hunk_key.clone()
21892            })
21893        });
21894
21895        if let Some(hunk_key) = hunk_key {
21896            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21897            cx.notify();
21898        }
21899    }
21900
21901    /// Handles the EditReviewComment action - sets a comment into editing mode.
21902    pub fn edit_review_comment(
21903        &mut self,
21904        action: &EditReviewComment,
21905        window: &mut Window,
21906        cx: &mut Context<Self>,
21907    ) {
21908        let comment_id = action.id;
21909
21910        // Set the comment to editing mode
21911        self.set_comment_editing(comment_id, true, cx);
21912
21913        // Find the overlay that contains this comment and create an inline editor if needed
21914        // First, find which hunk this comment belongs to
21915        let hunk_key = self
21916            .stored_review_comments
21917            .iter()
21918            .find_map(|(key, comments)| {
21919                if comments.iter().any(|c| c.id == comment_id) {
21920                    Some(key.clone())
21921                } else {
21922                    None
21923                }
21924            });
21925
21926        let snapshot = self.buffer.read(cx).snapshot(cx);
21927        if let Some(hunk_key) = hunk_key {
21928            if let Some(overlay) = self
21929                .diff_review_overlays
21930                .iter_mut()
21931                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21932            {
21933                if let std::collections::hash_map::Entry::Vacant(entry) =
21934                    overlay.inline_edit_editors.entry(comment_id)
21935                {
21936                    // Find the comment text
21937                    let comment_text = self
21938                        .stored_review_comments
21939                        .iter()
21940                        .flat_map(|(_, comments)| comments)
21941                        .find(|c| c.id == comment_id)
21942                        .map(|c| c.comment.clone())
21943                        .unwrap_or_default();
21944
21945                    // Create inline editor
21946                    let parent_editor = cx.entity().downgrade();
21947                    let inline_editor = cx.new(|cx| {
21948                        let mut editor = Editor::single_line(window, cx);
21949                        editor.set_text(&*comment_text, window, cx);
21950                        // Select all text for easy replacement
21951                        editor.select_all(&crate::actions::SelectAll, window, cx);
21952                        editor
21953                    });
21954
21955                    // Register the Newline action to confirm the edit
21956                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
21957                        inline_editor.register_action({
21958                            let parent_editor = parent_editor.clone();
21959                            move |_: &crate::actions::Newline, window, cx| {
21960                                if let Some(editor) = parent_editor.upgrade() {
21961                                    editor.update(cx, |editor, cx| {
21962                                        editor.confirm_edit_review_comment(comment_id, window, cx);
21963                                    });
21964                                }
21965                            }
21966                        })
21967                    });
21968
21969                    // Store the subscription to keep the action handler alive
21970                    overlay
21971                        .inline_edit_subscriptions
21972                        .insert(comment_id, subscription);
21973
21974                    // Focus the inline editor
21975                    let focus_handle = inline_editor.focus_handle(cx);
21976                    window.focus(&focus_handle, cx);
21977
21978                    entry.insert(inline_editor);
21979                }
21980            }
21981        }
21982
21983        cx.notify();
21984    }
21985
21986    /// Confirms an inline edit of a review comment.
21987    pub fn confirm_edit_review_comment(
21988        &mut self,
21989        comment_id: usize,
21990        _window: &mut Window,
21991        cx: &mut Context<Self>,
21992    ) {
21993        // Get the new text from the inline editor
21994        // Find the overlay containing this comment's inline editor
21995        let snapshot = self.buffer.read(cx).snapshot(cx);
21996        let hunk_key = self
21997            .stored_review_comments
21998            .iter()
21999            .find_map(|(key, comments)| {
22000                if comments.iter().any(|c| c.id == comment_id) {
22001                    Some(key.clone())
22002                } else {
22003                    None
22004                }
22005            });
22006
22007        let new_text = hunk_key
22008            .as_ref()
22009            .and_then(|hunk_key| {
22010                self.diff_review_overlays
22011                    .iter()
22012                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22013            })
22014            .as_ref()
22015            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22016            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22017
22018        if let Some(new_text) = new_text {
22019            if !new_text.is_empty() {
22020                self.update_review_comment(comment_id, new_text, cx);
22021            }
22022        }
22023
22024        // Remove the inline editor and its subscription
22025        if let Some(hunk_key) = hunk_key {
22026            if let Some(overlay) = self
22027                .diff_review_overlays
22028                .iter_mut()
22029                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22030            {
22031                overlay.inline_edit_editors.remove(&comment_id);
22032                overlay.inline_edit_subscriptions.remove(&comment_id);
22033            }
22034        }
22035
22036        // Clear editing state
22037        self.set_comment_editing(comment_id, false, cx);
22038    }
22039
22040    /// Cancels an inline edit of a review comment.
22041    pub fn cancel_edit_review_comment(
22042        &mut self,
22043        comment_id: usize,
22044        _window: &mut Window,
22045        cx: &mut Context<Self>,
22046    ) {
22047        // Find which hunk this comment belongs to
22048        let hunk_key = self
22049            .stored_review_comments
22050            .iter()
22051            .find_map(|(key, comments)| {
22052                if comments.iter().any(|c| c.id == comment_id) {
22053                    Some(key.clone())
22054                } else {
22055                    None
22056                }
22057            });
22058
22059        // Remove the inline editor and its subscription
22060        if let Some(hunk_key) = hunk_key {
22061            let snapshot = self.buffer.read(cx).snapshot(cx);
22062            if let Some(overlay) = self
22063                .diff_review_overlays
22064                .iter_mut()
22065                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22066            {
22067                overlay.inline_edit_editors.remove(&comment_id);
22068                overlay.inline_edit_subscriptions.remove(&comment_id);
22069            }
22070        }
22071
22072        // Clear editing state
22073        self.set_comment_editing(comment_id, false, cx);
22074    }
22075
22076    /// Action handler for ConfirmEditReviewComment.
22077    pub fn confirm_edit_review_comment_action(
22078        &mut self,
22079        action: &ConfirmEditReviewComment,
22080        window: &mut Window,
22081        cx: &mut Context<Self>,
22082    ) {
22083        self.confirm_edit_review_comment(action.id, window, cx);
22084    }
22085
22086    /// Action handler for CancelEditReviewComment.
22087    pub fn cancel_edit_review_comment_action(
22088        &mut self,
22089        action: &CancelEditReviewComment,
22090        window: &mut Window,
22091        cx: &mut Context<Self>,
22092    ) {
22093        self.cancel_edit_review_comment(action.id, window, cx);
22094    }
22095
22096    /// Handles the DeleteReviewComment action - removes a comment.
22097    pub fn delete_review_comment(
22098        &mut self,
22099        action: &DeleteReviewComment,
22100        window: &mut Window,
22101        cx: &mut Context<Self>,
22102    ) {
22103        // Get the hunk key before removing the comment
22104        // Find the hunk key from the comment itself
22105        let comment_id = action.id;
22106        let hunk_key = self
22107            .stored_review_comments
22108            .iter()
22109            .find_map(|(key, comments)| {
22110                if comments.iter().any(|c| c.id == comment_id) {
22111                    Some(key.clone())
22112                } else {
22113                    None
22114                }
22115            });
22116
22117        // Also get it from the overlay for refresh purposes
22118        let overlay_hunk_key = self
22119            .diff_review_overlays
22120            .first()
22121            .map(|o| o.hunk_key.clone());
22122
22123        self.remove_review_comment(action.id, cx);
22124
22125        // Refresh the overlay height after removing a comment
22126        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22127            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22128        }
22129    }
22130
22131    fn render_diff_review_overlay(
22132        prompt_editor: &Entity<Editor>,
22133        hunk_key: &DiffHunkKey,
22134        editor_handle: &WeakEntity<Editor>,
22135        cx: &mut BlockContext,
22136    ) -> AnyElement {
22137        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22138            if ranges.is_empty() {
22139                return None;
22140            }
22141            let formatted: Vec<String> = ranges
22142                .iter()
22143                .map(|(start, end)| {
22144                    let start_line = start + 1;
22145                    let end_line = end + 1;
22146                    if start_line == end_line {
22147                        format!("Line {start_line}")
22148                    } else {
22149                        format!("Lines {start_line}-{end_line}")
22150                    }
22151                })
22152                .collect();
22153            // Don't show label for single line in single excerpt
22154            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22155                return None;
22156            }
22157            Some(formatted.join(""))
22158        }
22159
22160        let theme = cx.theme();
22161        let colors = theme.colors();
22162
22163        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22164            editor_handle
22165                .upgrade()
22166                .map(|editor| {
22167                    let editor = editor.read(cx);
22168                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22169                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22170                    let (expanded, editors, avatar_uri, line_ranges) = editor
22171                        .diff_review_overlays
22172                        .iter()
22173                        .find(|overlay| {
22174                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22175                        })
22176                        .map(|o| {
22177                            let start_point = o.anchor_range.start.to_point(&snapshot);
22178                            let end_point = o.anchor_range.end.to_point(&snapshot);
22179                            // Get line ranges per excerpt to detect discontinuities
22180                            let buffer_ranges =
22181                                snapshot.range_to_buffer_ranges(start_point..end_point);
22182                            let ranges: Vec<(u32, u32)> = buffer_ranges
22183                                .iter()
22184                                .map(|(buffer, range, _)| {
22185                                    let start = buffer.offset_to_point(range.start.0).row;
22186                                    let end = buffer.offset_to_point(range.end.0).row;
22187                                    (start, end)
22188                                })
22189                                .collect();
22190                            (
22191                                o.comments_expanded,
22192                                o.inline_edit_editors.clone(),
22193                                o.user_avatar_uri.clone(),
22194                                if ranges.is_empty() {
22195                                    None
22196                                } else {
22197                                    Some(ranges)
22198                                },
22199                            )
22200                        })
22201                        .unwrap_or((true, HashMap::default(), None, None));
22202                    (comments, expanded, editors, avatar_uri, line_ranges)
22203                })
22204                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22205
22206        let comment_count = comments.len();
22207        let avatar_size = px(20.);
22208        let action_icon_size = IconSize::XSmall;
22209
22210        v_flex()
22211            .w_full()
22212            .bg(colors.editor_background)
22213            .border_b_1()
22214            .border_color(colors.border)
22215            .px_2()
22216            .pb_2()
22217            .gap_2()
22218            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22219            .when_some(line_ranges, |el, ranges| {
22220                let label = format_line_ranges(&ranges);
22221                if let Some(label) = label {
22222                    el.child(
22223                        h_flex()
22224                            .w_full()
22225                            .px_2()
22226                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22227                    )
22228                } else {
22229                    el
22230                }
22231            })
22232            // Top row: editable input with user's avatar
22233            .child(
22234                h_flex()
22235                    .w_full()
22236                    .items_center()
22237                    .gap_2()
22238                    .px_2()
22239                    .py_1p5()
22240                    .rounded_md()
22241                    .bg(colors.surface_background)
22242                    .child(
22243                        div()
22244                            .size(avatar_size)
22245                            .flex_shrink_0()
22246                            .rounded_full()
22247                            .overflow_hidden()
22248                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22249                                Avatar::new(avatar_uri.clone())
22250                                    .size(avatar_size)
22251                                    .into_any_element()
22252                            } else {
22253                                Icon::new(IconName::Person)
22254                                    .size(IconSize::Small)
22255                                    .color(ui::Color::Muted)
22256                                    .into_any_element()
22257                            }),
22258                    )
22259                    .child(
22260                        div()
22261                            .flex_1()
22262                            .border_1()
22263                            .border_color(colors.border)
22264                            .rounded_md()
22265                            .bg(colors.editor_background)
22266                            .px_2()
22267                            .py_1()
22268                            .child(prompt_editor.clone()),
22269                    )
22270                    .child(
22271                        h_flex()
22272                            .flex_shrink_0()
22273                            .gap_1()
22274                            .child(
22275                                IconButton::new("diff-review-close", IconName::Close)
22276                                    .icon_color(ui::Color::Muted)
22277                                    .icon_size(action_icon_size)
22278                                    .tooltip(Tooltip::text("Close"))
22279                                    .on_click(|_, window, cx| {
22280                                        window
22281                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22282                                    }),
22283                            )
22284                            .child(
22285                                IconButton::new("diff-review-add", IconName::Return)
22286                                    .icon_color(ui::Color::Muted)
22287                                    .icon_size(action_icon_size)
22288                                    .tooltip(Tooltip::text("Add comment"))
22289                                    .on_click(|_, window, cx| {
22290                                        window.dispatch_action(
22291                                            Box::new(crate::actions::SubmitDiffReviewComment),
22292                                            cx,
22293                                        );
22294                                    }),
22295                            ),
22296                    ),
22297            )
22298            // Expandable comments section (only shown when there are comments)
22299            .when(comment_count > 0, |el| {
22300                el.child(Self::render_comments_section(
22301                    comments,
22302                    comments_expanded,
22303                    inline_editors,
22304                    user_avatar_uri,
22305                    avatar_size,
22306                    action_icon_size,
22307                    colors,
22308                ))
22309            })
22310            .into_any_element()
22311    }
22312
22313    fn render_comments_section(
22314        comments: Vec<StoredReviewComment>,
22315        expanded: bool,
22316        inline_editors: HashMap<usize, Entity<Editor>>,
22317        user_avatar_uri: Option<SharedUri>,
22318        avatar_size: Pixels,
22319        action_icon_size: IconSize,
22320        colors: &theme::ThemeColors,
22321    ) -> impl IntoElement {
22322        let comment_count = comments.len();
22323
22324        v_flex()
22325            .w_full()
22326            .gap_1()
22327            // Header with expand/collapse toggle
22328            .child(
22329                h_flex()
22330                    .id("review-comments-header")
22331                    .w_full()
22332                    .items_center()
22333                    .gap_1()
22334                    .px_2()
22335                    .py_1()
22336                    .cursor_pointer()
22337                    .rounded_md()
22338                    .hover(|style| style.bg(colors.ghost_element_hover))
22339                    .on_click(|_, window: &mut Window, cx| {
22340                        window.dispatch_action(
22341                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22342                            cx,
22343                        );
22344                    })
22345                    .child(
22346                        Icon::new(if expanded {
22347                            IconName::ChevronDown
22348                        } else {
22349                            IconName::ChevronRight
22350                        })
22351                        .size(IconSize::Small)
22352                        .color(ui::Color::Muted),
22353                    )
22354                    .child(
22355                        Label::new(format!(
22356                            "{} Comment{}",
22357                            comment_count,
22358                            if comment_count == 1 { "" } else { "s" }
22359                        ))
22360                        .size(LabelSize::Small)
22361                        .color(Color::Muted),
22362                    ),
22363            )
22364            // Comments list (when expanded)
22365            .when(expanded, |el| {
22366                el.children(comments.into_iter().map(|comment| {
22367                    let inline_editor = inline_editors.get(&comment.id).cloned();
22368                    Self::render_comment_row(
22369                        comment,
22370                        inline_editor,
22371                        user_avatar_uri.clone(),
22372                        avatar_size,
22373                        action_icon_size,
22374                        colors,
22375                    )
22376                }))
22377            })
22378    }
22379
22380    fn render_comment_row(
22381        comment: StoredReviewComment,
22382        inline_editor: Option<Entity<Editor>>,
22383        user_avatar_uri: Option<SharedUri>,
22384        avatar_size: Pixels,
22385        action_icon_size: IconSize,
22386        colors: &theme::ThemeColors,
22387    ) -> impl IntoElement {
22388        let comment_id = comment.id;
22389        let is_editing = inline_editor.is_some();
22390
22391        h_flex()
22392            .w_full()
22393            .items_center()
22394            .gap_2()
22395            .px_2()
22396            .py_1p5()
22397            .rounded_md()
22398            .bg(colors.surface_background)
22399            .child(
22400                div()
22401                    .size(avatar_size)
22402                    .flex_shrink_0()
22403                    .rounded_full()
22404                    .overflow_hidden()
22405                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22406                        Avatar::new(avatar_uri.clone())
22407                            .size(avatar_size)
22408                            .into_any_element()
22409                    } else {
22410                        Icon::new(IconName::Person)
22411                            .size(IconSize::Small)
22412                            .color(ui::Color::Muted)
22413                            .into_any_element()
22414                    }),
22415            )
22416            .child(if let Some(editor) = inline_editor {
22417                // Inline edit mode: show an editable text field
22418                div()
22419                    .flex_1()
22420                    .border_1()
22421                    .border_color(colors.border)
22422                    .rounded_md()
22423                    .bg(colors.editor_background)
22424                    .px_2()
22425                    .py_1()
22426                    .child(editor)
22427                    .into_any_element()
22428            } else {
22429                // Display mode: show the comment text
22430                div()
22431                    .flex_1()
22432                    .text_sm()
22433                    .text_color(colors.text)
22434                    .child(comment.comment)
22435                    .into_any_element()
22436            })
22437            .child(if is_editing {
22438                // Editing mode: show close and confirm buttons
22439                h_flex()
22440                    .gap_1()
22441                    .child(
22442                        IconButton::new(
22443                            format!("diff-review-cancel-edit-{comment_id}"),
22444                            IconName::Close,
22445                        )
22446                        .icon_color(ui::Color::Muted)
22447                        .icon_size(action_icon_size)
22448                        .tooltip(Tooltip::text("Cancel"))
22449                        .on_click(move |_, window, cx| {
22450                            window.dispatch_action(
22451                                Box::new(crate::actions::CancelEditReviewComment {
22452                                    id: comment_id,
22453                                }),
22454                                cx,
22455                            );
22456                        }),
22457                    )
22458                    .child(
22459                        IconButton::new(
22460                            format!("diff-review-confirm-edit-{comment_id}"),
22461                            IconName::Return,
22462                        )
22463                        .icon_color(ui::Color::Muted)
22464                        .icon_size(action_icon_size)
22465                        .tooltip(Tooltip::text("Confirm"))
22466                        .on_click(move |_, window, cx| {
22467                            window.dispatch_action(
22468                                Box::new(crate::actions::ConfirmEditReviewComment {
22469                                    id: comment_id,
22470                                }),
22471                                cx,
22472                            );
22473                        }),
22474                    )
22475                    .into_any_element()
22476            } else {
22477                // Display mode: no action buttons for now (edit/delete not yet implemented)
22478                gpui::Empty.into_any_element()
22479            })
22480    }
22481
22482    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22483        if self.display_map.read(cx).masked != masked {
22484            self.display_map.update(cx, |map, _| map.masked = masked);
22485        }
22486        cx.notify()
22487    }
22488
22489    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22490        self.show_wrap_guides = Some(show_wrap_guides);
22491        cx.notify();
22492    }
22493
22494    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22495        self.show_indent_guides = Some(show_indent_guides);
22496        cx.notify();
22497    }
22498
22499    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22500        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22501            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22502                && let Some(dir) = file.abs_path(cx).parent()
22503            {
22504                return Some(dir.to_owned());
22505            }
22506        }
22507
22508        None
22509    }
22510
22511    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22512        self.active_excerpt(cx)?
22513            .1
22514            .read(cx)
22515            .file()
22516            .and_then(|f| f.as_local())
22517    }
22518
22519    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22520        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22521            let buffer = buffer.read(cx);
22522            if let Some(project_path) = buffer.project_path(cx) {
22523                let project = self.project()?.read(cx);
22524                project.absolute_path(&project_path, cx)
22525            } else {
22526                buffer
22527                    .file()
22528                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22529            }
22530        })
22531    }
22532
22533    pub fn reveal_in_finder(
22534        &mut self,
22535        _: &RevealInFileManager,
22536        _window: &mut Window,
22537        cx: &mut Context<Self>,
22538    ) {
22539        if let Some(path) = self.target_file_abs_path(cx) {
22540            if let Some(project) = self.project() {
22541                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22542            } else {
22543                cx.reveal_path(&path);
22544            }
22545        }
22546    }
22547
22548    pub fn copy_path(
22549        &mut self,
22550        _: &zed_actions::workspace::CopyPath,
22551        _window: &mut Window,
22552        cx: &mut Context<Self>,
22553    ) {
22554        if let Some(path) = self.target_file_abs_path(cx)
22555            && let Some(path) = path.to_str()
22556        {
22557            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22558        } else {
22559            cx.propagate();
22560        }
22561    }
22562
22563    pub fn copy_relative_path(
22564        &mut self,
22565        _: &zed_actions::workspace::CopyRelativePath,
22566        _window: &mut Window,
22567        cx: &mut Context<Self>,
22568    ) {
22569        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22570            let project = self.project()?.read(cx);
22571            let path = buffer.read(cx).file()?.path();
22572            let path = path.display(project.path_style(cx));
22573            Some(path)
22574        }) {
22575            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22576        } else {
22577            cx.propagate();
22578        }
22579    }
22580
22581    /// Returns the project path for the editor's buffer, if any buffer is
22582    /// opened in the editor.
22583    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22584        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22585            buffer.read(cx).project_path(cx)
22586        } else {
22587            None
22588        }
22589    }
22590
22591    // Returns true if the editor handled a go-to-line request
22592    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22593        maybe!({
22594            let breakpoint_store = self.breakpoint_store.as_ref()?;
22595
22596            let (active_stack_frame, debug_line_pane_id) = {
22597                let store = breakpoint_store.read(cx);
22598                let active_stack_frame = store.active_position().cloned();
22599                let debug_line_pane_id = store.active_debug_line_pane_id();
22600                (active_stack_frame, debug_line_pane_id)
22601            };
22602
22603            let Some(active_stack_frame) = active_stack_frame else {
22604                self.clear_row_highlights::<ActiveDebugLine>();
22605                return None;
22606            };
22607
22608            if let Some(debug_line_pane_id) = debug_line_pane_id {
22609                if let Some(workspace) = self
22610                    .workspace
22611                    .as_ref()
22612                    .and_then(|(workspace, _)| workspace.upgrade())
22613                {
22614                    let editor_pane_id = workspace
22615                        .read(cx)
22616                        .pane_for_item_id(cx.entity_id())
22617                        .map(|pane| pane.entity_id());
22618
22619                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22620                        self.clear_row_highlights::<ActiveDebugLine>();
22621                        return None;
22622                    }
22623                }
22624            }
22625
22626            let position = active_stack_frame.position;
22627            let buffer_id = position.buffer_id?;
22628            let snapshot = self
22629                .project
22630                .as_ref()?
22631                .read(cx)
22632                .buffer_for_id(buffer_id, cx)?
22633                .read(cx)
22634                .snapshot();
22635
22636            let mut handled = false;
22637            for (id, _, ExcerptRange { context, .. }) in
22638                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22639            {
22640                if context.start.cmp(&position, &snapshot).is_ge()
22641                    || context.end.cmp(&position, &snapshot).is_lt()
22642                {
22643                    continue;
22644                }
22645                let snapshot = self.buffer.read(cx).snapshot(cx);
22646                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22647
22648                handled = true;
22649                self.clear_row_highlights::<ActiveDebugLine>();
22650
22651                self.go_to_line::<ActiveDebugLine>(
22652                    multibuffer_anchor,
22653                    Some(cx.theme().colors().editor_debugger_active_line_background),
22654                    window,
22655                    cx,
22656                );
22657
22658                cx.notify();
22659            }
22660
22661            handled.then_some(())
22662        })
22663        .is_some()
22664    }
22665
22666    pub fn copy_file_name_without_extension(
22667        &mut self,
22668        _: &CopyFileNameWithoutExtension,
22669        _: &mut Window,
22670        cx: &mut Context<Self>,
22671    ) {
22672        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22673            let file = buffer.read(cx).file()?;
22674            file.path().file_stem()
22675        }) {
22676            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22677        }
22678    }
22679
22680    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22681        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22682            let file = buffer.read(cx).file()?;
22683            Some(file.file_name(cx))
22684        }) {
22685            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22686        }
22687    }
22688
22689    pub fn toggle_git_blame(
22690        &mut self,
22691        _: &::git::Blame,
22692        window: &mut Window,
22693        cx: &mut Context<Self>,
22694    ) {
22695        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22696
22697        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22698            self.start_git_blame(true, window, cx);
22699        }
22700
22701        cx.notify();
22702    }
22703
22704    pub fn toggle_git_blame_inline(
22705        &mut self,
22706        _: &ToggleGitBlameInline,
22707        window: &mut Window,
22708        cx: &mut Context<Self>,
22709    ) {
22710        self.toggle_git_blame_inline_internal(true, window, cx);
22711        cx.notify();
22712    }
22713
22714    pub fn open_git_blame_commit(
22715        &mut self,
22716        _: &OpenGitBlameCommit,
22717        window: &mut Window,
22718        cx: &mut Context<Self>,
22719    ) {
22720        self.open_git_blame_commit_internal(window, cx);
22721    }
22722
22723    fn open_git_blame_commit_internal(
22724        &mut self,
22725        window: &mut Window,
22726        cx: &mut Context<Self>,
22727    ) -> Option<()> {
22728        let blame = self.blame.as_ref()?;
22729        let snapshot = self.snapshot(window, cx);
22730        let cursor = self
22731            .selections
22732            .newest::<Point>(&snapshot.display_snapshot)
22733            .head();
22734        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22735        let (_, blame_entry) = blame
22736            .update(cx, |blame, cx| {
22737                blame
22738                    .blame_for_rows(
22739                        &[RowInfo {
22740                            buffer_id: Some(buffer.remote_id()),
22741                            buffer_row: Some(point.row),
22742                            ..Default::default()
22743                        }],
22744                        cx,
22745                    )
22746                    .next()
22747            })
22748            .flatten()?;
22749        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22750        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22751        let workspace = self.workspace()?.downgrade();
22752        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22753        None
22754    }
22755
22756    pub fn git_blame_inline_enabled(&self) -> bool {
22757        self.git_blame_inline_enabled
22758    }
22759
22760    pub fn toggle_selection_menu(
22761        &mut self,
22762        _: &ToggleSelectionMenu,
22763        _: &mut Window,
22764        cx: &mut Context<Self>,
22765    ) {
22766        self.show_selection_menu = self
22767            .show_selection_menu
22768            .map(|show_selections_menu| !show_selections_menu)
22769            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22770
22771        cx.notify();
22772    }
22773
22774    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22775        self.show_selection_menu
22776            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22777    }
22778
22779    fn start_git_blame(
22780        &mut self,
22781        user_triggered: bool,
22782        window: &mut Window,
22783        cx: &mut Context<Self>,
22784    ) {
22785        if let Some(project) = self.project() {
22786            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22787                && buffer.read(cx).file().is_none()
22788            {
22789                return;
22790            }
22791
22792            let focused = self.focus_handle(cx).contains_focused(window, cx);
22793
22794            let project = project.clone();
22795            let blame = cx
22796                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22797            self.blame_subscription =
22798                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22799            self.blame = Some(blame);
22800        }
22801    }
22802
22803    fn toggle_git_blame_inline_internal(
22804        &mut self,
22805        user_triggered: bool,
22806        window: &mut Window,
22807        cx: &mut Context<Self>,
22808    ) {
22809        if self.git_blame_inline_enabled {
22810            self.git_blame_inline_enabled = false;
22811            self.show_git_blame_inline = false;
22812            self.show_git_blame_inline_delay_task.take();
22813        } else {
22814            self.git_blame_inline_enabled = true;
22815            self.start_git_blame_inline(user_triggered, window, cx);
22816        }
22817
22818        cx.notify();
22819    }
22820
22821    fn start_git_blame_inline(
22822        &mut self,
22823        user_triggered: bool,
22824        window: &mut Window,
22825        cx: &mut Context<Self>,
22826    ) {
22827        self.start_git_blame(user_triggered, window, cx);
22828
22829        if ProjectSettings::get_global(cx)
22830            .git
22831            .inline_blame_delay()
22832            .is_some()
22833        {
22834            self.start_inline_blame_timer(window, cx);
22835        } else {
22836            self.show_git_blame_inline = true
22837        }
22838    }
22839
22840    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22841        self.blame.as_ref()
22842    }
22843
22844    pub fn show_git_blame_gutter(&self) -> bool {
22845        self.show_git_blame_gutter
22846    }
22847
22848    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
22849        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
22850    }
22851
22852    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
22853        self.show_git_blame_inline
22854            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
22855            && !self.newest_selection_head_on_empty_line(cx)
22856            && self.has_blame_entries(cx)
22857    }
22858
22859    fn has_blame_entries(&self, cx: &App) -> bool {
22860        self.blame()
22861            .is_some_and(|blame| blame.read(cx).has_generated_entries())
22862    }
22863
22864    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
22865        let cursor_anchor = self.selections.newest_anchor().head();
22866
22867        let snapshot = self.buffer.read(cx).snapshot(cx);
22868        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
22869
22870        snapshot.line_len(buffer_row) == 0
22871    }
22872
22873    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
22874        let buffer_and_selection = maybe!({
22875            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22876            let selection_range = selection.range();
22877
22878            let multi_buffer = self.buffer().read(cx);
22879            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
22880            let buffer_ranges = multi_buffer_snapshot
22881                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
22882
22883            let (buffer, range, _) = if selection.reversed {
22884                buffer_ranges.first()
22885            } else {
22886                buffer_ranges.last()
22887            }?;
22888
22889            let buffer_range = range.to_point(buffer);
22890
22891            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
22892                return Some((
22893                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
22894                    buffer_range.start.row..buffer_range.end.row,
22895                ));
22896            };
22897
22898            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
22899            let start =
22900                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
22901            let end =
22902                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
22903
22904            Some((
22905                multi_buffer.buffer(buffer.remote_id()).unwrap(),
22906                start.row..end.row,
22907            ))
22908        });
22909
22910        let Some((buffer, selection)) = buffer_and_selection else {
22911            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
22912        };
22913
22914        let Some(project) = self.project() else {
22915            return Task::ready(Err(anyhow!("editor does not have project")));
22916        };
22917
22918        project.update(cx, |project, cx| {
22919            project.get_permalink_to_line(&buffer, selection, cx)
22920        })
22921    }
22922
22923    pub fn copy_permalink_to_line(
22924        &mut self,
22925        _: &CopyPermalinkToLine,
22926        window: &mut Window,
22927        cx: &mut Context<Self>,
22928    ) {
22929        let permalink_task = self.get_permalink_to_line(cx);
22930        let workspace = self.workspace();
22931
22932        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22933            Ok(permalink) => {
22934                cx.update(|_, cx| {
22935                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
22936                })
22937                .ok();
22938            }
22939            Err(err) => {
22940                let message = format!("Failed to copy permalink: {err}");
22941
22942                anyhow::Result::<()>::Err(err).log_err();
22943
22944                if let Some(workspace) = workspace {
22945                    workspace
22946                        .update_in(cx, |workspace, _, cx| {
22947                            struct CopyPermalinkToLine;
22948
22949                            workspace.show_toast(
22950                                Toast::new(
22951                                    NotificationId::unique::<CopyPermalinkToLine>(),
22952                                    message,
22953                                ),
22954                                cx,
22955                            )
22956                        })
22957                        .ok();
22958                }
22959            }
22960        })
22961        .detach();
22962    }
22963
22964    pub fn copy_file_location(
22965        &mut self,
22966        _: &CopyFileLocation,
22967        _: &mut Window,
22968        cx: &mut Context<Self>,
22969    ) {
22970        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22971
22972        let start_line = selection.start.row + 1;
22973        let end_line = selection.end.row + 1;
22974
22975        let end_line = if selection.end.column == 0 && end_line > start_line {
22976            end_line - 1
22977        } else {
22978            end_line
22979        };
22980
22981        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22982            let project = self.project()?.read(cx);
22983            let file = buffer.read(cx).file()?;
22984            let path = file.path().display(project.path_style(cx));
22985
22986            let location = if start_line == end_line {
22987                format!("{path}:{start_line}")
22988            } else {
22989                format!("{path}:{start_line}-{end_line}")
22990            };
22991            Some(location)
22992        }) {
22993            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
22994        }
22995    }
22996
22997    pub fn open_permalink_to_line(
22998        &mut self,
22999        _: &OpenPermalinkToLine,
23000        window: &mut Window,
23001        cx: &mut Context<Self>,
23002    ) {
23003        let permalink_task = self.get_permalink_to_line(cx);
23004        let workspace = self.workspace();
23005
23006        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23007            Ok(permalink) => {
23008                cx.update(|_, cx| {
23009                    cx.open_url(permalink.as_ref());
23010                })
23011                .ok();
23012            }
23013            Err(err) => {
23014                let message = format!("Failed to open permalink: {err}");
23015
23016                anyhow::Result::<()>::Err(err).log_err();
23017
23018                if let Some(workspace) = workspace {
23019                    workspace.update(cx, |workspace, cx| {
23020                        struct OpenPermalinkToLine;
23021
23022                        workspace.show_toast(
23023                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23024                            cx,
23025                        )
23026                    });
23027                }
23028            }
23029        })
23030        .detach();
23031    }
23032
23033    pub fn insert_uuid_v4(
23034        &mut self,
23035        _: &InsertUuidV4,
23036        window: &mut Window,
23037        cx: &mut Context<Self>,
23038    ) {
23039        self.insert_uuid(UuidVersion::V4, window, cx);
23040    }
23041
23042    pub fn insert_uuid_v7(
23043        &mut self,
23044        _: &InsertUuidV7,
23045        window: &mut Window,
23046        cx: &mut Context<Self>,
23047    ) {
23048        self.insert_uuid(UuidVersion::V7, window, cx);
23049    }
23050
23051    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23052        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23053        self.transact(window, cx, |this, window, cx| {
23054            let edits = this
23055                .selections
23056                .all::<Point>(&this.display_snapshot(cx))
23057                .into_iter()
23058                .map(|selection| {
23059                    let uuid = match version {
23060                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23061                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23062                    };
23063
23064                    (selection.range(), uuid.to_string())
23065                });
23066            this.edit(edits, cx);
23067            this.refresh_edit_prediction(true, false, window, cx);
23068        });
23069    }
23070
23071    pub fn open_selections_in_multibuffer(
23072        &mut self,
23073        _: &OpenSelectionsInMultibuffer,
23074        window: &mut Window,
23075        cx: &mut Context<Self>,
23076    ) {
23077        let multibuffer = self.buffer.read(cx);
23078
23079        let Some(buffer) = multibuffer.as_singleton() else {
23080            return;
23081        };
23082
23083        let Some(workspace) = self.workspace() else {
23084            return;
23085        };
23086
23087        let title = multibuffer.title(cx).to_string();
23088
23089        let locations = self
23090            .selections
23091            .all_anchors(&self.display_snapshot(cx))
23092            .iter()
23093            .map(|selection| {
23094                (
23095                    buffer.clone(),
23096                    (selection.start.text_anchor..selection.end.text_anchor)
23097                        .to_point(buffer.read(cx)),
23098                )
23099            })
23100            .into_group_map();
23101
23102        cx.spawn_in(window, async move |_, cx| {
23103            workspace.update_in(cx, |workspace, window, cx| {
23104                Self::open_locations_in_multibuffer(
23105                    workspace,
23106                    locations,
23107                    format!("Selections for '{title}'"),
23108                    false,
23109                    false,
23110                    MultibufferSelectionMode::All,
23111                    window,
23112                    cx,
23113                );
23114            })
23115        })
23116        .detach();
23117    }
23118
23119    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23120    /// last highlight added will be used.
23121    ///
23122    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23123    pub fn highlight_rows<T: 'static>(
23124        &mut self,
23125        range: Range<Anchor>,
23126        color: Hsla,
23127        options: RowHighlightOptions,
23128        cx: &mut Context<Self>,
23129    ) {
23130        let snapshot = self.buffer().read(cx).snapshot(cx);
23131        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23132        let ix = row_highlights.binary_search_by(|highlight| {
23133            Ordering::Equal
23134                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23135                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23136        });
23137
23138        if let Err(mut ix) = ix {
23139            let index = post_inc(&mut self.highlight_order);
23140
23141            // If this range intersects with the preceding highlight, then merge it with
23142            // the preceding highlight. Otherwise insert a new highlight.
23143            let mut merged = false;
23144            if ix > 0 {
23145                let prev_highlight = &mut row_highlights[ix - 1];
23146                if prev_highlight
23147                    .range
23148                    .end
23149                    .cmp(&range.start, &snapshot)
23150                    .is_ge()
23151                {
23152                    ix -= 1;
23153                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23154                        prev_highlight.range.end = range.end;
23155                    }
23156                    merged = true;
23157                    prev_highlight.index = index;
23158                    prev_highlight.color = color;
23159                    prev_highlight.options = options;
23160                }
23161            }
23162
23163            if !merged {
23164                row_highlights.insert(
23165                    ix,
23166                    RowHighlight {
23167                        range,
23168                        index,
23169                        color,
23170                        options,
23171                        type_id: TypeId::of::<T>(),
23172                    },
23173                );
23174            }
23175
23176            // If any of the following highlights intersect with this one, merge them.
23177            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23178                let highlight = &row_highlights[ix];
23179                if next_highlight
23180                    .range
23181                    .start
23182                    .cmp(&highlight.range.end, &snapshot)
23183                    .is_le()
23184                {
23185                    if next_highlight
23186                        .range
23187                        .end
23188                        .cmp(&highlight.range.end, &snapshot)
23189                        .is_gt()
23190                    {
23191                        row_highlights[ix].range.end = next_highlight.range.end;
23192                    }
23193                    row_highlights.remove(ix + 1);
23194                } else {
23195                    break;
23196                }
23197            }
23198        }
23199    }
23200
23201    /// Remove any highlighted row ranges of the given type that intersect the
23202    /// given ranges.
23203    pub fn remove_highlighted_rows<T: 'static>(
23204        &mut self,
23205        ranges_to_remove: Vec<Range<Anchor>>,
23206        cx: &mut Context<Self>,
23207    ) {
23208        let snapshot = self.buffer().read(cx).snapshot(cx);
23209        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23210        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23211        row_highlights.retain(|highlight| {
23212            while let Some(range_to_remove) = ranges_to_remove.peek() {
23213                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23214                    Ordering::Less | Ordering::Equal => {
23215                        ranges_to_remove.next();
23216                    }
23217                    Ordering::Greater => {
23218                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23219                            Ordering::Less | Ordering::Equal => {
23220                                return false;
23221                            }
23222                            Ordering::Greater => break,
23223                        }
23224                    }
23225                }
23226            }
23227
23228            true
23229        })
23230    }
23231
23232    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23233    pub fn clear_row_highlights<T: 'static>(&mut self) {
23234        self.highlighted_rows.remove(&TypeId::of::<T>());
23235    }
23236
23237    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23238    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23239        self.highlighted_rows
23240            .get(&TypeId::of::<T>())
23241            .map_or(&[] as &[_], |vec| vec.as_slice())
23242            .iter()
23243            .map(|highlight| (highlight.range.clone(), highlight.color))
23244    }
23245
23246    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23247    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23248    /// Allows to ignore certain kinds of highlights.
23249    pub fn highlighted_display_rows(
23250        &self,
23251        window: &mut Window,
23252        cx: &mut App,
23253    ) -> BTreeMap<DisplayRow, LineHighlight> {
23254        let snapshot = self.snapshot(window, cx);
23255        let mut used_highlight_orders = HashMap::default();
23256        self.highlighted_rows
23257            .iter()
23258            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23259            .fold(
23260                BTreeMap::<DisplayRow, LineHighlight>::new(),
23261                |mut unique_rows, highlight| {
23262                    let start = highlight.range.start.to_display_point(&snapshot);
23263                    let end = highlight.range.end.to_display_point(&snapshot);
23264                    let start_row = start.row().0;
23265                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23266                    {
23267                        end.row().0.saturating_sub(1)
23268                    } else {
23269                        end.row().0
23270                    };
23271                    for row in start_row..=end_row {
23272                        let used_index =
23273                            used_highlight_orders.entry(row).or_insert(highlight.index);
23274                        if highlight.index >= *used_index {
23275                            *used_index = highlight.index;
23276                            unique_rows.insert(
23277                                DisplayRow(row),
23278                                LineHighlight {
23279                                    include_gutter: highlight.options.include_gutter,
23280                                    border: None,
23281                                    background: highlight.color.into(),
23282                                    type_id: Some(highlight.type_id),
23283                                },
23284                            );
23285                        }
23286                    }
23287                    unique_rows
23288                },
23289            )
23290    }
23291
23292    pub fn highlighted_display_row_for_autoscroll(
23293        &self,
23294        snapshot: &DisplaySnapshot,
23295    ) -> Option<DisplayRow> {
23296        self.highlighted_rows
23297            .values()
23298            .flat_map(|highlighted_rows| highlighted_rows.iter())
23299            .filter_map(|highlight| {
23300                if highlight.options.autoscroll {
23301                    Some(highlight.range.start.to_display_point(snapshot).row())
23302                } else {
23303                    None
23304                }
23305            })
23306            .min()
23307    }
23308
23309    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23310        self.highlight_background(
23311            HighlightKey::SearchWithinRange,
23312            ranges,
23313            |_, colors| colors.colors().editor_document_highlight_read_background,
23314            cx,
23315        )
23316    }
23317
23318    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23319        self.breadcrumb_header = Some(new_header);
23320    }
23321
23322    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23323        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23324    }
23325
23326    pub fn highlight_background(
23327        &mut self,
23328        key: HighlightKey,
23329        ranges: &[Range<Anchor>],
23330        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23331        cx: &mut Context<Self>,
23332    ) {
23333        self.background_highlights
23334            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23335        self.scrollbar_marker_state.dirty = true;
23336        cx.notify();
23337    }
23338
23339    pub fn clear_background_highlights(
23340        &mut self,
23341        key: HighlightKey,
23342        cx: &mut Context<Self>,
23343    ) -> Option<BackgroundHighlight> {
23344        let text_highlights = self.background_highlights.remove(&key)?;
23345        if !text_highlights.1.is_empty() {
23346            self.scrollbar_marker_state.dirty = true;
23347            cx.notify();
23348        }
23349        Some(text_highlights)
23350    }
23351
23352    pub fn highlight_gutter<T: 'static>(
23353        &mut self,
23354        ranges: impl Into<Vec<Range<Anchor>>>,
23355        color_fetcher: fn(&App) -> Hsla,
23356        cx: &mut Context<Self>,
23357    ) {
23358        self.gutter_highlights
23359            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23360        cx.notify();
23361    }
23362
23363    pub fn clear_gutter_highlights<T: 'static>(
23364        &mut self,
23365        cx: &mut Context<Self>,
23366    ) -> Option<GutterHighlight> {
23367        cx.notify();
23368        self.gutter_highlights.remove(&TypeId::of::<T>())
23369    }
23370
23371    pub fn insert_gutter_highlight<T: 'static>(
23372        &mut self,
23373        range: Range<Anchor>,
23374        color_fetcher: fn(&App) -> Hsla,
23375        cx: &mut Context<Self>,
23376    ) {
23377        let snapshot = self.buffer().read(cx).snapshot(cx);
23378        let mut highlights = self
23379            .gutter_highlights
23380            .remove(&TypeId::of::<T>())
23381            .map(|(_, highlights)| highlights)
23382            .unwrap_or_default();
23383        let ix = highlights.binary_search_by(|highlight| {
23384            Ordering::Equal
23385                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23386                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23387        });
23388        if let Err(ix) = ix {
23389            highlights.insert(ix, range);
23390        }
23391        self.gutter_highlights
23392            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23393    }
23394
23395    pub fn remove_gutter_highlights<T: 'static>(
23396        &mut self,
23397        ranges_to_remove: Vec<Range<Anchor>>,
23398        cx: &mut Context<Self>,
23399    ) {
23400        let snapshot = self.buffer().read(cx).snapshot(cx);
23401        let Some((color_fetcher, mut gutter_highlights)) =
23402            self.gutter_highlights.remove(&TypeId::of::<T>())
23403        else {
23404            return;
23405        };
23406        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23407        gutter_highlights.retain(|highlight| {
23408            while let Some(range_to_remove) = ranges_to_remove.peek() {
23409                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23410                    Ordering::Less | Ordering::Equal => {
23411                        ranges_to_remove.next();
23412                    }
23413                    Ordering::Greater => {
23414                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23415                            Ordering::Less | Ordering::Equal => {
23416                                return false;
23417                            }
23418                            Ordering::Greater => break,
23419                        }
23420                    }
23421                }
23422            }
23423
23424            true
23425        });
23426        self.gutter_highlights
23427            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23428    }
23429
23430    #[cfg(any(test, feature = "test-support"))]
23431    pub fn all_text_highlights(
23432        &self,
23433        window: &mut Window,
23434        cx: &mut Context<Self>,
23435    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23436        let snapshot = self.snapshot(window, cx);
23437        self.display_map.update(cx, |display_map, _| {
23438            display_map
23439                .all_text_highlights()
23440                .map(|(_, highlight)| {
23441                    let (style, ranges) = highlight.as_ref();
23442                    (
23443                        *style,
23444                        ranges
23445                            .iter()
23446                            .map(|range| range.clone().to_display_points(&snapshot))
23447                            .collect(),
23448                    )
23449                })
23450                .collect()
23451        })
23452    }
23453
23454    #[cfg(any(test, feature = "test-support"))]
23455    pub fn all_text_background_highlights(
23456        &self,
23457        window: &mut Window,
23458        cx: &mut Context<Self>,
23459    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23460        let snapshot = self.snapshot(window, cx);
23461        let buffer = &snapshot.buffer_snapshot();
23462        let start = buffer.anchor_before(MultiBufferOffset(0));
23463        let end = buffer.anchor_after(buffer.len());
23464        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23465    }
23466
23467    #[cfg(any(test, feature = "test-support"))]
23468    pub fn sorted_background_highlights_in_range(
23469        &self,
23470        search_range: Range<Anchor>,
23471        display_snapshot: &DisplaySnapshot,
23472        theme: &Theme,
23473    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23474        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23475        res.sort_by(|a, b| {
23476            a.0.start
23477                .cmp(&b.0.start)
23478                .then_with(|| a.0.end.cmp(&b.0.end))
23479                .then_with(|| a.1.cmp(&b.1))
23480        });
23481        res
23482    }
23483
23484    #[cfg(any(test, feature = "test-support"))]
23485    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23486        let snapshot = self.buffer().read(cx).snapshot(cx);
23487
23488        let highlights = self
23489            .background_highlights
23490            .get(&HighlightKey::BufferSearchHighlights);
23491
23492        if let Some((_color, ranges)) = highlights {
23493            ranges
23494                .iter()
23495                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23496                .collect_vec()
23497        } else {
23498            vec![]
23499        }
23500    }
23501
23502    fn document_highlights_for_position<'a>(
23503        &'a self,
23504        position: Anchor,
23505        buffer: &'a MultiBufferSnapshot,
23506    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23507        let read_highlights = self
23508            .background_highlights
23509            .get(&HighlightKey::DocumentHighlightRead)
23510            .map(|h| &h.1);
23511        let write_highlights = self
23512            .background_highlights
23513            .get(&HighlightKey::DocumentHighlightWrite)
23514            .map(|h| &h.1);
23515        let left_position = position.bias_left(buffer);
23516        let right_position = position.bias_right(buffer);
23517        read_highlights
23518            .into_iter()
23519            .chain(write_highlights)
23520            .flat_map(move |ranges| {
23521                let start_ix = match ranges.binary_search_by(|probe| {
23522                    let cmp = probe.end.cmp(&left_position, buffer);
23523                    if cmp.is_ge() {
23524                        Ordering::Greater
23525                    } else {
23526                        Ordering::Less
23527                    }
23528                }) {
23529                    Ok(i) | Err(i) => i,
23530                };
23531
23532                ranges[start_ix..]
23533                    .iter()
23534                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23535            })
23536    }
23537
23538    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23539        self.background_highlights
23540            .get(&key)
23541            .is_some_and(|(_, highlights)| !highlights.is_empty())
23542    }
23543
23544    /// Returns all background highlights for a given range.
23545    ///
23546    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23547    pub fn background_highlights_in_range(
23548        &self,
23549        search_range: Range<Anchor>,
23550        display_snapshot: &DisplaySnapshot,
23551        theme: &Theme,
23552    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23553        let mut results = Vec::new();
23554        for (color_fetcher, ranges) in self.background_highlights.values() {
23555            let start_ix = match ranges.binary_search_by(|probe| {
23556                let cmp = probe
23557                    .end
23558                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23559                if cmp.is_gt() {
23560                    Ordering::Greater
23561                } else {
23562                    Ordering::Less
23563                }
23564            }) {
23565                Ok(i) | Err(i) => i,
23566            };
23567            for (index, range) in ranges[start_ix..].iter().enumerate() {
23568                if range
23569                    .start
23570                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23571                    .is_ge()
23572                {
23573                    break;
23574                }
23575
23576                let color = color_fetcher(&(start_ix + index), theme);
23577                let start = range.start.to_display_point(display_snapshot);
23578                let end = range.end.to_display_point(display_snapshot);
23579                results.push((start..end, color))
23580            }
23581        }
23582        results
23583    }
23584
23585    pub fn gutter_highlights_in_range(
23586        &self,
23587        search_range: Range<Anchor>,
23588        display_snapshot: &DisplaySnapshot,
23589        cx: &App,
23590    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23591        let mut results = Vec::new();
23592        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23593            let color = color_fetcher(cx);
23594            let start_ix = match ranges.binary_search_by(|probe| {
23595                let cmp = probe
23596                    .end
23597                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23598                if cmp.is_gt() {
23599                    Ordering::Greater
23600                } else {
23601                    Ordering::Less
23602                }
23603            }) {
23604                Ok(i) | Err(i) => i,
23605            };
23606            for range in &ranges[start_ix..] {
23607                if range
23608                    .start
23609                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23610                    .is_ge()
23611                {
23612                    break;
23613                }
23614
23615                let start = range.start.to_display_point(display_snapshot);
23616                let end = range.end.to_display_point(display_snapshot);
23617                results.push((start..end, color))
23618            }
23619        }
23620        results
23621    }
23622
23623    /// Get the text ranges corresponding to the redaction query
23624    pub fn redacted_ranges(
23625        &self,
23626        search_range: Range<Anchor>,
23627        display_snapshot: &DisplaySnapshot,
23628        cx: &App,
23629    ) -> Vec<Range<DisplayPoint>> {
23630        display_snapshot
23631            .buffer_snapshot()
23632            .redacted_ranges(search_range, |file| {
23633                if let Some(file) = file {
23634                    file.is_private()
23635                        && EditorSettings::get(
23636                            Some(SettingsLocation {
23637                                worktree_id: file.worktree_id(cx),
23638                                path: file.path().as_ref(),
23639                            }),
23640                            cx,
23641                        )
23642                        .redact_private_values
23643                } else {
23644                    false
23645                }
23646            })
23647            .map(|range| {
23648                range.start.to_display_point(display_snapshot)
23649                    ..range.end.to_display_point(display_snapshot)
23650            })
23651            .collect()
23652    }
23653
23654    pub fn highlight_text_key(
23655        &mut self,
23656        key: HighlightKey,
23657        ranges: Vec<Range<Anchor>>,
23658        style: HighlightStyle,
23659        merge: bool,
23660        cx: &mut Context<Self>,
23661    ) {
23662        self.display_map.update(cx, |map, cx| {
23663            map.highlight_text(key, ranges, style, merge, cx);
23664        });
23665        cx.notify();
23666    }
23667
23668    pub fn highlight_text(
23669        &mut self,
23670        key: HighlightKey,
23671        ranges: Vec<Range<Anchor>>,
23672        style: HighlightStyle,
23673        cx: &mut Context<Self>,
23674    ) {
23675        self.display_map.update(cx, |map, cx| {
23676            map.highlight_text(key, ranges, style, false, cx)
23677        });
23678        cx.notify();
23679    }
23680
23681    pub fn text_highlights<'a>(
23682        &'a self,
23683        key: HighlightKey,
23684        cx: &'a App,
23685    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23686        self.display_map.read(cx).text_highlights(key)
23687    }
23688
23689    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23690        let cleared = self
23691            .display_map
23692            .update(cx, |map, _| map.clear_highlights(key));
23693        if cleared {
23694            cx.notify();
23695        }
23696    }
23697
23698    pub fn clear_highlights_with(
23699        &mut self,
23700        f: &mut dyn FnMut(&HighlightKey) -> bool,
23701        cx: &mut Context<Self>,
23702    ) {
23703        let cleared = self
23704            .display_map
23705            .update(cx, |map, _| map.clear_highlights_with(f));
23706        if cleared {
23707            cx.notify();
23708        }
23709    }
23710
23711    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23712        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23713            && self.focus_handle.is_focused(window)
23714    }
23715
23716    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23717        self.show_cursor_when_unfocused = is_enabled;
23718        cx.notify();
23719    }
23720
23721    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23722        cx.notify();
23723    }
23724
23725    fn on_debug_session_event(
23726        &mut self,
23727        _session: Entity<Session>,
23728        event: &SessionEvent,
23729        cx: &mut Context<Self>,
23730    ) {
23731        if let SessionEvent::InvalidateInlineValue = event {
23732            self.refresh_inline_values(cx);
23733        }
23734    }
23735
23736    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23737        let Some(semantics) = self.semantics_provider.clone() else {
23738            return;
23739        };
23740
23741        if !self.inline_value_cache.enabled {
23742            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23743            self.splice_inlays(&inlays, Vec::new(), cx);
23744            return;
23745        }
23746
23747        let current_execution_position = self
23748            .highlighted_rows
23749            .get(&TypeId::of::<ActiveDebugLine>())
23750            .and_then(|lines| lines.last().map(|line| line.range.end));
23751
23752        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23753            let inline_values = editor
23754                .update(cx, |editor, cx| {
23755                    let Some(current_execution_position) = current_execution_position else {
23756                        return Some(Task::ready(Ok(Vec::new())));
23757                    };
23758
23759                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23760                        let snapshot = buffer.snapshot(cx);
23761
23762                        let excerpt = snapshot.excerpt_containing(
23763                            current_execution_position..current_execution_position,
23764                        )?;
23765
23766                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23767                    })?;
23768
23769                    if current_execution_position
23770                        .text_anchor
23771                        .buffer_id
23772                        .is_some_and(|id| id != buffer.read(cx).remote_id())
23773                    {
23774                        return Some(Task::ready(Ok(Vec::new())));
23775                    }
23776
23777                    let range =
23778                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23779
23780                    semantics.inline_values(buffer, range, cx)
23781                })
23782                .ok()
23783                .flatten()?
23784                .await
23785                .context("refreshing debugger inlays")
23786                .log_err()?;
23787
23788            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23789
23790            for (buffer_id, inline_value) in inline_values
23791                .into_iter()
23792                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23793            {
23794                buffer_inline_values
23795                    .entry(buffer_id)
23796                    .or_default()
23797                    .push(inline_value);
23798            }
23799
23800            editor
23801                .update(cx, |editor, cx| {
23802                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23803                    let mut new_inlays = Vec::default();
23804
23805                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23806                        let buffer_id = buffer_snapshot.remote_id();
23807                        buffer_inline_values
23808                            .get(&buffer_id)
23809                            .into_iter()
23810                            .flatten()
23811                            .for_each(|hint| {
23812                                let inlay = Inlay::debugger(
23813                                    post_inc(&mut editor.next_inlay_id),
23814                                    Anchor::in_buffer(excerpt_id, hint.position),
23815                                    hint.text(),
23816                                );
23817                                if !inlay.text().chars().contains(&'\n') {
23818                                    new_inlays.push(inlay);
23819                                }
23820                            });
23821                    }
23822
23823                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23824                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23825
23826                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23827                })
23828                .ok()?;
23829            Some(())
23830        });
23831    }
23832
23833    fn on_buffer_event(
23834        &mut self,
23835        multibuffer: &Entity<MultiBuffer>,
23836        event: &multi_buffer::Event,
23837        window: &mut Window,
23838        cx: &mut Context<Self>,
23839    ) {
23840        match event {
23841            multi_buffer::Event::Edited {
23842                edited_buffer,
23843                is_local,
23844            } => {
23845                self.scrollbar_marker_state.dirty = true;
23846                self.active_indent_guides_state.dirty = true;
23847                self.refresh_active_diagnostics(cx);
23848                self.refresh_code_actions(window, cx);
23849                self.refresh_single_line_folds(window, cx);
23850                let snapshot = self.snapshot(window, cx);
23851                self.refresh_matching_bracket_highlights(&snapshot, cx);
23852                self.refresh_outline_symbols_at_cursor(cx);
23853                self.refresh_sticky_headers(&snapshot, cx);
23854                if *is_local && self.has_active_edit_prediction() {
23855                    self.update_visible_edit_prediction(window, cx);
23856                }
23857
23858                // Clean up orphaned review comments after edits
23859                self.cleanup_orphaned_review_comments(cx);
23860
23861                if let Some(buffer) = edited_buffer {
23862                    if buffer.read(cx).file().is_none() {
23863                        cx.emit(EditorEvent::TitleChanged);
23864                    }
23865
23866                    if self.project.is_some() {
23867                        let buffer_id = buffer.read(cx).remote_id();
23868                        self.register_buffer(buffer_id, cx);
23869                        self.update_lsp_data(Some(buffer_id), window, cx);
23870                        self.refresh_inlay_hints(
23871                            InlayHintRefreshReason::BufferEdited(buffer_id),
23872                            cx,
23873                        );
23874                    }
23875                }
23876
23877                cx.emit(EditorEvent::BufferEdited);
23878                cx.emit(SearchEvent::MatchesInvalidated);
23879
23880                let Some(project) = &self.project else { return };
23881                let (telemetry, is_via_ssh) = {
23882                    let project = project.read(cx);
23883                    let telemetry = project.client().telemetry().clone();
23884                    let is_via_ssh = project.is_via_remote_server();
23885                    (telemetry, is_via_ssh)
23886                };
23887                telemetry.log_edit_event("editor", is_via_ssh);
23888            }
23889            multi_buffer::Event::ExcerptsAdded {
23890                buffer,
23891                predecessor,
23892                excerpts,
23893            } => {
23894                let buffer_id = buffer.read(cx).remote_id();
23895                if self.buffer.read(cx).diff_for(buffer_id).is_none()
23896                    && let Some(project) = &self.project
23897                {
23898                    update_uncommitted_diff_for_buffer(
23899                        cx.entity(),
23900                        project,
23901                        [buffer.clone()],
23902                        self.buffer.clone(),
23903                        cx,
23904                    )
23905                    .detach();
23906                }
23907                self.semantic_token_state
23908                    .invalidate_buffer(&buffer.read(cx).remote_id());
23909                self.update_lsp_data(Some(buffer_id), window, cx);
23910                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23911                self.refresh_runnables(None, window, cx);
23912                self.colorize_brackets(false, cx);
23913                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
23914                cx.emit(EditorEvent::ExcerptsAdded {
23915                    buffer: buffer.clone(),
23916                    predecessor: *predecessor,
23917                    excerpts: excerpts.clone(),
23918                });
23919            }
23920            multi_buffer::Event::ExcerptsRemoved {
23921                ids,
23922                removed_buffer_ids,
23923            } => {
23924                if let Some(inlay_hints) = &mut self.inlay_hints {
23925                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
23926                }
23927                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
23928                for buffer_id in removed_buffer_ids {
23929                    self.registered_buffers.remove(buffer_id);
23930                    self.clear_runnables(Some(*buffer_id));
23931                    self.semantic_token_state.invalidate_buffer(buffer_id);
23932                    self.display_map.update(cx, |display_map, cx| {
23933                        display_map.invalidate_semantic_highlights(*buffer_id);
23934                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
23935                    });
23936                }
23937
23938                self.display_map.update(cx, |display_map, cx| {
23939                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
23940                });
23941
23942                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23943                cx.emit(EditorEvent::ExcerptsRemoved {
23944                    ids: ids.clone(),
23945                    removed_buffer_ids: removed_buffer_ids.clone(),
23946                });
23947            }
23948            multi_buffer::Event::ExcerptsEdited {
23949                excerpt_ids,
23950                buffer_ids,
23951            } => {
23952                self.display_map.update(cx, |map, cx| {
23953                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
23954                });
23955                cx.emit(EditorEvent::ExcerptsEdited {
23956                    ids: excerpt_ids.clone(),
23957                });
23958            }
23959            multi_buffer::Event::ExcerptsExpanded { ids } => {
23960                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23961                self.refresh_document_highlights(cx);
23962                let snapshot = multibuffer.read(cx).snapshot(cx);
23963                for id in ids {
23964                    self.bracket_fetched_tree_sitter_chunks.remove(id);
23965                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
23966                        self.semantic_token_state
23967                            .invalidate_buffer(&buffer.remote_id());
23968                    }
23969                }
23970                self.colorize_brackets(false, cx);
23971                self.update_lsp_data(None, window, cx);
23972                self.refresh_runnables(None, window, cx);
23973                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
23974            }
23975            multi_buffer::Event::Reparsed(buffer_id) => {
23976                self.refresh_runnables(Some(*buffer_id), window, cx);
23977                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
23978                self.colorize_brackets(true, cx);
23979                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23980
23981                cx.emit(EditorEvent::Reparsed(*buffer_id));
23982            }
23983            multi_buffer::Event::DiffHunksToggled => {
23984                self.refresh_runnables(None, window, cx);
23985            }
23986            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
23987                if !is_fresh_language {
23988                    self.registered_buffers.remove(&buffer_id);
23989                }
23990                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23991                cx.emit(EditorEvent::Reparsed(*buffer_id));
23992                self.update_edit_prediction_settings(cx);
23993                cx.notify();
23994            }
23995            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
23996            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
23997            multi_buffer::Event::FileHandleChanged
23998            | multi_buffer::Event::Reloaded
23999            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24000            multi_buffer::Event::DiagnosticsUpdated => {
24001                self.update_diagnostics_state(window, cx);
24002            }
24003            _ => {}
24004        };
24005    }
24006
24007    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24008        if !self.diagnostics_enabled() {
24009            return;
24010        }
24011        self.refresh_active_diagnostics(cx);
24012        self.refresh_inline_diagnostics(true, window, cx);
24013        self.scrollbar_marker_state.dirty = true;
24014        cx.notify();
24015    }
24016
24017    pub fn start_temporary_diff_override(&mut self) {
24018        self.load_diff_task.take();
24019        self.temporary_diff_override = true;
24020    }
24021
24022    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24023        self.temporary_diff_override = false;
24024        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24025        self.buffer.update(cx, |buffer, cx| {
24026            buffer.set_all_diff_hunks_collapsed(cx);
24027        });
24028
24029        if let Some(project) = self.project.clone() {
24030            self.load_diff_task = Some(
24031                update_uncommitted_diff_for_buffer(
24032                    cx.entity(),
24033                    &project,
24034                    self.buffer.read(cx).all_buffers(),
24035                    self.buffer.clone(),
24036                    cx,
24037                )
24038                .shared(),
24039            );
24040        }
24041    }
24042
24043    fn on_display_map_changed(
24044        &mut self,
24045        _: Entity<DisplayMap>,
24046        _: &mut Window,
24047        cx: &mut Context<Self>,
24048    ) {
24049        cx.notify();
24050    }
24051
24052    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24053        if !self.mode.is_full() {
24054            return None;
24055        }
24056
24057        let theme_settings = theme::ThemeSettings::get_global(cx);
24058        let theme = cx.theme();
24059        let accent_colors = theme.accents().clone();
24060
24061        let accent_overrides = theme_settings
24062            .theme_overrides
24063            .get(theme.name.as_ref())
24064            .map(|theme_style| &theme_style.accents)
24065            .into_iter()
24066            .flatten()
24067            .chain(
24068                theme_settings
24069                    .experimental_theme_overrides
24070                    .as_ref()
24071                    .map(|overrides| &overrides.accents)
24072                    .into_iter()
24073                    .flatten(),
24074            )
24075            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24076            .collect();
24077
24078        Some(AccentData {
24079            colors: accent_colors,
24080            overrides: accent_overrides,
24081        })
24082    }
24083
24084    fn fetch_applicable_language_settings(
24085        &self,
24086        cx: &App,
24087    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24088        if !self.mode.is_full() {
24089            return HashMap::default();
24090        }
24091
24092        self.buffer().read(cx).all_buffers().into_iter().fold(
24093            HashMap::default(),
24094            |mut acc, buffer| {
24095                let buffer = buffer.read(cx);
24096                let language = buffer.language().map(|language| language.name());
24097                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24098                    let file = buffer.file();
24099                    v.insert(language_settings(language, file, cx).into_owned());
24100                }
24101                acc
24102            },
24103        )
24104    }
24105
24106    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24107        let new_language_settings = self.fetch_applicable_language_settings(cx);
24108        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24109        self.applicable_language_settings = new_language_settings;
24110
24111        let new_accents = self.fetch_accent_data(cx);
24112        let accents_changed = new_accents != self.accent_data;
24113        self.accent_data = new_accents;
24114
24115        if self.diagnostics_enabled() {
24116            let new_severity = EditorSettings::get_global(cx)
24117                .diagnostics_max_severity
24118                .unwrap_or(DiagnosticSeverity::Hint);
24119            self.set_max_diagnostics_severity(new_severity, cx);
24120        }
24121        self.refresh_runnables(None, window, cx);
24122        self.update_edit_prediction_settings(cx);
24123        self.refresh_edit_prediction(true, false, window, cx);
24124        self.refresh_inline_values(cx);
24125
24126        let old_cursor_shape = self.cursor_shape;
24127        let old_show_breadcrumbs = self.show_breadcrumbs;
24128
24129        {
24130            let editor_settings = EditorSettings::get_global(cx);
24131            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24132            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24133            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24134            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24135        }
24136
24137        if old_cursor_shape != self.cursor_shape {
24138            cx.emit(EditorEvent::CursorShapeChanged);
24139        }
24140
24141        if old_show_breadcrumbs != self.show_breadcrumbs {
24142            cx.emit(EditorEvent::BreadcrumbsChanged);
24143        }
24144
24145        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24146            let project_settings = ProjectSettings::get_global(cx);
24147            (
24148                project_settings.session.restore_unsaved_buffers,
24149                project_settings.diagnostics.inline.enabled,
24150                project_settings.git.inline_blame.enabled,
24151            )
24152        };
24153        self.buffer_serialization = self
24154            .should_serialize_buffer()
24155            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24156
24157        if self.mode.is_full() {
24158            if self.show_inline_diagnostics != show_inline_diagnostics {
24159                self.show_inline_diagnostics = show_inline_diagnostics;
24160                self.refresh_inline_diagnostics(false, window, cx);
24161            }
24162
24163            if self.git_blame_inline_enabled != inline_blame_enabled {
24164                self.toggle_git_blame_inline_internal(false, window, cx);
24165            }
24166
24167            let minimap_settings = EditorSettings::get_global(cx).minimap;
24168            if self.minimap_visibility != MinimapVisibility::Disabled {
24169                if self.minimap_visibility.settings_visibility()
24170                    != minimap_settings.minimap_enabled()
24171                {
24172                    self.set_minimap_visibility(
24173                        MinimapVisibility::for_mode(self.mode(), cx),
24174                        window,
24175                        cx,
24176                    );
24177                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24178                    minimap_entity.update(cx, |minimap_editor, cx| {
24179                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24180                    })
24181                }
24182            }
24183
24184            if language_settings_changed || accents_changed {
24185                self.colorize_brackets(true, cx);
24186            }
24187
24188            if language_settings_changed {
24189                self.clear_disabled_lsp_folding_ranges(window, cx);
24190                self.refresh_document_symbols(None, cx);
24191            }
24192
24193            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24194                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24195            }) {
24196                if !inlay_splice.is_empty() {
24197                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24198                }
24199                self.refresh_document_colors(None, window, cx);
24200            }
24201
24202            self.refresh_inlay_hints(
24203                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24204                    self.selections.newest_anchor().head(),
24205                    &self.buffer.read(cx).snapshot(cx),
24206                    cx,
24207                )),
24208                cx,
24209            );
24210
24211            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24212                .global_lsp_settings
24213                .semantic_token_rules
24214                .clone();
24215            let semantic_token_rules_changed = self
24216                .semantic_token_state
24217                .update_rules(new_semantic_token_rules);
24218            if language_settings_changed || semantic_token_rules_changed {
24219                self.invalidate_semantic_tokens(None);
24220                self.refresh_semantic_tokens(None, None, cx);
24221            }
24222        }
24223
24224        cx.notify();
24225    }
24226
24227    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24228        if !self.mode.is_full() {
24229            return;
24230        }
24231
24232        let new_accents = self.fetch_accent_data(cx);
24233        if new_accents != self.accent_data {
24234            self.accent_data = new_accents;
24235            self.colorize_brackets(true, cx);
24236        }
24237
24238        self.invalidate_semantic_tokens(None);
24239        self.refresh_semantic_tokens(None, None, cx);
24240    }
24241
24242    pub fn set_searchable(&mut self, searchable: bool) {
24243        self.searchable = searchable;
24244    }
24245
24246    pub fn searchable(&self) -> bool {
24247        self.searchable
24248    }
24249
24250    pub fn open_excerpts_in_split(
24251        &mut self,
24252        _: &OpenExcerptsSplit,
24253        window: &mut Window,
24254        cx: &mut Context<Self>,
24255    ) {
24256        self.open_excerpts_common(None, true, window, cx)
24257    }
24258
24259    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24260        self.open_excerpts_common(None, false, window, cx)
24261    }
24262
24263    pub(crate) fn open_excerpts_common(
24264        &mut self,
24265        jump_data: Option<JumpData>,
24266        split: bool,
24267        window: &mut Window,
24268        cx: &mut Context<Self>,
24269    ) {
24270        if self.buffer.read(cx).is_singleton() {
24271            cx.propagate();
24272            return;
24273        }
24274
24275        let mut new_selections_by_buffer = HashMap::default();
24276        match &jump_data {
24277            Some(JumpData::MultiBufferPoint {
24278                excerpt_id,
24279                position,
24280                anchor,
24281                line_offset_from_top,
24282            }) => {
24283                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24284                if let Some(buffer) = multi_buffer_snapshot
24285                    .buffer_id_for_excerpt(*excerpt_id)
24286                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24287                {
24288                    let buffer_snapshot = buffer.read(cx).snapshot();
24289                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24290                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24291                    } else {
24292                        buffer_snapshot.clip_point(*position, Bias::Left)
24293                    };
24294                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24295                    new_selections_by_buffer.insert(
24296                        buffer,
24297                        (
24298                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24299                            Some(*line_offset_from_top),
24300                        ),
24301                    );
24302                }
24303            }
24304            Some(JumpData::MultiBufferRow {
24305                row,
24306                line_offset_from_top,
24307            }) => {
24308                let point = MultiBufferPoint::new(row.0, 0);
24309                if let Some((buffer, buffer_point, _)) =
24310                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24311                {
24312                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24313                    new_selections_by_buffer
24314                        .entry(buffer)
24315                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24316                        .0
24317                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24318                }
24319            }
24320            None => {
24321                let selections = self
24322                    .selections
24323                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24324                let multi_buffer = self.buffer.read(cx);
24325                for selection in selections {
24326                    for (snapshot, range, _, anchor) in multi_buffer
24327                        .snapshot(cx)
24328                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24329                    {
24330                        if let Some(anchor) = anchor {
24331                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24332                            else {
24333                                continue;
24334                            };
24335                            let offset = text::ToOffset::to_offset(
24336                                &anchor.text_anchor,
24337                                &buffer_handle.read(cx).snapshot(),
24338                            );
24339                            let range = BufferOffset(offset)..BufferOffset(offset);
24340                            new_selections_by_buffer
24341                                .entry(buffer_handle)
24342                                .or_insert((Vec::new(), None))
24343                                .0
24344                                .push(range)
24345                        } else {
24346                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24347                            else {
24348                                continue;
24349                            };
24350                            new_selections_by_buffer
24351                                .entry(buffer_handle)
24352                                .or_insert((Vec::new(), None))
24353                                .0
24354                                .push(range)
24355                        }
24356                    }
24357                }
24358            }
24359        }
24360
24361        if self.delegate_open_excerpts {
24362            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24363                .into_iter()
24364                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24365                .collect();
24366            if !selections_by_buffer.is_empty() {
24367                cx.emit(EditorEvent::OpenExcerptsRequested {
24368                    selections_by_buffer,
24369                    split,
24370                });
24371            }
24372            return;
24373        }
24374
24375        let Some(workspace) = self.workspace() else {
24376            cx.propagate();
24377            return;
24378        };
24379
24380        new_selections_by_buffer
24381            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24382
24383        if new_selections_by_buffer.is_empty() {
24384            return;
24385        }
24386
24387        Self::open_buffers_in_workspace(
24388            workspace.downgrade(),
24389            new_selections_by_buffer,
24390            split,
24391            window,
24392            cx,
24393        );
24394    }
24395
24396    pub(crate) fn open_buffers_in_workspace(
24397        workspace: WeakEntity<Workspace>,
24398        new_selections_by_buffer: HashMap<
24399            Entity<language::Buffer>,
24400            (Vec<Range<BufferOffset>>, Option<u32>),
24401        >,
24402        split: bool,
24403        window: &mut Window,
24404        cx: &mut App,
24405    ) {
24406        // We defer the pane interaction because we ourselves are a workspace item
24407        // and activating a new item causes the pane to call a method on us reentrantly,
24408        // which panics if we're on the stack.
24409        window.defer(cx, move |window, cx| {
24410            workspace
24411                .update(cx, |workspace, cx| {
24412                    let pane = if split {
24413                        workspace.adjacent_pane(window, cx)
24414                    } else {
24415                        workspace.active_pane().clone()
24416                    };
24417
24418                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24419                        let buffer_read = buffer.read(cx);
24420                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24421                            (true, project::File::from_dyn(Some(file)).is_some())
24422                        } else {
24423                            (false, false)
24424                        };
24425
24426                        // If project file is none workspace.open_project_item will fail to open the excerpt
24427                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24428                        // so we check if there's a tab match in that case first
24429                        let editor = (!has_file || !is_project_file)
24430                            .then(|| {
24431                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24432                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24433                                // Instead, we try to activate the existing editor in the pane first.
24434                                let (editor, pane_item_index, pane_item_id) =
24435                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24436                                        let editor = item.downcast::<Editor>()?;
24437                                        let singleton_buffer =
24438                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24439                                        if singleton_buffer == buffer {
24440                                            Some((editor, i, item.item_id()))
24441                                        } else {
24442                                            None
24443                                        }
24444                                    })?;
24445                                pane.update(cx, |pane, cx| {
24446                                    pane.activate_item(pane_item_index, true, true, window, cx);
24447                                    if !PreviewTabsSettings::get_global(cx)
24448                                        .enable_preview_from_multibuffer
24449                                    {
24450                                        pane.unpreview_item_if_preview(pane_item_id);
24451                                    }
24452                                });
24453                                Some(editor)
24454                            })
24455                            .flatten()
24456                            .unwrap_or_else(|| {
24457                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24458                                    .enable_keep_preview_on_code_navigation;
24459                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24460                                    .enable_preview_from_multibuffer;
24461                                workspace.open_project_item::<Self>(
24462                                    pane.clone(),
24463                                    buffer,
24464                                    true,
24465                                    true,
24466                                    keep_old_preview,
24467                                    allow_new_preview,
24468                                    window,
24469                                    cx,
24470                                )
24471                            });
24472
24473                        editor.update(cx, |editor, cx| {
24474                            if has_file && !is_project_file {
24475                                editor.set_read_only(true);
24476                            }
24477                            let autoscroll = match scroll_offset {
24478                                Some(scroll_offset) => {
24479                                    Autoscroll::top_relative(scroll_offset as usize)
24480                                }
24481                                None => Autoscroll::newest(),
24482                            };
24483                            let nav_history = editor.nav_history.take();
24484                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24485                            let Some((excerpt_id, _, buffer_snapshot)) =
24486                                multibuffer_snapshot.as_singleton()
24487                            else {
24488                                return;
24489                            };
24490                            editor.change_selections(
24491                                SelectionEffects::scroll(autoscroll),
24492                                window,
24493                                cx,
24494                                |s| {
24495                                    s.select_ranges(ranges.into_iter().map(|range| {
24496                                        let range = buffer_snapshot.anchor_before(range.start)
24497                                            ..buffer_snapshot.anchor_after(range.end);
24498                                        multibuffer_snapshot
24499                                            .anchor_range_in_excerpt(excerpt_id, range)
24500                                            .unwrap()
24501                                    }));
24502                                },
24503                            );
24504                            editor.nav_history = nav_history;
24505                        });
24506                    }
24507                })
24508                .ok();
24509        });
24510    }
24511
24512    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24513        let snapshot = self.buffer.read(cx).read(cx);
24514        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24515        Some(
24516            ranges
24517                .iter()
24518                .map(move |range| {
24519                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24520                })
24521                .collect(),
24522        )
24523    }
24524
24525    fn selection_replacement_ranges(
24526        &self,
24527        range: Range<MultiBufferOffsetUtf16>,
24528        cx: &mut App,
24529    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24530        let selections = self
24531            .selections
24532            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24533        let newest_selection = selections
24534            .iter()
24535            .max_by_key(|selection| selection.id)
24536            .unwrap();
24537        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24538        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24539        let snapshot = self.buffer.read(cx).read(cx);
24540        selections
24541            .into_iter()
24542            .map(|mut selection| {
24543                selection.start.0.0 =
24544                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24545                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24546                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24547                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24548            })
24549            .collect()
24550    }
24551
24552    fn report_editor_event(
24553        &self,
24554        reported_event: ReportEditorEvent,
24555        file_extension: Option<String>,
24556        cx: &App,
24557    ) {
24558        if cfg!(any(test, feature = "test-support")) {
24559            return;
24560        }
24561
24562        let Some(project) = &self.project else { return };
24563
24564        // If None, we are in a file without an extension
24565        let file = self
24566            .buffer
24567            .read(cx)
24568            .as_singleton()
24569            .and_then(|b| b.read(cx).file());
24570        let file_extension = file_extension.or(file
24571            .as_ref()
24572            .and_then(|file| Path::new(file.file_name(cx)).extension())
24573            .and_then(|e| e.to_str())
24574            .map(|a| a.to_string()));
24575
24576        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24577            .map(|vim_mode| vim_mode.0)
24578            .unwrap_or(false);
24579
24580        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24581        let copilot_enabled = edit_predictions_provider
24582            == language::language_settings::EditPredictionProvider::Copilot;
24583        let copilot_enabled_for_language = self
24584            .buffer
24585            .read(cx)
24586            .language_settings(cx)
24587            .show_edit_predictions;
24588
24589        let project = project.read(cx);
24590        let event_type = reported_event.event_type();
24591
24592        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24593            telemetry::event!(
24594                event_type,
24595                type = if auto_saved {"autosave"} else {"manual"},
24596                file_extension,
24597                vim_mode,
24598                copilot_enabled,
24599                copilot_enabled_for_language,
24600                edit_predictions_provider,
24601                is_via_ssh = project.is_via_remote_server(),
24602            );
24603        } else {
24604            telemetry::event!(
24605                event_type,
24606                file_extension,
24607                vim_mode,
24608                copilot_enabled,
24609                copilot_enabled_for_language,
24610                edit_predictions_provider,
24611                is_via_ssh = project.is_via_remote_server(),
24612            );
24613        };
24614    }
24615
24616    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24617    /// with each line being an array of {text, highlight} objects.
24618    fn copy_highlight_json(
24619        &mut self,
24620        _: &CopyHighlightJson,
24621        window: &mut Window,
24622        cx: &mut Context<Self>,
24623    ) {
24624        #[derive(Serialize)]
24625        struct Chunk<'a> {
24626            text: String,
24627            highlight: Option<&'a str>,
24628        }
24629
24630        let snapshot = self.buffer.read(cx).snapshot(cx);
24631        let range = self
24632            .selected_text_range(false, window, cx)
24633            .and_then(|selection| {
24634                if selection.range.is_empty() {
24635                    None
24636                } else {
24637                    Some(
24638                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24639                            selection.range.start,
24640                        )))
24641                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24642                                selection.range.end,
24643                            ))),
24644                    )
24645                }
24646            })
24647            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24648
24649        let chunks = snapshot.chunks(range, true);
24650        let mut lines = Vec::new();
24651        let mut line: VecDeque<Chunk> = VecDeque::new();
24652
24653        let Some(style) = self.style.as_ref() else {
24654            return;
24655        };
24656
24657        for chunk in chunks {
24658            let highlight = chunk
24659                .syntax_highlight_id
24660                .and_then(|id| id.name(&style.syntax));
24661            let mut chunk_lines = chunk.text.split('\n').peekable();
24662            while let Some(text) = chunk_lines.next() {
24663                let mut merged_with_last_token = false;
24664                if let Some(last_token) = line.back_mut()
24665                    && last_token.highlight == highlight
24666                {
24667                    last_token.text.push_str(text);
24668                    merged_with_last_token = true;
24669                }
24670
24671                if !merged_with_last_token {
24672                    line.push_back(Chunk {
24673                        text: text.into(),
24674                        highlight,
24675                    });
24676                }
24677
24678                if chunk_lines.peek().is_some() {
24679                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24680                        line.pop_front();
24681                    }
24682                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24683                        line.pop_back();
24684                    }
24685
24686                    lines.push(mem::take(&mut line));
24687                }
24688            }
24689        }
24690
24691        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24692            return;
24693        };
24694        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24695    }
24696
24697    pub fn open_context_menu(
24698        &mut self,
24699        _: &OpenContextMenu,
24700        window: &mut Window,
24701        cx: &mut Context<Self>,
24702    ) {
24703        self.request_autoscroll(Autoscroll::newest(), cx);
24704        let position = self
24705            .selections
24706            .newest_display(&self.display_snapshot(cx))
24707            .start;
24708        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24709    }
24710
24711    pub fn replay_insert_event(
24712        &mut self,
24713        text: &str,
24714        relative_utf16_range: Option<Range<isize>>,
24715        window: &mut Window,
24716        cx: &mut Context<Self>,
24717    ) {
24718        if !self.input_enabled {
24719            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24720            return;
24721        }
24722        if let Some(relative_utf16_range) = relative_utf16_range {
24723            let selections = self
24724                .selections
24725                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24726            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24727                let new_ranges = selections.into_iter().map(|range| {
24728                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24729                        range
24730                            .head()
24731                            .0
24732                            .0
24733                            .saturating_add_signed(relative_utf16_range.start),
24734                    ));
24735                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24736                        range
24737                            .head()
24738                            .0
24739                            .0
24740                            .saturating_add_signed(relative_utf16_range.end),
24741                    ));
24742                    start..end
24743                });
24744                s.select_ranges(new_ranges);
24745            });
24746        }
24747
24748        self.handle_input(text, window, cx);
24749    }
24750
24751    pub fn is_focused(&self, window: &Window) -> bool {
24752        self.focus_handle.is_focused(window)
24753    }
24754
24755    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24756        cx.emit(EditorEvent::Focused);
24757
24758        if let Some(descendant) = self
24759            .last_focused_descendant
24760            .take()
24761            .and_then(|descendant| descendant.upgrade())
24762        {
24763            window.focus(&descendant, cx);
24764        } else {
24765            if let Some(blame) = self.blame.as_ref() {
24766                blame.update(cx, GitBlame::focus)
24767            }
24768
24769            self.blink_manager.update(cx, BlinkManager::enable);
24770            self.show_cursor_names(window, cx);
24771            self.buffer.update(cx, |buffer, cx| {
24772                buffer.finalize_last_transaction(cx);
24773                if self.leader_id.is_none() {
24774                    buffer.set_active_selections(
24775                        &self.selections.disjoint_anchors_arc(),
24776                        self.selections.line_mode(),
24777                        self.cursor_shape,
24778                        cx,
24779                    );
24780                }
24781            });
24782
24783            if let Some(position_map) = self.last_position_map.clone() {
24784                EditorElement::mouse_moved(
24785                    self,
24786                    &MouseMoveEvent {
24787                        position: window.mouse_position(),
24788                        pressed_button: None,
24789                        modifiers: window.modifiers(),
24790                    },
24791                    &position_map,
24792                    None,
24793                    window,
24794                    cx,
24795                );
24796            }
24797        }
24798    }
24799
24800    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24801        cx.emit(EditorEvent::FocusedIn)
24802    }
24803
24804    fn handle_focus_out(
24805        &mut self,
24806        event: FocusOutEvent,
24807        _window: &mut Window,
24808        cx: &mut Context<Self>,
24809    ) {
24810        if event.blurred != self.focus_handle {
24811            self.last_focused_descendant = Some(event.blurred);
24812        }
24813        self.selection_drag_state = SelectionDragState::None;
24814        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24815    }
24816
24817    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24818        self.blink_manager.update(cx, BlinkManager::disable);
24819        self.buffer
24820            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24821
24822        if let Some(blame) = self.blame.as_ref() {
24823            blame.update(cx, GitBlame::blur)
24824        }
24825        if !self.hover_state.focused(window, cx) {
24826            hide_hover(self, cx);
24827        }
24828        if !self
24829            .context_menu
24830            .borrow()
24831            .as_ref()
24832            .is_some_and(|context_menu| context_menu.focused(window, cx))
24833        {
24834            self.hide_context_menu(window, cx);
24835        }
24836        self.take_active_edit_prediction(cx);
24837        cx.emit(EditorEvent::Blurred);
24838        cx.notify();
24839    }
24840
24841    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24842        let mut pending: String = window
24843            .pending_input_keystrokes()
24844            .into_iter()
24845            .flatten()
24846            .filter_map(|keystroke| keystroke.key_char.clone())
24847            .collect();
24848
24849        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24850            pending = "".to_string();
24851        }
24852
24853        let existing_pending = self
24854            .text_highlights(HighlightKey::PendingInput, cx)
24855            .map(|(_, ranges)| ranges.to_vec());
24856        if existing_pending.is_none() && pending.is_empty() {
24857            return;
24858        }
24859        let transaction =
24860            self.transact(window, cx, |this, window, cx| {
24861                let selections = this
24862                    .selections
24863                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
24864                let edits = selections
24865                    .iter()
24866                    .map(|selection| (selection.end..selection.end, pending.clone()));
24867                this.edit(edits, cx);
24868                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24869                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
24870                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
24871                    }));
24872                });
24873                if let Some(existing_ranges) = existing_pending {
24874                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
24875                    this.edit(edits, cx);
24876                }
24877            });
24878
24879        let snapshot = self.snapshot(window, cx);
24880        let ranges = self
24881            .selections
24882            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
24883            .into_iter()
24884            .map(|selection| {
24885                snapshot.buffer_snapshot().anchor_after(selection.end)
24886                    ..snapshot
24887                        .buffer_snapshot()
24888                        .anchor_before(selection.end + pending.len())
24889            })
24890            .collect();
24891
24892        if pending.is_empty() {
24893            self.clear_highlights(HighlightKey::PendingInput, cx);
24894        } else {
24895            self.highlight_text(
24896                HighlightKey::PendingInput,
24897                ranges,
24898                HighlightStyle {
24899                    underline: Some(UnderlineStyle {
24900                        thickness: px(1.),
24901                        color: None,
24902                        wavy: false,
24903                    }),
24904                    ..Default::default()
24905                },
24906                cx,
24907            );
24908        }
24909
24910        self.ime_transaction = self.ime_transaction.or(transaction);
24911        if let Some(transaction) = self.ime_transaction {
24912            self.buffer.update(cx, |buffer, cx| {
24913                buffer.group_until_transaction(transaction, cx);
24914            });
24915        }
24916
24917        if self
24918            .text_highlights(HighlightKey::PendingInput, cx)
24919            .is_none()
24920        {
24921            self.ime_transaction.take();
24922        }
24923    }
24924
24925    pub fn register_action_renderer(
24926        &mut self,
24927        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
24928    ) -> Subscription {
24929        let id = self.next_editor_action_id.post_inc();
24930        self.editor_actions
24931            .borrow_mut()
24932            .insert(id, Box::new(listener));
24933
24934        let editor_actions = self.editor_actions.clone();
24935        Subscription::new(move || {
24936            editor_actions.borrow_mut().remove(&id);
24937        })
24938    }
24939
24940    pub fn register_action<A: Action>(
24941        &mut self,
24942        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
24943    ) -> Subscription {
24944        let id = self.next_editor_action_id.post_inc();
24945        let listener = Arc::new(listener);
24946        self.editor_actions.borrow_mut().insert(
24947            id,
24948            Box::new(move |_, window, _| {
24949                let listener = listener.clone();
24950                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
24951                    let action = action.downcast_ref().unwrap();
24952                    if phase == DispatchPhase::Bubble {
24953                        listener(action, window, cx)
24954                    }
24955                })
24956            }),
24957        );
24958
24959        let editor_actions = self.editor_actions.clone();
24960        Subscription::new(move || {
24961            editor_actions.borrow_mut().remove(&id);
24962        })
24963    }
24964
24965    pub fn file_header_size(&self) -> u32 {
24966        FILE_HEADER_HEIGHT
24967    }
24968
24969    pub fn restore(
24970        &mut self,
24971        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
24972        window: &mut Window,
24973        cx: &mut Context<Self>,
24974    ) {
24975        self.buffer().update(cx, |multi_buffer, cx| {
24976            for (buffer_id, changes) in revert_changes {
24977                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
24978                    buffer.update(cx, |buffer, cx| {
24979                        buffer.edit(
24980                            changes
24981                                .into_iter()
24982                                .map(|(range, text)| (range, text.to_string())),
24983                            None,
24984                            cx,
24985                        );
24986                    });
24987                }
24988            }
24989        });
24990        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
24991            selections.refresh()
24992        });
24993    }
24994
24995    pub fn to_pixel_point(
24996        &mut self,
24997        source: Anchor,
24998        editor_snapshot: &EditorSnapshot,
24999        window: &mut Window,
25000        cx: &mut App,
25001    ) -> Option<gpui::Point<Pixels>> {
25002        let source_point = source.to_display_point(editor_snapshot);
25003        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25004    }
25005
25006    pub fn display_to_pixel_point(
25007        &mut self,
25008        source: DisplayPoint,
25009        editor_snapshot: &EditorSnapshot,
25010        window: &mut Window,
25011        cx: &mut App,
25012    ) -> Option<gpui::Point<Pixels>> {
25013        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25014        let text_layout_details = self.text_layout_details(window, cx);
25015        let scroll_top = text_layout_details
25016            .scroll_anchor
25017            .scroll_position(editor_snapshot)
25018            .y;
25019
25020        if source.row().as_f64() < scroll_top.floor() {
25021            return None;
25022        }
25023        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25024        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25025        Some(gpui::Point::new(source_x, source_y))
25026    }
25027
25028    pub fn has_visible_completions_menu(&self) -> bool {
25029        !self.edit_prediction_preview_is_active()
25030            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25031                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25032            })
25033    }
25034
25035    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25036        if self.mode.is_minimap() {
25037            return;
25038        }
25039        self.addons
25040            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25041    }
25042
25043    pub fn unregister_addon<T: Addon>(&mut self) {
25044        self.addons.remove(&std::any::TypeId::of::<T>());
25045    }
25046
25047    pub fn addon<T: Addon>(&self) -> Option<&T> {
25048        let type_id = std::any::TypeId::of::<T>();
25049        self.addons
25050            .get(&type_id)
25051            .and_then(|item| item.to_any().downcast_ref::<T>())
25052    }
25053
25054    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25055        let type_id = std::any::TypeId::of::<T>();
25056        self.addons
25057            .get_mut(&type_id)
25058            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25059    }
25060
25061    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25062        let text_layout_details = self.text_layout_details(window, cx);
25063        let style = &text_layout_details.editor_style;
25064        let font_id = window.text_system().resolve_font(&style.text.font());
25065        let font_size = style.text.font_size.to_pixels(window.rem_size());
25066        let line_height = style.text.line_height_in_pixels(window.rem_size());
25067        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25068        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25069
25070        CharacterDimensions {
25071            em_width,
25072            em_advance,
25073            line_height,
25074        }
25075    }
25076
25077    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25078        self.load_diff_task.clone()
25079    }
25080
25081    fn read_metadata_from_db(
25082        &mut self,
25083        item_id: u64,
25084        workspace_id: WorkspaceId,
25085        window: &mut Window,
25086        cx: &mut Context<Editor>,
25087    ) {
25088        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25089            && !self.mode.is_minimap()
25090            && WorkspaceSettings::get(None, cx).restore_on_startup
25091                != RestoreOnStartupBehavior::EmptyTab
25092        {
25093            let buffer_snapshot = OnceCell::new();
25094
25095            // Get file path for path-based fold lookup
25096            let file_path: Option<Arc<Path>> =
25097                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25098                    project::File::from_dyn(buffer.read(cx).file())
25099                        .map(|file| Arc::from(file.abs_path(cx)))
25100                });
25101
25102            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25103            let (folds, needs_migration) = if let Some(ref path) = file_path {
25104                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
25105                    && !folds.is_empty()
25106                {
25107                    (Some(folds), false)
25108                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25109                    && !folds.is_empty()
25110                {
25111                    // Found old editor_folds data, will migrate to file_folds
25112                    (Some(folds), true)
25113                } else {
25114                    (None, false)
25115                }
25116            } else {
25117                // No file path, try editor_folds as fallback
25118                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
25119                (folds.filter(|f| !f.is_empty()), false)
25120            };
25121
25122            if let Some(folds) = folds {
25123                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25124                let snapshot_len = snapshot.len().0;
25125
25126                // Helper: search for fingerprint in buffer, return offset if found
25127                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25128                    // Ensure we start at a character boundary (defensive)
25129                    let search_start = snapshot
25130                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25131                        .0;
25132                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25133
25134                    let mut byte_offset = search_start;
25135                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25136                        if byte_offset > search_end {
25137                            break;
25138                        }
25139                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25140                            return Some(byte_offset);
25141                        }
25142                        byte_offset += ch.len_utf8();
25143                    }
25144                    None
25145                };
25146
25147                // Track search position to handle duplicate fingerprints correctly.
25148                // Folds are stored in document order, so we advance after each match.
25149                let mut search_start = 0usize;
25150
25151                // Collect db_folds for migration (only folds with valid fingerprints)
25152                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25153
25154                let valid_folds: Vec<_> = folds
25155                    .into_iter()
25156                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25157                        // Skip folds without fingerprints (old data before migration)
25158                        let sfp = start_fp?;
25159                        let efp = end_fp?;
25160                        let efp_len = efp.len();
25161
25162                        // Fast path: check if fingerprints match at stored offsets
25163                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25164                        let start_matches = stored_start < snapshot_len
25165                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25166                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25167                        let end_matches = efp_check_pos >= stored_start
25168                            && stored_end <= snapshot_len
25169                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25170
25171                        let (new_start, new_end) = if start_matches && end_matches {
25172                            // Offsets unchanged, use stored values
25173                            (stored_start, stored_end)
25174                        } else if sfp == efp {
25175                            // Short fold: identical fingerprints can only match once per search
25176                            // Use stored fold length to compute new_end
25177                            let new_start = find_fingerprint(&sfp, search_start)?;
25178                            let fold_len = stored_end - stored_start;
25179                            let new_end = new_start + fold_len;
25180                            (new_start, new_end)
25181                        } else {
25182                            // Slow path: search for fingerprints in buffer
25183                            let new_start = find_fingerprint(&sfp, search_start)?;
25184                            // Search for end_fp after start, then add efp_len to get actual fold end
25185                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25186                            let new_end = efp_pos + efp_len;
25187                            (new_start, new_end)
25188                        };
25189
25190                        // Advance search position for next fold
25191                        search_start = new_end;
25192
25193                        // Validate fold makes sense (end must be after start)
25194                        if new_end <= new_start {
25195                            return None;
25196                        }
25197
25198                        // Collect for migration if needed
25199                        if needs_migration {
25200                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25201                        }
25202
25203                        Some(
25204                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25205                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25206                        )
25207                    })
25208                    .collect();
25209
25210                if !valid_folds.is_empty() {
25211                    self.fold_ranges(valid_folds, false, window, cx);
25212
25213                    // Migrate from editor_folds to file_folds if we loaded from old table
25214                    if needs_migration {
25215                        if let Some(ref path) = file_path {
25216                            let path = path.clone();
25217                            cx.spawn(async move |_, _| {
25218                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25219                                    .await
25220                                    .log_err();
25221                            })
25222                            .detach();
25223                        }
25224                    }
25225                }
25226            }
25227
25228            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25229                && !selections.is_empty()
25230            {
25231                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25232                // skip adding the initial selection to selection history
25233                self.selection_history.mode = SelectionHistoryMode::Skipping;
25234                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25235                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25236                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25237                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25238                    }));
25239                });
25240                self.selection_history.mode = SelectionHistoryMode::Normal;
25241            };
25242        }
25243
25244        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25245    }
25246
25247    /// Load folds from the file_folds database table by file path.
25248    /// Used when manually opening a file that was previously closed.
25249    fn load_folds_from_db(
25250        &mut self,
25251        workspace_id: WorkspaceId,
25252        file_path: PathBuf,
25253        window: &mut Window,
25254        cx: &mut Context<Editor>,
25255    ) {
25256        if self.mode.is_minimap()
25257            || WorkspaceSettings::get(None, cx).restore_on_startup
25258                == RestoreOnStartupBehavior::EmptyTab
25259        {
25260            return;
25261        }
25262
25263        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25264            return;
25265        };
25266        if folds.is_empty() {
25267            return;
25268        }
25269
25270        let snapshot = self.buffer.read(cx).snapshot(cx);
25271        let snapshot_len = snapshot.len().0;
25272
25273        // Helper: search for fingerprint in buffer, return offset if found
25274        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25275            let search_start = snapshot
25276                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25277                .0;
25278            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25279
25280            let mut byte_offset = search_start;
25281            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25282                if byte_offset > search_end {
25283                    break;
25284                }
25285                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25286                    return Some(byte_offset);
25287                }
25288                byte_offset += ch.len_utf8();
25289            }
25290            None
25291        };
25292
25293        let mut search_start = 0usize;
25294
25295        let valid_folds: Vec<_> = folds
25296            .into_iter()
25297            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25298                let sfp = start_fp?;
25299                let efp = end_fp?;
25300                let efp_len = efp.len();
25301
25302                let start_matches = stored_start < snapshot_len
25303                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25304                let efp_check_pos = stored_end.saturating_sub(efp_len);
25305                let end_matches = efp_check_pos >= stored_start
25306                    && stored_end <= snapshot_len
25307                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25308
25309                let (new_start, new_end) = if start_matches && end_matches {
25310                    (stored_start, stored_end)
25311                } else if sfp == efp {
25312                    let new_start = find_fingerprint(&sfp, search_start)?;
25313                    let fold_len = stored_end - stored_start;
25314                    let new_end = new_start + fold_len;
25315                    (new_start, new_end)
25316                } else {
25317                    let new_start = find_fingerprint(&sfp, search_start)?;
25318                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25319                    let new_end = efp_pos + efp_len;
25320                    (new_start, new_end)
25321                };
25322
25323                search_start = new_end;
25324
25325                if new_end <= new_start {
25326                    return None;
25327                }
25328
25329                Some(
25330                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25331                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25332                )
25333            })
25334            .collect();
25335
25336        if !valid_folds.is_empty() {
25337            self.fold_ranges(valid_folds, false, window, cx);
25338        }
25339    }
25340
25341    fn lsp_data_enabled(&self) -> bool {
25342        self.enable_lsp_data && self.mode().is_full()
25343    }
25344
25345    fn update_lsp_data(
25346        &mut self,
25347        for_buffer: Option<BufferId>,
25348        window: &mut Window,
25349        cx: &mut Context<'_, Self>,
25350    ) {
25351        if !self.lsp_data_enabled() {
25352            return;
25353        }
25354
25355        if let Some(buffer_id) = for_buffer {
25356            self.pull_diagnostics(buffer_id, window, cx);
25357        }
25358        self.refresh_semantic_tokens(for_buffer, None, cx);
25359        self.refresh_document_colors(for_buffer, window, cx);
25360        self.refresh_folding_ranges(for_buffer, window, cx);
25361        self.refresh_document_symbols(for_buffer, cx);
25362    }
25363
25364    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25365        if !self.lsp_data_enabled() {
25366            return;
25367        }
25368        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25369            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25370        }
25371    }
25372
25373    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25374        if !self.lsp_data_enabled() {
25375            return;
25376        }
25377
25378        if !self.registered_buffers.contains_key(&buffer_id)
25379            && let Some(project) = self.project.as_ref()
25380        {
25381            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25382                project.update(cx, |project, cx| {
25383                    self.registered_buffers.insert(
25384                        buffer_id,
25385                        project.register_buffer_with_language_servers(&buffer, cx),
25386                    );
25387                });
25388            } else {
25389                self.registered_buffers.remove(&buffer_id);
25390            }
25391        }
25392    }
25393
25394    fn create_style(&self, cx: &App) -> EditorStyle {
25395        let settings = ThemeSettings::get_global(cx);
25396
25397        let mut text_style = match self.mode {
25398            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25399                color: cx.theme().colors().editor_foreground,
25400                font_family: settings.ui_font.family.clone(),
25401                font_features: settings.ui_font.features.clone(),
25402                font_fallbacks: settings.ui_font.fallbacks.clone(),
25403                font_size: rems(0.875).into(),
25404                font_weight: settings.ui_font.weight,
25405                line_height: relative(settings.buffer_line_height.value()),
25406                ..Default::default()
25407            },
25408            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25409                color: cx.theme().colors().editor_foreground,
25410                font_family: settings.buffer_font.family.clone(),
25411                font_features: settings.buffer_font.features.clone(),
25412                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25413                font_size: settings.buffer_font_size(cx).into(),
25414                font_weight: settings.buffer_font.weight,
25415                line_height: relative(settings.buffer_line_height.value()),
25416                ..Default::default()
25417            },
25418        };
25419        if let Some(text_style_refinement) = &self.text_style_refinement {
25420            text_style.refine(text_style_refinement)
25421        }
25422
25423        let background = match self.mode {
25424            EditorMode::SingleLine => cx.theme().system().transparent,
25425            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25426            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25427            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25428        };
25429
25430        EditorStyle {
25431            background,
25432            border: cx.theme().colors().border,
25433            local_player: cx.theme().players().local(),
25434            text: text_style,
25435            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25436            syntax: cx.theme().syntax().clone(),
25437            status: cx.theme().status().clone(),
25438            inlay_hints_style: make_inlay_hints_style(cx),
25439            edit_prediction_styles: make_suggestion_styles(cx),
25440            unnecessary_code_fade: settings.unnecessary_code_fade,
25441            show_underlines: self.diagnostics_enabled(),
25442        }
25443    }
25444
25445    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25446        let multibuffer = self.buffer().read(cx);
25447        let is_singleton = multibuffer.is_singleton();
25448        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25449        let buffer = multibuffer.buffer(*buffer_id)?;
25450
25451        let buffer = buffer.read(cx);
25452        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25453        let mut breadcrumbs = if is_singleton {
25454            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25455                buffer
25456                    .snapshot()
25457                    .resolve_file_path(
25458                        self.project
25459                            .as_ref()
25460                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25461                            .unwrap_or_default(),
25462                        cx,
25463                    )
25464                    .unwrap_or_else(|| {
25465                        if multibuffer.is_singleton() {
25466                            multibuffer.title(cx).to_string()
25467                        } else {
25468                            "untitled".to_string()
25469                        }
25470                    })
25471            });
25472            vec![HighlightedText {
25473                text: text.into(),
25474                highlights: vec![],
25475            }]
25476        } else {
25477            vec![]
25478        };
25479
25480        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25481            text: symbol.text.clone().into(),
25482            highlights: symbol.highlight_ranges.clone(),
25483        }));
25484        Some(breadcrumbs)
25485    }
25486
25487    fn disable_lsp_data(&mut self) {
25488        self.enable_lsp_data = false;
25489    }
25490
25491    fn disable_runnables(&mut self) {
25492        self.enable_runnables = false;
25493    }
25494
25495    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25496        self.register_visible_buffers(cx);
25497        self.colorize_brackets(false, cx);
25498        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25499        if !self.buffer().read(cx).is_singleton() {
25500            self.update_lsp_data(None, window, cx);
25501            self.refresh_runnables(None, window, cx);
25502        }
25503    }
25504}
25505
25506fn edit_for_markdown_paste<'a>(
25507    buffer: &MultiBufferSnapshot,
25508    range: Range<MultiBufferOffset>,
25509    to_insert: &'a str,
25510    url: Option<url::Url>,
25511) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25512    if url.is_none() {
25513        return (range, Cow::Borrowed(to_insert));
25514    };
25515
25516    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25517
25518    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25519        Cow::Borrowed(to_insert)
25520    } else {
25521        Cow::Owned(format!("[{old_text}]({to_insert})"))
25522    };
25523    (range, new_text)
25524}
25525
25526fn process_completion_for_edit(
25527    completion: &Completion,
25528    intent: CompletionIntent,
25529    buffer: &Entity<Buffer>,
25530    cursor_position: &text::Anchor,
25531    cx: &mut Context<Editor>,
25532) -> CompletionEdit {
25533    let buffer = buffer.read(cx);
25534    let buffer_snapshot = buffer.snapshot();
25535    let (snippet, new_text) = if completion.is_snippet() {
25536        let mut snippet_source = completion.new_text.clone();
25537        // Workaround for typescript language server issues so that methods don't expand within
25538        // strings and functions with type expressions. The previous point is used because the query
25539        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25540        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25541        let previous_point = if previous_point.column > 0 {
25542            cursor_position.to_previous_offset(&buffer_snapshot)
25543        } else {
25544            cursor_position.to_offset(&buffer_snapshot)
25545        };
25546        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25547            && scope.prefers_label_for_snippet_in_completion()
25548            && let Some(label) = completion.label()
25549            && matches!(
25550                completion.kind(),
25551                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25552            )
25553        {
25554            snippet_source = label;
25555        }
25556        match Snippet::parse(&snippet_source).log_err() {
25557            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25558            None => (None, completion.new_text.clone()),
25559        }
25560    } else {
25561        (None, completion.new_text.clone())
25562    };
25563
25564    let mut range_to_replace = {
25565        let replace_range = &completion.replace_range;
25566        if let CompletionSource::Lsp {
25567            insert_range: Some(insert_range),
25568            ..
25569        } = &completion.source
25570        {
25571            debug_assert_eq!(
25572                insert_range.start, replace_range.start,
25573                "insert_range and replace_range should start at the same position"
25574            );
25575            debug_assert!(
25576                insert_range
25577                    .start
25578                    .cmp(cursor_position, &buffer_snapshot)
25579                    .is_le(),
25580                "insert_range should start before or at cursor position"
25581            );
25582            debug_assert!(
25583                replace_range
25584                    .start
25585                    .cmp(cursor_position, &buffer_snapshot)
25586                    .is_le(),
25587                "replace_range should start before or at cursor position"
25588            );
25589
25590            let should_replace = match intent {
25591                CompletionIntent::CompleteWithInsert => false,
25592                CompletionIntent::CompleteWithReplace => true,
25593                CompletionIntent::Complete | CompletionIntent::Compose => {
25594                    let insert_mode =
25595                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25596                            .completions
25597                            .lsp_insert_mode;
25598                    match insert_mode {
25599                        LspInsertMode::Insert => false,
25600                        LspInsertMode::Replace => true,
25601                        LspInsertMode::ReplaceSubsequence => {
25602                            let mut text_to_replace = buffer.chars_for_range(
25603                                buffer.anchor_before(replace_range.start)
25604                                    ..buffer.anchor_after(replace_range.end),
25605                            );
25606                            let mut current_needle = text_to_replace.next();
25607                            for haystack_ch in completion.label.text.chars() {
25608                                if let Some(needle_ch) = current_needle
25609                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25610                                {
25611                                    current_needle = text_to_replace.next();
25612                                }
25613                            }
25614                            current_needle.is_none()
25615                        }
25616                        LspInsertMode::ReplaceSuffix => {
25617                            if replace_range
25618                                .end
25619                                .cmp(cursor_position, &buffer_snapshot)
25620                                .is_gt()
25621                            {
25622                                let range_after_cursor = *cursor_position..replace_range.end;
25623                                let text_after_cursor = buffer
25624                                    .text_for_range(
25625                                        buffer.anchor_before(range_after_cursor.start)
25626                                            ..buffer.anchor_after(range_after_cursor.end),
25627                                    )
25628                                    .collect::<String>()
25629                                    .to_ascii_lowercase();
25630                                completion
25631                                    .label
25632                                    .text
25633                                    .to_ascii_lowercase()
25634                                    .ends_with(&text_after_cursor)
25635                            } else {
25636                                true
25637                            }
25638                        }
25639                    }
25640                }
25641            };
25642
25643            if should_replace {
25644                replace_range.clone()
25645            } else {
25646                insert_range.clone()
25647            }
25648        } else {
25649            replace_range.clone()
25650        }
25651    };
25652
25653    if range_to_replace
25654        .end
25655        .cmp(cursor_position, &buffer_snapshot)
25656        .is_lt()
25657    {
25658        range_to_replace.end = *cursor_position;
25659    }
25660
25661    let replace_range = range_to_replace.to_offset(buffer);
25662    CompletionEdit {
25663        new_text,
25664        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25665        snippet,
25666    }
25667}
25668
25669struct CompletionEdit {
25670    new_text: String,
25671    replace_range: Range<BufferOffset>,
25672    snippet: Option<Snippet>,
25673}
25674
25675fn comment_delimiter_for_newline(
25676    start_point: &Point,
25677    buffer: &MultiBufferSnapshot,
25678    language: &LanguageScope,
25679) -> Option<Arc<str>> {
25680    let delimiters = language.line_comment_prefixes();
25681    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25682    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25683
25684    let num_of_whitespaces = snapshot
25685        .chars_for_range(range.clone())
25686        .take_while(|c| c.is_whitespace())
25687        .count();
25688    let comment_candidate = snapshot
25689        .chars_for_range(range.clone())
25690        .skip(num_of_whitespaces)
25691        .take(max_len_of_delimiter + 2)
25692        .collect::<String>();
25693    let (delimiter, trimmed_len, is_repl) = delimiters
25694        .iter()
25695        .filter_map(|delimiter| {
25696            let prefix = delimiter.trim_end();
25697            if comment_candidate.starts_with(prefix) {
25698                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25699                {
25700                    stripped_comment.starts_with(" %%")
25701                } else {
25702                    false
25703                };
25704                Some((delimiter, prefix.len(), is_repl))
25705            } else {
25706                None
25707            }
25708        })
25709        .max_by_key(|(_, len, _)| *len)?;
25710
25711    if let Some(BlockCommentConfig {
25712        start: block_start, ..
25713    }) = language.block_comment()
25714    {
25715        let block_start_trimmed = block_start.trim_end();
25716        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25717            let line_content = snapshot
25718                .chars_for_range(range.clone())
25719                .skip(num_of_whitespaces)
25720                .take(block_start_trimmed.len())
25721                .collect::<String>();
25722
25723            if line_content.starts_with(block_start_trimmed) {
25724                return None;
25725            }
25726        }
25727    }
25728
25729    let cursor_is_placed_after_comment_marker =
25730        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25731    if cursor_is_placed_after_comment_marker {
25732        if !is_repl {
25733            return Some(delimiter.clone());
25734        }
25735
25736        let line_content_after_cursor: String = snapshot
25737            .chars_for_range(range)
25738            .skip(start_point.column as usize)
25739            .collect();
25740
25741        if line_content_after_cursor.trim().is_empty() {
25742            return None;
25743        } else {
25744            return Some(delimiter.clone());
25745        }
25746    } else {
25747        None
25748    }
25749}
25750
25751fn documentation_delimiter_for_newline(
25752    start_point: &Point,
25753    buffer: &MultiBufferSnapshot,
25754    language: &LanguageScope,
25755    newline_config: &mut NewlineConfig,
25756) -> Option<Arc<str>> {
25757    let BlockCommentConfig {
25758        start: start_tag,
25759        end: end_tag,
25760        prefix: delimiter,
25761        tab_size: len,
25762    } = language.documentation_comment()?;
25763    let is_within_block_comment = buffer
25764        .language_scope_at(*start_point)
25765        .is_some_and(|scope| scope.override_name() == Some("comment"));
25766    if !is_within_block_comment {
25767        return None;
25768    }
25769
25770    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25771
25772    let num_of_whitespaces = snapshot
25773        .chars_for_range(range.clone())
25774        .take_while(|c| c.is_whitespace())
25775        .count();
25776
25777    // 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.
25778    let column = start_point.column;
25779    let cursor_is_after_start_tag = {
25780        let start_tag_len = start_tag.len();
25781        let start_tag_line = snapshot
25782            .chars_for_range(range.clone())
25783            .skip(num_of_whitespaces)
25784            .take(start_tag_len)
25785            .collect::<String>();
25786        if start_tag_line.starts_with(start_tag.as_ref()) {
25787            num_of_whitespaces + start_tag_len <= column as usize
25788        } else {
25789            false
25790        }
25791    };
25792
25793    let cursor_is_after_delimiter = {
25794        let delimiter_trim = delimiter.trim_end();
25795        let delimiter_line = snapshot
25796            .chars_for_range(range.clone())
25797            .skip(num_of_whitespaces)
25798            .take(delimiter_trim.len())
25799            .collect::<String>();
25800        if delimiter_line.starts_with(delimiter_trim) {
25801            num_of_whitespaces + delimiter_trim.len() <= column as usize
25802        } else {
25803            false
25804        }
25805    };
25806
25807    let mut needs_extra_line = false;
25808    let mut extra_line_additional_indent = IndentSize::spaces(0);
25809
25810    let cursor_is_before_end_tag_if_exists = {
25811        let mut char_position = 0u32;
25812        let mut end_tag_offset = None;
25813
25814        'outer: for chunk in snapshot.text_for_range(range) {
25815            if let Some(byte_pos) = chunk.find(&**end_tag) {
25816                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25817                end_tag_offset = Some(char_position + chars_before_match);
25818                break 'outer;
25819            }
25820            char_position += chunk.chars().count() as u32;
25821        }
25822
25823        if let Some(end_tag_offset) = end_tag_offset {
25824            let cursor_is_before_end_tag = column <= end_tag_offset;
25825            if cursor_is_after_start_tag {
25826                if cursor_is_before_end_tag {
25827                    needs_extra_line = true;
25828                }
25829                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25830                if cursor_is_at_start_of_end_tag {
25831                    extra_line_additional_indent.len = *len;
25832                }
25833            }
25834            cursor_is_before_end_tag
25835        } else {
25836            true
25837        }
25838    };
25839
25840    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25841        && cursor_is_before_end_tag_if_exists
25842    {
25843        let additional_indent = if cursor_is_after_start_tag {
25844            IndentSize::spaces(*len)
25845        } else {
25846            IndentSize::spaces(0)
25847        };
25848
25849        *newline_config = NewlineConfig::Newline {
25850            additional_indent,
25851            extra_line_additional_indent: if needs_extra_line {
25852                Some(extra_line_additional_indent)
25853            } else {
25854                None
25855            },
25856            prevent_auto_indent: true,
25857        };
25858        Some(delimiter.clone())
25859    } else {
25860        None
25861    }
25862}
25863
25864const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25865
25866fn list_delimiter_for_newline(
25867    start_point: &Point,
25868    buffer: &MultiBufferSnapshot,
25869    language: &LanguageScope,
25870    newline_config: &mut NewlineConfig,
25871) -> Option<Arc<str>> {
25872    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25873
25874    let num_of_whitespaces = snapshot
25875        .chars_for_range(range.clone())
25876        .take_while(|c| c.is_whitespace())
25877        .count();
25878
25879    let task_list_entries: Vec<_> = language
25880        .task_list()
25881        .into_iter()
25882        .flat_map(|config| {
25883            config
25884                .prefixes
25885                .iter()
25886                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25887        })
25888        .collect();
25889    let unordered_list_entries: Vec<_> = language
25890        .unordered_list()
25891        .iter()
25892        .map(|marker| (marker.as_ref(), marker.as_ref()))
25893        .collect();
25894
25895    let all_entries: Vec<_> = task_list_entries
25896        .into_iter()
25897        .chain(unordered_list_entries)
25898        .collect();
25899
25900    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25901        let candidate: String = snapshot
25902            .chars_for_range(range.clone())
25903            .skip(num_of_whitespaces)
25904            .take(max_prefix_len)
25905            .collect();
25906
25907        if let Some((prefix, continuation)) = all_entries
25908            .iter()
25909            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25910            .max_by_key(|(prefix, _)| prefix.len())
25911        {
25912            let end_of_prefix = num_of_whitespaces + prefix.len();
25913            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25914            let has_content_after_marker = snapshot
25915                .chars_for_range(range)
25916                .skip(end_of_prefix)
25917                .any(|c| !c.is_whitespace());
25918
25919            if has_content_after_marker && cursor_is_after_prefix {
25920                return Some((*continuation).into());
25921            }
25922
25923            if start_point.column as usize == end_of_prefix {
25924                if num_of_whitespaces == 0 {
25925                    *newline_config = NewlineConfig::ClearCurrentLine;
25926                } else {
25927                    *newline_config = NewlineConfig::UnindentCurrentLine {
25928                        continuation: (*continuation).into(),
25929                    };
25930                }
25931            }
25932
25933            return None;
25934        }
25935    }
25936
25937    let candidate: String = snapshot
25938        .chars_for_range(range.clone())
25939        .skip(num_of_whitespaces)
25940        .take(ORDERED_LIST_MAX_MARKER_LEN)
25941        .collect();
25942
25943    for ordered_config in language.ordered_list() {
25944        let regex = match Regex::new(&ordered_config.pattern) {
25945            Ok(r) => r,
25946            Err(_) => continue,
25947        };
25948
25949        if let Some(captures) = regex.captures(&candidate) {
25950            let full_match = captures.get(0)?;
25951            let marker_len = full_match.len();
25952            let end_of_prefix = num_of_whitespaces + marker_len;
25953            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25954
25955            let has_content_after_marker = snapshot
25956                .chars_for_range(range)
25957                .skip(end_of_prefix)
25958                .any(|c| !c.is_whitespace());
25959
25960            if has_content_after_marker && cursor_is_after_prefix {
25961                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25962                let continuation = ordered_config
25963                    .format
25964                    .replace("{1}", &(number + 1).to_string());
25965                return Some(continuation.into());
25966            }
25967
25968            if start_point.column as usize == end_of_prefix {
25969                let continuation = ordered_config.format.replace("{1}", "1");
25970                if num_of_whitespaces == 0 {
25971                    *newline_config = NewlineConfig::ClearCurrentLine;
25972                } else {
25973                    *newline_config = NewlineConfig::UnindentCurrentLine {
25974                        continuation: continuation.into(),
25975                    };
25976                }
25977            }
25978
25979            return None;
25980        }
25981    }
25982
25983    None
25984}
25985
25986fn is_list_prefix_row(
25987    row: MultiBufferRow,
25988    buffer: &MultiBufferSnapshot,
25989    language: &LanguageScope,
25990) -> bool {
25991    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25992        return false;
25993    };
25994
25995    let num_of_whitespaces = snapshot
25996        .chars_for_range(range.clone())
25997        .take_while(|c| c.is_whitespace())
25998        .count();
25999
26000    let task_list_prefixes: Vec<_> = language
26001        .task_list()
26002        .into_iter()
26003        .flat_map(|config| {
26004            config
26005                .prefixes
26006                .iter()
26007                .map(|p| p.as_ref())
26008                .collect::<Vec<_>>()
26009        })
26010        .collect();
26011    let unordered_list_markers: Vec<_> = language
26012        .unordered_list()
26013        .iter()
26014        .map(|marker| marker.as_ref())
26015        .collect();
26016    let all_prefixes: Vec<_> = task_list_prefixes
26017        .into_iter()
26018        .chain(unordered_list_markers)
26019        .collect();
26020    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26021        let candidate: String = snapshot
26022            .chars_for_range(range.clone())
26023            .skip(num_of_whitespaces)
26024            .take(max_prefix_len)
26025            .collect();
26026        if all_prefixes
26027            .iter()
26028            .any(|prefix| candidate.starts_with(*prefix))
26029        {
26030            return true;
26031        }
26032    }
26033
26034    let ordered_list_candidate: String = snapshot
26035        .chars_for_range(range)
26036        .skip(num_of_whitespaces)
26037        .take(ORDERED_LIST_MAX_MARKER_LEN)
26038        .collect();
26039    for ordered_config in language.ordered_list() {
26040        let regex = match Regex::new(&ordered_config.pattern) {
26041            Ok(r) => r,
26042            Err(_) => continue,
26043        };
26044        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26045            return captures.get(0).is_some();
26046        }
26047    }
26048
26049    false
26050}
26051
26052#[derive(Debug)]
26053enum NewlineConfig {
26054    /// Insert newline with optional additional indent and optional extra blank line
26055    Newline {
26056        additional_indent: IndentSize,
26057        extra_line_additional_indent: Option<IndentSize>,
26058        prevent_auto_indent: bool,
26059    },
26060    /// Clear the current line
26061    ClearCurrentLine,
26062    /// Unindent the current line and add continuation
26063    UnindentCurrentLine { continuation: Arc<str> },
26064}
26065
26066impl NewlineConfig {
26067    fn has_extra_line(&self) -> bool {
26068        matches!(
26069            self,
26070            Self::Newline {
26071                extra_line_additional_indent: Some(_),
26072                ..
26073            }
26074        )
26075    }
26076
26077    fn insert_extra_newline_brackets(
26078        buffer: &MultiBufferSnapshot,
26079        range: Range<MultiBufferOffset>,
26080        language: &language::LanguageScope,
26081    ) -> bool {
26082        let leading_whitespace_len = buffer
26083            .reversed_chars_at(range.start)
26084            .take_while(|c| c.is_whitespace() && *c != '\n')
26085            .map(|c| c.len_utf8())
26086            .sum::<usize>();
26087        let trailing_whitespace_len = buffer
26088            .chars_at(range.end)
26089            .take_while(|c| c.is_whitespace() && *c != '\n')
26090            .map(|c| c.len_utf8())
26091            .sum::<usize>();
26092        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26093
26094        language.brackets().any(|(pair, enabled)| {
26095            let pair_start = pair.start.trim_end();
26096            let pair_end = pair.end.trim_start();
26097
26098            enabled
26099                && pair.newline
26100                && buffer.contains_str_at(range.end, pair_end)
26101                && buffer.contains_str_at(
26102                    range.start.saturating_sub_usize(pair_start.len()),
26103                    pair_start,
26104                )
26105        })
26106    }
26107
26108    fn insert_extra_newline_tree_sitter(
26109        buffer: &MultiBufferSnapshot,
26110        range: Range<MultiBufferOffset>,
26111    ) -> bool {
26112        let (buffer, range) = match buffer
26113            .range_to_buffer_ranges(range.start..=range.end)
26114            .as_slice()
26115        {
26116            [(buffer, range, _)] => (*buffer, range.clone()),
26117            _ => return false,
26118        };
26119        let pair = {
26120            let mut result: Option<BracketMatch<usize>> = None;
26121
26122            for pair in buffer
26123                .all_bracket_ranges(range.start.0..range.end.0)
26124                .filter(move |pair| {
26125                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26126                })
26127            {
26128                let len = pair.close_range.end - pair.open_range.start;
26129
26130                if let Some(existing) = &result {
26131                    let existing_len = existing.close_range.end - existing.open_range.start;
26132                    if len > existing_len {
26133                        continue;
26134                    }
26135                }
26136
26137                result = Some(pair);
26138            }
26139
26140            result
26141        };
26142        let Some(pair) = pair else {
26143            return false;
26144        };
26145        pair.newline_only
26146            && buffer
26147                .chars_for_range(pair.open_range.end..range.start.0)
26148                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26149                .all(|c| c.is_whitespace() && c != '\n')
26150    }
26151}
26152
26153fn update_uncommitted_diff_for_buffer(
26154    editor: Entity<Editor>,
26155    project: &Entity<Project>,
26156    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26157    buffer: Entity<MultiBuffer>,
26158    cx: &mut App,
26159) -> Task<()> {
26160    let mut tasks = Vec::new();
26161    project.update(cx, |project, cx| {
26162        for buffer in buffers {
26163            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26164                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26165            }
26166        }
26167    });
26168    cx.spawn(async move |cx| {
26169        let diffs = future::join_all(tasks).await;
26170        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26171            return;
26172        }
26173
26174        buffer.update(cx, |buffer, cx| {
26175            for diff in diffs.into_iter().flatten() {
26176                buffer.add_diff(diff, cx);
26177            }
26178        });
26179    })
26180}
26181
26182fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26183    let tab_size = tab_size.get() as usize;
26184    let mut width = offset;
26185
26186    for ch in text.chars() {
26187        width += if ch == '\t' {
26188            tab_size - (width % tab_size)
26189        } else {
26190            1
26191        };
26192    }
26193
26194    width - offset
26195}
26196
26197#[cfg(test)]
26198mod tests {
26199    use super::*;
26200
26201    #[test]
26202    fn test_string_size_with_expanded_tabs() {
26203        let nz = |val| NonZeroU32::new(val).unwrap();
26204        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26205        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26206        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26207        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26208        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26209        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26210        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26211        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26212    }
26213}
26214
26215/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26216struct WordBreakingTokenizer<'a> {
26217    input: &'a str,
26218}
26219
26220impl<'a> WordBreakingTokenizer<'a> {
26221    fn new(input: &'a str) -> Self {
26222        Self { input }
26223    }
26224}
26225
26226fn is_char_ideographic(ch: char) -> bool {
26227    use unicode_script::Script::*;
26228    use unicode_script::UnicodeScript;
26229    matches!(ch.script(), Han | Tangut | Yi)
26230}
26231
26232fn is_grapheme_ideographic(text: &str) -> bool {
26233    text.chars().any(is_char_ideographic)
26234}
26235
26236fn is_grapheme_whitespace(text: &str) -> bool {
26237    text.chars().any(|x| x.is_whitespace())
26238}
26239
26240fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26241    text.chars()
26242        .next()
26243        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26244}
26245
26246#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26247enum WordBreakToken<'a> {
26248    Word { token: &'a str, grapheme_len: usize },
26249    InlineWhitespace { token: &'a str, grapheme_len: usize },
26250    Newline,
26251}
26252
26253impl<'a> Iterator for WordBreakingTokenizer<'a> {
26254    /// Yields a span, the count of graphemes in the token, and whether it was
26255    /// whitespace. Note that it also breaks at word boundaries.
26256    type Item = WordBreakToken<'a>;
26257
26258    fn next(&mut self) -> Option<Self::Item> {
26259        use unicode_segmentation::UnicodeSegmentation;
26260        if self.input.is_empty() {
26261            return None;
26262        }
26263
26264        let mut iter = self.input.graphemes(true).peekable();
26265        let mut offset = 0;
26266        let mut grapheme_len = 0;
26267        if let Some(first_grapheme) = iter.next() {
26268            let is_newline = first_grapheme == "\n";
26269            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26270            offset += first_grapheme.len();
26271            grapheme_len += 1;
26272            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26273                if let Some(grapheme) = iter.peek().copied()
26274                    && should_stay_with_preceding_ideograph(grapheme)
26275                {
26276                    offset += grapheme.len();
26277                    grapheme_len += 1;
26278                }
26279            } else {
26280                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26281                let mut next_word_bound = words.peek().copied();
26282                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26283                    next_word_bound = words.next();
26284                }
26285                while let Some(grapheme) = iter.peek().copied() {
26286                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26287                        break;
26288                    };
26289                    if is_grapheme_whitespace(grapheme) != is_whitespace
26290                        || (grapheme == "\n") != is_newline
26291                    {
26292                        break;
26293                    };
26294                    offset += grapheme.len();
26295                    grapheme_len += 1;
26296                    iter.next();
26297                }
26298            }
26299            let token = &self.input[..offset];
26300            self.input = &self.input[offset..];
26301            if token == "\n" {
26302                Some(WordBreakToken::Newline)
26303            } else if is_whitespace {
26304                Some(WordBreakToken::InlineWhitespace {
26305                    token,
26306                    grapheme_len,
26307                })
26308            } else {
26309                Some(WordBreakToken::Word {
26310                    token,
26311                    grapheme_len,
26312                })
26313            }
26314        } else {
26315            None
26316        }
26317    }
26318}
26319
26320#[test]
26321fn test_word_breaking_tokenizer() {
26322    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26323        ("", &[]),
26324        ("  ", &[whitespace("  ", 2)]),
26325        ("Ʒ", &[word("Ʒ", 1)]),
26326        ("Ǽ", &[word("Ǽ", 1)]),
26327        ("", &[word("", 1)]),
26328        ("⋑⋑", &[word("⋑⋑", 2)]),
26329        (
26330            "原理,进而",
26331            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26332        ),
26333        (
26334            "hello world",
26335            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26336        ),
26337        (
26338            "hello, world",
26339            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26340        ),
26341        (
26342            "  hello world",
26343            &[
26344                whitespace("  ", 2),
26345                word("hello", 5),
26346                whitespace(" ", 1),
26347                word("world", 5),
26348            ],
26349        ),
26350        (
26351            "这是什么 \n 钢笔",
26352            &[
26353                word("", 1),
26354                word("", 1),
26355                word("", 1),
26356                word("", 1),
26357                whitespace(" ", 1),
26358                newline(),
26359                whitespace(" ", 1),
26360                word("", 1),
26361                word("", 1),
26362            ],
26363        ),
26364        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26365    ];
26366
26367    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26368        WordBreakToken::Word {
26369            token,
26370            grapheme_len,
26371        }
26372    }
26373
26374    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26375        WordBreakToken::InlineWhitespace {
26376            token,
26377            grapheme_len,
26378        }
26379    }
26380
26381    fn newline() -> WordBreakToken<'static> {
26382        WordBreakToken::Newline
26383    }
26384
26385    for (input, result) in tests {
26386        assert_eq!(
26387            WordBreakingTokenizer::new(input)
26388                .collect::<Vec<_>>()
26389                .as_slice(),
26390            *result,
26391        );
26392    }
26393}
26394
26395fn wrap_with_prefix(
26396    first_line_prefix: String,
26397    subsequent_lines_prefix: String,
26398    unwrapped_text: String,
26399    wrap_column: usize,
26400    tab_size: NonZeroU32,
26401    preserve_existing_whitespace: bool,
26402) -> String {
26403    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26404    let subsequent_lines_prefix_len =
26405        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26406    let mut wrapped_text = String::new();
26407    let mut current_line = first_line_prefix;
26408    let mut is_first_line = true;
26409
26410    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26411    let mut current_line_len = first_line_prefix_len;
26412    let mut in_whitespace = false;
26413    for token in tokenizer {
26414        let have_preceding_whitespace = in_whitespace;
26415        match token {
26416            WordBreakToken::Word {
26417                token,
26418                grapheme_len,
26419            } => {
26420                in_whitespace = false;
26421                let current_prefix_len = if is_first_line {
26422                    first_line_prefix_len
26423                } else {
26424                    subsequent_lines_prefix_len
26425                };
26426                if current_line_len + grapheme_len > wrap_column
26427                    && current_line_len != current_prefix_len
26428                {
26429                    wrapped_text.push_str(current_line.trim_end());
26430                    wrapped_text.push('\n');
26431                    is_first_line = false;
26432                    current_line = subsequent_lines_prefix.clone();
26433                    current_line_len = subsequent_lines_prefix_len;
26434                }
26435                current_line.push_str(token);
26436                current_line_len += grapheme_len;
26437            }
26438            WordBreakToken::InlineWhitespace {
26439                mut token,
26440                mut grapheme_len,
26441            } => {
26442                in_whitespace = true;
26443                if have_preceding_whitespace && !preserve_existing_whitespace {
26444                    continue;
26445                }
26446                if !preserve_existing_whitespace {
26447                    // Keep a single whitespace grapheme as-is
26448                    if let Some(first) =
26449                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26450                    {
26451                        token = first;
26452                    } else {
26453                        token = " ";
26454                    }
26455                    grapheme_len = 1;
26456                }
26457                let current_prefix_len = if is_first_line {
26458                    first_line_prefix_len
26459                } else {
26460                    subsequent_lines_prefix_len
26461                };
26462                if current_line_len + grapheme_len > wrap_column {
26463                    wrapped_text.push_str(current_line.trim_end());
26464                    wrapped_text.push('\n');
26465                    is_first_line = false;
26466                    current_line = subsequent_lines_prefix.clone();
26467                    current_line_len = subsequent_lines_prefix_len;
26468                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26469                    current_line.push_str(token);
26470                    current_line_len += grapheme_len;
26471                }
26472            }
26473            WordBreakToken::Newline => {
26474                in_whitespace = true;
26475                let current_prefix_len = if is_first_line {
26476                    first_line_prefix_len
26477                } else {
26478                    subsequent_lines_prefix_len
26479                };
26480                if preserve_existing_whitespace {
26481                    wrapped_text.push_str(current_line.trim_end());
26482                    wrapped_text.push('\n');
26483                    is_first_line = false;
26484                    current_line = subsequent_lines_prefix.clone();
26485                    current_line_len = subsequent_lines_prefix_len;
26486                } else if have_preceding_whitespace {
26487                    continue;
26488                } else if current_line_len + 1 > wrap_column
26489                    && current_line_len != current_prefix_len
26490                {
26491                    wrapped_text.push_str(current_line.trim_end());
26492                    wrapped_text.push('\n');
26493                    is_first_line = false;
26494                    current_line = subsequent_lines_prefix.clone();
26495                    current_line_len = subsequent_lines_prefix_len;
26496                } else if current_line_len != current_prefix_len {
26497                    current_line.push(' ');
26498                    current_line_len += 1;
26499                }
26500            }
26501        }
26502    }
26503
26504    if !current_line.is_empty() {
26505        wrapped_text.push_str(&current_line);
26506    }
26507    wrapped_text
26508}
26509
26510#[test]
26511fn test_wrap_with_prefix() {
26512    assert_eq!(
26513        wrap_with_prefix(
26514            "# ".to_string(),
26515            "# ".to_string(),
26516            "abcdefg".to_string(),
26517            4,
26518            NonZeroU32::new(4).unwrap(),
26519            false,
26520        ),
26521        "# abcdefg"
26522    );
26523    assert_eq!(
26524        wrap_with_prefix(
26525            "".to_string(),
26526            "".to_string(),
26527            "\thello world".to_string(),
26528            8,
26529            NonZeroU32::new(4).unwrap(),
26530            false,
26531        ),
26532        "hello\nworld"
26533    );
26534    assert_eq!(
26535        wrap_with_prefix(
26536            "// ".to_string(),
26537            "// ".to_string(),
26538            "xx \nyy zz aa bb cc".to_string(),
26539            12,
26540            NonZeroU32::new(4).unwrap(),
26541            false,
26542        ),
26543        "// xx yy zz\n// aa bb cc"
26544    );
26545    assert_eq!(
26546        wrap_with_prefix(
26547            String::new(),
26548            String::new(),
26549            "这是什么 \n 钢笔".to_string(),
26550            3,
26551            NonZeroU32::new(4).unwrap(),
26552            false,
26553        ),
26554        "这是什\n么 钢\n"
26555    );
26556    assert_eq!(
26557        wrap_with_prefix(
26558            String::new(),
26559            String::new(),
26560            format!("foo{}bar", '\u{2009}'), // thin space
26561            80,
26562            NonZeroU32::new(4).unwrap(),
26563            false,
26564        ),
26565        format!("foo{}bar", '\u{2009}')
26566    );
26567}
26568
26569pub trait CollaborationHub {
26570    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26571    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26572    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26573}
26574
26575impl CollaborationHub for Entity<Project> {
26576    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26577        self.read(cx).collaborators()
26578    }
26579
26580    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26581        self.read(cx).user_store().read(cx).participant_indices()
26582    }
26583
26584    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26585        let this = self.read(cx);
26586        let user_ids = this.collaborators().values().map(|c| c.user_id);
26587        this.user_store().read(cx).participant_names(user_ids, cx)
26588    }
26589}
26590
26591pub trait SemanticsProvider {
26592    fn hover(
26593        &self,
26594        buffer: &Entity<Buffer>,
26595        position: text::Anchor,
26596        cx: &mut App,
26597    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26598
26599    fn inline_values(
26600        &self,
26601        buffer_handle: Entity<Buffer>,
26602        range: Range<text::Anchor>,
26603        cx: &mut App,
26604    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26605
26606    fn applicable_inlay_chunks(
26607        &self,
26608        buffer: &Entity<Buffer>,
26609        ranges: &[Range<text::Anchor>],
26610        cx: &mut App,
26611    ) -> Vec<Range<BufferRow>>;
26612
26613    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26614
26615    fn inlay_hints(
26616        &self,
26617        invalidate: InvalidationStrategy,
26618        buffer: Entity<Buffer>,
26619        ranges: Vec<Range<text::Anchor>>,
26620        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26621        cx: &mut App,
26622    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26623
26624    fn semantic_tokens(
26625        &self,
26626        buffer: Entity<Buffer>,
26627        refresh: Option<RefreshForServer>,
26628        cx: &mut App,
26629    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
26630
26631    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26632
26633    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26634
26635    fn document_highlights(
26636        &self,
26637        buffer: &Entity<Buffer>,
26638        position: text::Anchor,
26639        cx: &mut App,
26640    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26641
26642    fn definitions(
26643        &self,
26644        buffer: &Entity<Buffer>,
26645        position: text::Anchor,
26646        kind: GotoDefinitionKind,
26647        cx: &mut App,
26648    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26649
26650    fn range_for_rename(
26651        &self,
26652        buffer: &Entity<Buffer>,
26653        position: text::Anchor,
26654        cx: &mut App,
26655    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26656
26657    fn perform_rename(
26658        &self,
26659        buffer: &Entity<Buffer>,
26660        position: text::Anchor,
26661        new_name: String,
26662        cx: &mut App,
26663    ) -> Option<Task<Result<ProjectTransaction>>>;
26664}
26665
26666pub trait CompletionProvider {
26667    fn completions(
26668        &self,
26669        excerpt_id: ExcerptId,
26670        buffer: &Entity<Buffer>,
26671        buffer_position: text::Anchor,
26672        trigger: CompletionContext,
26673        window: &mut Window,
26674        cx: &mut Context<Editor>,
26675    ) -> Task<Result<Vec<CompletionResponse>>>;
26676
26677    fn resolve_completions(
26678        &self,
26679        _buffer: Entity<Buffer>,
26680        _completion_indices: Vec<usize>,
26681        _completions: Rc<RefCell<Box<[Completion]>>>,
26682        _cx: &mut Context<Editor>,
26683    ) -> Task<Result<bool>> {
26684        Task::ready(Ok(false))
26685    }
26686
26687    fn apply_additional_edits_for_completion(
26688        &self,
26689        _buffer: Entity<Buffer>,
26690        _completions: Rc<RefCell<Box<[Completion]>>>,
26691        _completion_index: usize,
26692        _push_to_history: bool,
26693        _all_commit_ranges: Vec<Range<language::Anchor>>,
26694        _cx: &mut Context<Editor>,
26695    ) -> Task<Result<Option<language::Transaction>>> {
26696        Task::ready(Ok(None))
26697    }
26698
26699    fn is_completion_trigger(
26700        &self,
26701        buffer: &Entity<Buffer>,
26702        position: language::Anchor,
26703        text: &str,
26704        trigger_in_words: bool,
26705        cx: &mut Context<Editor>,
26706    ) -> bool;
26707
26708    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26709
26710    fn sort_completions(&self) -> bool {
26711        true
26712    }
26713
26714    fn filter_completions(&self) -> bool {
26715        true
26716    }
26717
26718    fn show_snippets(&self) -> bool {
26719        false
26720    }
26721}
26722
26723pub trait CodeActionProvider {
26724    fn id(&self) -> Arc<str>;
26725
26726    fn code_actions(
26727        &self,
26728        buffer: &Entity<Buffer>,
26729        range: Range<text::Anchor>,
26730        window: &mut Window,
26731        cx: &mut App,
26732    ) -> Task<Result<Vec<CodeAction>>>;
26733
26734    fn apply_code_action(
26735        &self,
26736        buffer_handle: Entity<Buffer>,
26737        action: CodeAction,
26738        excerpt_id: ExcerptId,
26739        push_to_history: bool,
26740        window: &mut Window,
26741        cx: &mut App,
26742    ) -> Task<Result<ProjectTransaction>>;
26743}
26744
26745impl CodeActionProvider for Entity<Project> {
26746    fn id(&self) -> Arc<str> {
26747        "project".into()
26748    }
26749
26750    fn code_actions(
26751        &self,
26752        buffer: &Entity<Buffer>,
26753        range: Range<text::Anchor>,
26754        _window: &mut Window,
26755        cx: &mut App,
26756    ) -> Task<Result<Vec<CodeAction>>> {
26757        self.update(cx, |project, cx| {
26758            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26759            let code_actions = project.code_actions(buffer, range, None, cx);
26760            cx.background_spawn(async move {
26761                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26762                Ok(code_lens_actions
26763                    .context("code lens fetch")?
26764                    .into_iter()
26765                    .flatten()
26766                    .chain(
26767                        code_actions
26768                            .context("code action fetch")?
26769                            .into_iter()
26770                            .flatten(),
26771                    )
26772                    .collect())
26773            })
26774        })
26775    }
26776
26777    fn apply_code_action(
26778        &self,
26779        buffer_handle: Entity<Buffer>,
26780        action: CodeAction,
26781        _excerpt_id: ExcerptId,
26782        push_to_history: bool,
26783        _window: &mut Window,
26784        cx: &mut App,
26785    ) -> Task<Result<ProjectTransaction>> {
26786        self.update(cx, |project, cx| {
26787            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26788        })
26789    }
26790}
26791
26792fn snippet_completions(
26793    project: &Project,
26794    buffer: &Entity<Buffer>,
26795    buffer_anchor: text::Anchor,
26796    classifier: CharClassifier,
26797    cx: &mut App,
26798) -> Task<Result<CompletionResponse>> {
26799    let languages = buffer.read(cx).languages_at(buffer_anchor);
26800    let snippet_store = project.snippets().read(cx);
26801
26802    let scopes: Vec<_> = languages
26803        .iter()
26804        .filter_map(|language| {
26805            let language_name = language.lsp_id();
26806            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26807
26808            if snippets.is_empty() {
26809                None
26810            } else {
26811                Some((language.default_scope(), snippets))
26812            }
26813        })
26814        .collect();
26815
26816    if scopes.is_empty() {
26817        return Task::ready(Ok(CompletionResponse {
26818            completions: vec![],
26819            display_options: CompletionDisplayOptions::default(),
26820            is_incomplete: false,
26821        }));
26822    }
26823
26824    let snapshot = buffer.read(cx).text_snapshot();
26825    let executor = cx.background_executor().clone();
26826
26827    cx.background_spawn(async move {
26828        let is_word_char = |c| classifier.is_word(c);
26829
26830        let mut is_incomplete = false;
26831        let mut completions: Vec<Completion> = Vec::new();
26832
26833        const MAX_PREFIX_LEN: usize = 128;
26834        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26835        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26836        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26837
26838        let max_buffer_window: String = snapshot
26839            .text_for_range(window_start..buffer_offset)
26840            .collect();
26841
26842        if max_buffer_window.is_empty() {
26843            return Ok(CompletionResponse {
26844                completions: vec![],
26845                display_options: CompletionDisplayOptions::default(),
26846                is_incomplete: true,
26847            });
26848        }
26849
26850        for (_scope, snippets) in scopes.into_iter() {
26851            // Sort snippets by word count to match longer snippet prefixes first.
26852            let mut sorted_snippet_candidates = snippets
26853                .iter()
26854                .enumerate()
26855                .flat_map(|(snippet_ix, snippet)| {
26856                    snippet
26857                        .prefix
26858                        .iter()
26859                        .enumerate()
26860                        .map(move |(prefix_ix, prefix)| {
26861                            let word_count =
26862                                snippet_candidate_suffixes(prefix, &is_word_char).count();
26863                            ((snippet_ix, prefix_ix), prefix, word_count)
26864                        })
26865                })
26866                .collect_vec();
26867            sorted_snippet_candidates
26868                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26869
26870            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26871
26872            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
26873                .take(
26874                    sorted_snippet_candidates
26875                        .first()
26876                        .map(|(_, _, word_count)| *word_count)
26877                        .unwrap_or_default(),
26878                )
26879                .collect_vec();
26880
26881            const MAX_RESULTS: usize = 100;
26882            // Each match also remembers how many characters from the buffer it consumed
26883            let mut matches: Vec<(StringMatch, usize)> = vec![];
26884
26885            let mut snippet_list_cutoff_index = 0;
26886            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26887                let word_count = buffer_index + 1;
26888                // Increase `snippet_list_cutoff_index` until we have all of the
26889                // snippets with sufficiently many words.
26890                while sorted_snippet_candidates
26891                    .get(snippet_list_cutoff_index)
26892                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26893                        *snippet_word_count >= word_count
26894                    })
26895                {
26896                    snippet_list_cutoff_index += 1;
26897                }
26898
26899                // Take only the candidates with at least `word_count` many words
26900                let snippet_candidates_at_word_len =
26901                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26902
26903                let candidates = snippet_candidates_at_word_len
26904                    .iter()
26905                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26906                    .enumerate() // index in `sorted_snippet_candidates`
26907                    // First char must match
26908                    .filter(|(_ix, prefix)| {
26909                        itertools::equal(
26910                            prefix
26911                                .chars()
26912                                .next()
26913                                .into_iter()
26914                                .flat_map(|c| c.to_lowercase()),
26915                            buffer_window
26916                                .chars()
26917                                .next()
26918                                .into_iter()
26919                                .flat_map(|c| c.to_lowercase()),
26920                        )
26921                    })
26922                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26923                    .collect::<Vec<StringMatchCandidate>>();
26924
26925                matches.extend(
26926                    fuzzy::match_strings(
26927                        &candidates,
26928                        &buffer_window,
26929                        buffer_window.chars().any(|c| c.is_uppercase()),
26930                        true,
26931                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26932                        &Default::default(),
26933                        executor.clone(),
26934                    )
26935                    .await
26936                    .into_iter()
26937                    .map(|string_match| (string_match, buffer_window.len())),
26938                );
26939
26940                if matches.len() >= MAX_RESULTS {
26941                    break;
26942                }
26943            }
26944
26945            let to_lsp = |point: &text::Anchor| {
26946                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26947                point_to_lsp(end)
26948            };
26949            let lsp_end = to_lsp(&buffer_anchor);
26950
26951            if matches.len() >= MAX_RESULTS {
26952                is_incomplete = true;
26953            }
26954
26955            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26956                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26957                    sorted_snippet_candidates[string_match.candidate_id];
26958                let snippet = &snippets[snippet_index];
26959                let start = buffer_offset - buffer_window_len;
26960                let start = snapshot.anchor_before(start);
26961                let range = start..buffer_anchor;
26962                let lsp_start = to_lsp(&start);
26963                let lsp_range = lsp::Range {
26964                    start: lsp_start,
26965                    end: lsp_end,
26966                };
26967                Completion {
26968                    replace_range: range,
26969                    new_text: snippet.body.clone(),
26970                    source: CompletionSource::Lsp {
26971                        insert_range: None,
26972                        server_id: LanguageServerId(usize::MAX),
26973                        resolved: true,
26974                        lsp_completion: Box::new(lsp::CompletionItem {
26975                            label: snippet.prefix.first().unwrap().clone(),
26976                            kind: Some(CompletionItemKind::SNIPPET),
26977                            label_details: snippet.description.as_ref().map(|description| {
26978                                lsp::CompletionItemLabelDetails {
26979                                    detail: Some(description.clone()),
26980                                    description: None,
26981                                }
26982                            }),
26983                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26984                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26985                                lsp::InsertReplaceEdit {
26986                                    new_text: snippet.body.clone(),
26987                                    insert: lsp_range,
26988                                    replace: lsp_range,
26989                                },
26990                            )),
26991                            filter_text: Some(snippet.body.clone()),
26992                            sort_text: Some(char::MAX.to_string()),
26993                            ..lsp::CompletionItem::default()
26994                        }),
26995                        lsp_defaults: None,
26996                    },
26997                    label: CodeLabel {
26998                        text: matching_prefix.clone(),
26999                        runs: Vec::new(),
27000                        filter_range: 0..matching_prefix.len(),
27001                    },
27002                    icon_path: None,
27003                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27004                        single_line: snippet.name.clone().into(),
27005                        plain_text: snippet
27006                            .description
27007                            .clone()
27008                            .map(|description| description.into()),
27009                    }),
27010                    insert_text_mode: None,
27011                    confirm: None,
27012                    match_start: Some(start),
27013                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27014                }
27015            }));
27016        }
27017
27018        Ok(CompletionResponse {
27019            completions,
27020            display_options: CompletionDisplayOptions::default(),
27021            is_incomplete,
27022        })
27023    })
27024}
27025
27026impl CompletionProvider for Entity<Project> {
27027    fn completions(
27028        &self,
27029        _excerpt_id: ExcerptId,
27030        buffer: &Entity<Buffer>,
27031        buffer_position: text::Anchor,
27032        options: CompletionContext,
27033        _window: &mut Window,
27034        cx: &mut Context<Editor>,
27035    ) -> Task<Result<Vec<CompletionResponse>>> {
27036        self.update(cx, |project, cx| {
27037            let task = project.completions(buffer, buffer_position, options, cx);
27038            cx.background_spawn(task)
27039        })
27040    }
27041
27042    fn resolve_completions(
27043        &self,
27044        buffer: Entity<Buffer>,
27045        completion_indices: Vec<usize>,
27046        completions: Rc<RefCell<Box<[Completion]>>>,
27047        cx: &mut Context<Editor>,
27048    ) -> Task<Result<bool>> {
27049        self.update(cx, |project, cx| {
27050            project.lsp_store().update(cx, |lsp_store, cx| {
27051                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27052            })
27053        })
27054    }
27055
27056    fn apply_additional_edits_for_completion(
27057        &self,
27058        buffer: Entity<Buffer>,
27059        completions: Rc<RefCell<Box<[Completion]>>>,
27060        completion_index: usize,
27061        push_to_history: bool,
27062        all_commit_ranges: Vec<Range<language::Anchor>>,
27063        cx: &mut Context<Editor>,
27064    ) -> Task<Result<Option<language::Transaction>>> {
27065        self.update(cx, |project, cx| {
27066            project.lsp_store().update(cx, |lsp_store, cx| {
27067                lsp_store.apply_additional_edits_for_completion(
27068                    buffer,
27069                    completions,
27070                    completion_index,
27071                    push_to_history,
27072                    all_commit_ranges,
27073                    cx,
27074                )
27075            })
27076        })
27077    }
27078
27079    fn is_completion_trigger(
27080        &self,
27081        buffer: &Entity<Buffer>,
27082        position: language::Anchor,
27083        text: &str,
27084        trigger_in_words: bool,
27085        cx: &mut Context<Editor>,
27086    ) -> bool {
27087        let mut chars = text.chars();
27088        let char = if let Some(char) = chars.next() {
27089            char
27090        } else {
27091            return false;
27092        };
27093        if chars.next().is_some() {
27094            return false;
27095        }
27096
27097        let buffer = buffer.read(cx);
27098        let snapshot = buffer.snapshot();
27099        let classifier = snapshot
27100            .char_classifier_at(position)
27101            .scope_context(Some(CharScopeContext::Completion));
27102        if trigger_in_words && classifier.is_word(char) {
27103            return true;
27104        }
27105
27106        buffer.completion_triggers().contains(text)
27107    }
27108
27109    fn show_snippets(&self) -> bool {
27110        true
27111    }
27112}
27113
27114impl SemanticsProvider for WeakEntity<Project> {
27115    fn hover(
27116        &self,
27117        buffer: &Entity<Buffer>,
27118        position: text::Anchor,
27119        cx: &mut App,
27120    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27121        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27122            .ok()
27123    }
27124
27125    fn document_highlights(
27126        &self,
27127        buffer: &Entity<Buffer>,
27128        position: text::Anchor,
27129        cx: &mut App,
27130    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27131        self.update(cx, |project, cx| {
27132            project.document_highlights(buffer, position, cx)
27133        })
27134        .ok()
27135    }
27136
27137    fn definitions(
27138        &self,
27139        buffer: &Entity<Buffer>,
27140        position: text::Anchor,
27141        kind: GotoDefinitionKind,
27142        cx: &mut App,
27143    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27144        self.update(cx, |project, cx| match kind {
27145            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27146            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27147            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27148            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27149        })
27150        .ok()
27151    }
27152
27153    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27154        self.update(cx, |project, cx| {
27155            if project
27156                .active_debug_session(cx)
27157                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27158            {
27159                return true;
27160            }
27161
27162            buffer.update(cx, |buffer, cx| {
27163                project.any_language_server_supports_inlay_hints(buffer, cx)
27164            })
27165        })
27166        .unwrap_or(false)
27167    }
27168
27169    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27170        self.update(cx, |project, cx| {
27171            buffer.update(cx, |buffer, cx| {
27172                project.any_language_server_supports_semantic_tokens(buffer, cx)
27173            })
27174        })
27175        .unwrap_or(false)
27176    }
27177
27178    fn inline_values(
27179        &self,
27180        buffer_handle: Entity<Buffer>,
27181        range: Range<text::Anchor>,
27182        cx: &mut App,
27183    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27184        self.update(cx, |project, cx| {
27185            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27186
27187            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27188        })
27189        .ok()
27190        .flatten()
27191    }
27192
27193    fn applicable_inlay_chunks(
27194        &self,
27195        buffer: &Entity<Buffer>,
27196        ranges: &[Range<text::Anchor>],
27197        cx: &mut App,
27198    ) -> Vec<Range<BufferRow>> {
27199        self.update(cx, |project, cx| {
27200            project.lsp_store().update(cx, |lsp_store, cx| {
27201                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27202            })
27203        })
27204        .unwrap_or_default()
27205    }
27206
27207    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27208        self.update(cx, |project, cx| {
27209            project.lsp_store().update(cx, |lsp_store, _| {
27210                lsp_store.invalidate_inlay_hints(for_buffers)
27211            })
27212        })
27213        .ok();
27214    }
27215
27216    fn inlay_hints(
27217        &self,
27218        invalidate: InvalidationStrategy,
27219        buffer: Entity<Buffer>,
27220        ranges: Vec<Range<text::Anchor>>,
27221        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27222        cx: &mut App,
27223    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27224        self.update(cx, |project, cx| {
27225            project.lsp_store().update(cx, |lsp_store, cx| {
27226                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27227            })
27228        })
27229        .ok()
27230    }
27231
27232    fn semantic_tokens(
27233        &self,
27234        buffer: Entity<Buffer>,
27235        refresh: Option<RefreshForServer>,
27236        cx: &mut App,
27237    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27238        self.update(cx, |this, cx| {
27239            this.lsp_store().update(cx, |lsp_store, cx| {
27240                lsp_store.semantic_tokens(buffer, refresh, cx)
27241            })
27242        })
27243        .ok()
27244    }
27245
27246    fn range_for_rename(
27247        &self,
27248        buffer: &Entity<Buffer>,
27249        position: text::Anchor,
27250        cx: &mut App,
27251    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27252        self.update(cx, |project, cx| {
27253            let buffer = buffer.clone();
27254            let task = project.prepare_rename(buffer.clone(), position, cx);
27255            cx.spawn(async move |_, cx| {
27256                Ok(match task.await? {
27257                    PrepareRenameResponse::Success(range) => Some(range),
27258                    PrepareRenameResponse::InvalidPosition => None,
27259                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27260                        // Fallback on using TreeSitter info to determine identifier range
27261                        buffer.read_with(cx, |buffer, _| {
27262                            let snapshot = buffer.snapshot();
27263                            let (range, kind) = snapshot.surrounding_word(position, None);
27264                            if kind != Some(CharKind::Word) {
27265                                return None;
27266                            }
27267                            Some(
27268                                snapshot.anchor_before(range.start)
27269                                    ..snapshot.anchor_after(range.end),
27270                            )
27271                        })
27272                    }
27273                })
27274            })
27275        })
27276        .ok()
27277    }
27278
27279    fn perform_rename(
27280        &self,
27281        buffer: &Entity<Buffer>,
27282        position: text::Anchor,
27283        new_name: String,
27284        cx: &mut App,
27285    ) -> Option<Task<Result<ProjectTransaction>>> {
27286        self.update(cx, |project, cx| {
27287            project.perform_rename(buffer.clone(), position, new_name, cx)
27288        })
27289        .ok()
27290    }
27291}
27292
27293fn consume_contiguous_rows(
27294    contiguous_row_selections: &mut Vec<Selection<Point>>,
27295    selection: &Selection<Point>,
27296    display_map: &DisplaySnapshot,
27297    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27298) -> (MultiBufferRow, MultiBufferRow) {
27299    contiguous_row_selections.push(selection.clone());
27300    let start_row = starting_row(selection, display_map);
27301    let mut end_row = ending_row(selection, display_map);
27302
27303    while let Some(next_selection) = selections.peek() {
27304        if next_selection.start.row <= end_row.0 {
27305            end_row = ending_row(next_selection, display_map);
27306            contiguous_row_selections.push(selections.next().unwrap().clone());
27307        } else {
27308            break;
27309        }
27310    }
27311    (start_row, end_row)
27312}
27313
27314fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27315    if selection.start.column > 0 {
27316        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27317    } else {
27318        MultiBufferRow(selection.start.row)
27319    }
27320}
27321
27322fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27323    if next_selection.end.column > 0 || next_selection.is_empty() {
27324        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27325    } else {
27326        MultiBufferRow(next_selection.end.row)
27327    }
27328}
27329
27330impl EditorSnapshot {
27331    pub fn remote_selections_in_range<'a>(
27332        &'a self,
27333        range: &'a Range<Anchor>,
27334        collaboration_hub: &dyn CollaborationHub,
27335        cx: &'a App,
27336    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27337        let participant_names = collaboration_hub.user_names(cx);
27338        let participant_indices = collaboration_hub.user_participant_indices(cx);
27339        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27340        let collaborators_by_replica_id = collaborators_by_peer_id
27341            .values()
27342            .map(|collaborator| (collaborator.replica_id, collaborator))
27343            .collect::<HashMap<_, _>>();
27344        self.buffer_snapshot()
27345            .selections_in_range(range, false)
27346            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27347                if replica_id == ReplicaId::AGENT {
27348                    Some(RemoteSelection {
27349                        replica_id,
27350                        selection,
27351                        cursor_shape,
27352                        line_mode,
27353                        collaborator_id: CollaboratorId::Agent,
27354                        user_name: Some("Agent".into()),
27355                        color: cx.theme().players().agent(),
27356                    })
27357                } else {
27358                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27359                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27360                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27361                    Some(RemoteSelection {
27362                        replica_id,
27363                        selection,
27364                        cursor_shape,
27365                        line_mode,
27366                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27367                        user_name,
27368                        color: if let Some(index) = participant_index {
27369                            cx.theme().players().color_for_participant(index.0)
27370                        } else {
27371                            cx.theme().players().absent()
27372                        },
27373                    })
27374                }
27375            })
27376    }
27377
27378    pub fn hunks_for_ranges(
27379        &self,
27380        ranges: impl IntoIterator<Item = Range<Point>>,
27381    ) -> Vec<MultiBufferDiffHunk> {
27382        let mut hunks = Vec::new();
27383        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27384            HashMap::default();
27385        for query_range in ranges {
27386            let query_rows =
27387                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27388            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27389                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27390            ) {
27391                // Include deleted hunks that are adjacent to the query range, because
27392                // otherwise they would be missed.
27393                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27394                if hunk.status().is_deleted() {
27395                    intersects_range |= hunk.row_range.start == query_rows.end;
27396                    intersects_range |= hunk.row_range.end == query_rows.start;
27397                }
27398                if intersects_range {
27399                    if !processed_buffer_rows
27400                        .entry(hunk.buffer_id)
27401                        .or_default()
27402                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27403                    {
27404                        continue;
27405                    }
27406                    hunks.push(hunk);
27407                }
27408            }
27409        }
27410
27411        hunks
27412    }
27413
27414    fn display_diff_hunks_for_rows<'a>(
27415        &'a self,
27416        display_rows: Range<DisplayRow>,
27417        folded_buffers: &'a HashSet<BufferId>,
27418    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27419        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27420        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27421
27422        self.buffer_snapshot()
27423            .diff_hunks_in_range(buffer_start..buffer_end)
27424            .filter_map(|hunk| {
27425                if folded_buffers.contains(&hunk.buffer_id)
27426                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27427                {
27428                    return None;
27429                }
27430
27431                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27432                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27433                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27434                    let line_len = self.buffer_snapshot().line_len(last_row);
27435                    Point::new(last_row.0, line_len)
27436                } else {
27437                    Point::new(hunk.row_range.end.0, 0)
27438                };
27439
27440                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27441                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27442
27443                let display_hunk = if hunk_display_start.column() != 0 {
27444                    DisplayDiffHunk::Folded {
27445                        display_row: hunk_display_start.row(),
27446                    }
27447                } else {
27448                    let mut end_row = hunk_display_end.row();
27449                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27450                        end_row.0 += 1;
27451                    }
27452                    let is_created_file = hunk.is_created_file();
27453
27454                    DisplayDiffHunk::Unfolded {
27455                        status: hunk.status(),
27456                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27457                            ..hunk.diff_base_byte_range.end.0,
27458                        word_diffs: hunk.word_diffs,
27459                        display_row_range: hunk_display_start.row()..end_row,
27460                        multi_buffer_range: Anchor::range_in_buffer(
27461                            hunk.excerpt_id,
27462                            hunk.buffer_range,
27463                        ),
27464                        is_created_file,
27465                    }
27466                };
27467
27468                Some(display_hunk)
27469            })
27470    }
27471
27472    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27473        self.display_snapshot
27474            .buffer_snapshot()
27475            .language_at(position)
27476    }
27477
27478    pub fn is_focused(&self) -> bool {
27479        self.is_focused
27480    }
27481
27482    pub fn placeholder_text(&self) -> Option<String> {
27483        self.placeholder_display_snapshot
27484            .as_ref()
27485            .map(|display_map| display_map.text())
27486    }
27487
27488    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27489        self.scroll_anchor.scroll_position(&self.display_snapshot)
27490    }
27491
27492    pub fn gutter_dimensions(
27493        &self,
27494        font_id: FontId,
27495        font_size: Pixels,
27496        style: &EditorStyle,
27497        window: &mut Window,
27498        cx: &App,
27499    ) -> GutterDimensions {
27500        if self.show_gutter
27501            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27502            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27503        {
27504            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27505                matches!(
27506                    ProjectSettings::get_global(cx).git.git_gutter,
27507                    GitGutterSetting::TrackedFiles
27508                )
27509            });
27510            let gutter_settings = EditorSettings::get_global(cx).gutter;
27511            let show_line_numbers = self
27512                .show_line_numbers
27513                .unwrap_or(gutter_settings.line_numbers);
27514            let line_gutter_width = if show_line_numbers {
27515                // Avoid flicker-like gutter resizes when the line number gains another digit by
27516                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27517                let min_width_for_number_on_gutter =
27518                    ch_advance * gutter_settings.min_line_number_digits as f32;
27519                self.max_line_number_width(style, window)
27520                    .max(min_width_for_number_on_gutter)
27521            } else {
27522                0.0.into()
27523            };
27524
27525            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27526            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27527
27528            let git_blame_entries_width =
27529                self.git_blame_gutter_max_author_length
27530                    .map(|max_author_length| {
27531                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27532                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27533
27534                        /// The number of characters to dedicate to gaps and margins.
27535                        const SPACING_WIDTH: usize = 4;
27536
27537                        let max_char_count = max_author_length.min(renderer.max_author_length())
27538                            + ::git::SHORT_SHA_LENGTH
27539                            + MAX_RELATIVE_TIMESTAMP.len()
27540                            + SPACING_WIDTH;
27541
27542                        ch_advance * max_char_count
27543                    });
27544
27545            let is_singleton = self.buffer_snapshot().is_singleton();
27546
27547            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27548            left_padding += if !is_singleton {
27549                ch_width * 4.0
27550            } else if show_runnables || show_breakpoints {
27551                ch_width * 3.0
27552            } else if show_git_gutter && show_line_numbers {
27553                ch_width * 2.0
27554            } else if show_git_gutter || show_line_numbers {
27555                ch_width
27556            } else {
27557                px(0.)
27558            };
27559
27560            let shows_folds = is_singleton && gutter_settings.folds;
27561
27562            let right_padding = if shows_folds && show_line_numbers {
27563                ch_width * 4.0
27564            } else if shows_folds || (!is_singleton && show_line_numbers) {
27565                ch_width * 3.0
27566            } else if show_line_numbers {
27567                ch_width
27568            } else {
27569                px(0.)
27570            };
27571
27572            GutterDimensions {
27573                left_padding,
27574                right_padding,
27575                width: line_gutter_width + left_padding + right_padding,
27576                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27577                git_blame_entries_width,
27578            }
27579        } else if self.offset_content {
27580            GutterDimensions::default_with_margin(font_id, font_size, cx)
27581        } else {
27582            GutterDimensions::default()
27583        }
27584    }
27585
27586    pub fn render_crease_toggle(
27587        &self,
27588        buffer_row: MultiBufferRow,
27589        row_contains_cursor: bool,
27590        editor: Entity<Editor>,
27591        window: &mut Window,
27592        cx: &mut App,
27593    ) -> Option<AnyElement> {
27594        let folded = self.is_line_folded(buffer_row);
27595        let mut is_foldable = false;
27596
27597        if let Some(crease) = self
27598            .crease_snapshot
27599            .query_row(buffer_row, self.buffer_snapshot())
27600        {
27601            is_foldable = true;
27602            match crease {
27603                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27604                    if let Some(render_toggle) = render_toggle {
27605                        let toggle_callback =
27606                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27607                                if folded {
27608                                    editor.update(cx, |editor, cx| {
27609                                        editor.fold_at(buffer_row, window, cx)
27610                                    });
27611                                } else {
27612                                    editor.update(cx, |editor, cx| {
27613                                        editor.unfold_at(buffer_row, window, cx)
27614                                    });
27615                                }
27616                            });
27617                        return Some((render_toggle)(
27618                            buffer_row,
27619                            folded,
27620                            toggle_callback,
27621                            window,
27622                            cx,
27623                        ));
27624                    }
27625                }
27626            }
27627        }
27628
27629        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27630
27631        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27632            Some(
27633                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27634                    .toggle_state(folded)
27635                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27636                        if folded {
27637                            this.unfold_at(buffer_row, window, cx);
27638                        } else {
27639                            this.fold_at(buffer_row, window, cx);
27640                        }
27641                    }))
27642                    .into_any_element(),
27643            )
27644        } else {
27645            None
27646        }
27647    }
27648
27649    pub fn render_crease_trailer(
27650        &self,
27651        buffer_row: MultiBufferRow,
27652        window: &mut Window,
27653        cx: &mut App,
27654    ) -> Option<AnyElement> {
27655        let folded = self.is_line_folded(buffer_row);
27656        if let Crease::Inline { render_trailer, .. } = self
27657            .crease_snapshot
27658            .query_row(buffer_row, self.buffer_snapshot())?
27659        {
27660            let render_trailer = render_trailer.as_ref()?;
27661            Some(render_trailer(buffer_row, folded, window, cx))
27662        } else {
27663            None
27664        }
27665    }
27666
27667    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27668        let digit_count = self.widest_line_number().ilog10() + 1;
27669        column_pixels(style, digit_count as usize, window)
27670    }
27671
27672    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27673    ///
27674    /// This is positive if `base` is before `line`.
27675    fn relative_line_delta(
27676        &self,
27677        current_selection_head: DisplayRow,
27678        first_visible_row: DisplayRow,
27679        consider_wrapped_lines: bool,
27680    ) -> i64 {
27681        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27682        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27683
27684        if consider_wrapped_lines {
27685            let wrap_snapshot = self.wrap_snapshot();
27686            let base_wrap_row = wrap_snapshot
27687                .make_wrap_point(current_selection_head, Bias::Left)
27688                .row();
27689            let wrap_row = wrap_snapshot
27690                .make_wrap_point(first_visible_row, Bias::Left)
27691                .row();
27692
27693            wrap_row.0 as i64 - base_wrap_row.0 as i64
27694        } else {
27695            let fold_snapshot = self.fold_snapshot();
27696            let base_fold_row = fold_snapshot
27697                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27698                .row();
27699            let fold_row = fold_snapshot
27700                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27701                .row();
27702
27703            fold_row as i64 - base_fold_row as i64
27704        }
27705    }
27706
27707    /// Returns the unsigned relative line number to display for each row in `rows`.
27708    ///
27709    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27710    pub fn calculate_relative_line_numbers(
27711        &self,
27712        rows: &Range<DisplayRow>,
27713        current_selection_head: DisplayRow,
27714        count_wrapped_lines: bool,
27715    ) -> HashMap<DisplayRow, u32> {
27716        let initial_offset =
27717            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27718
27719        self.row_infos(rows.start)
27720            .take(rows.len())
27721            .enumerate()
27722            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27723            .filter(|(_row, row_info)| {
27724                row_info.buffer_row.is_some()
27725                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27726            })
27727            .enumerate()
27728            .filter_map(|(i, (row, row_info))| {
27729                // We want to ensure here that the current line has absolute
27730                // numbering, even if we are in a soft-wrapped line. With the
27731                // exception that if we are in a deleted line, we should number this
27732                // relative with 0, as otherwise it would have no line number at all
27733                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27734
27735                (relative_line_number != 0
27736                    || row_info
27737                        .diff_status
27738                        .is_some_and(|status| status.is_deleted()))
27739                .then_some((row, relative_line_number))
27740            })
27741            .collect()
27742    }
27743}
27744
27745pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27746    let font_size = style.text.font_size.to_pixels(window.rem_size());
27747    let layout = window.text_system().shape_line(
27748        SharedString::from(" ".repeat(column)),
27749        font_size,
27750        &[TextRun {
27751            len: column,
27752            font: style.text.font(),
27753            color: Hsla::default(),
27754            ..Default::default()
27755        }],
27756        None,
27757    );
27758
27759    layout.width
27760}
27761
27762impl Deref for EditorSnapshot {
27763    type Target = DisplaySnapshot;
27764
27765    fn deref(&self) -> &Self::Target {
27766        &self.display_snapshot
27767    }
27768}
27769
27770#[derive(Clone, Debug, PartialEq, Eq)]
27771pub enum EditorEvent {
27772    /// Emitted when the stored review comments change (added, removed, or updated).
27773    ReviewCommentsChanged {
27774        /// The new total count of review comments.
27775        total_count: usize,
27776    },
27777    InputIgnored {
27778        text: Arc<str>,
27779    },
27780    InputHandled {
27781        utf16_range_to_replace: Option<Range<isize>>,
27782        text: Arc<str>,
27783    },
27784    ExcerptsAdded {
27785        buffer: Entity<Buffer>,
27786        predecessor: ExcerptId,
27787        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27788    },
27789    ExcerptsRemoved {
27790        ids: Vec<ExcerptId>,
27791        removed_buffer_ids: Vec<BufferId>,
27792    },
27793    BufferFoldToggled {
27794        ids: Vec<ExcerptId>,
27795        folded: bool,
27796    },
27797    ExcerptsEdited {
27798        ids: Vec<ExcerptId>,
27799    },
27800    ExcerptsExpanded {
27801        ids: Vec<ExcerptId>,
27802    },
27803    ExpandExcerptsRequested {
27804        excerpt_ids: Vec<ExcerptId>,
27805        lines: u32,
27806        direction: ExpandExcerptDirection,
27807    },
27808    StageOrUnstageRequested {
27809        stage: bool,
27810        hunks: Vec<MultiBufferDiffHunk>,
27811    },
27812    OpenExcerptsRequested {
27813        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27814        split: bool,
27815    },
27816    RestoreRequested {
27817        hunks: Vec<MultiBufferDiffHunk>,
27818    },
27819    BufferEdited,
27820    Edited {
27821        transaction_id: clock::Lamport,
27822    },
27823    Reparsed(BufferId),
27824    Focused,
27825    FocusedIn,
27826    Blurred,
27827    DirtyChanged,
27828    Saved,
27829    TitleChanged,
27830    SelectionsChanged {
27831        local: bool,
27832    },
27833    ScrollPositionChanged {
27834        local: bool,
27835        autoscroll: bool,
27836    },
27837    TransactionUndone {
27838        transaction_id: clock::Lamport,
27839    },
27840    TransactionBegun {
27841        transaction_id: clock::Lamport,
27842    },
27843    CursorShapeChanged,
27844    BreadcrumbsChanged,
27845    OutlineSymbolsChanged,
27846    PushedToNavHistory {
27847        anchor: Anchor,
27848        is_deactivate: bool,
27849    },
27850}
27851
27852impl EventEmitter<EditorEvent> for Editor {}
27853
27854impl Focusable for Editor {
27855    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27856        self.focus_handle.clone()
27857    }
27858}
27859
27860impl Render for Editor {
27861    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27862        EditorElement::new(&cx.entity(), self.create_style(cx))
27863    }
27864}
27865
27866impl EntityInputHandler for Editor {
27867    fn text_for_range(
27868        &mut self,
27869        range_utf16: Range<usize>,
27870        adjusted_range: &mut Option<Range<usize>>,
27871        _: &mut Window,
27872        cx: &mut Context<Self>,
27873    ) -> Option<String> {
27874        let snapshot = self.buffer.read(cx).read(cx);
27875        let start = snapshot.clip_offset_utf16(
27876            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27877            Bias::Left,
27878        );
27879        let end = snapshot.clip_offset_utf16(
27880            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27881            Bias::Right,
27882        );
27883        if (start.0.0..end.0.0) != range_utf16 {
27884            adjusted_range.replace(start.0.0..end.0.0);
27885        }
27886        Some(snapshot.text_for_range(start..end).collect())
27887    }
27888
27889    fn selected_text_range(
27890        &mut self,
27891        ignore_disabled_input: bool,
27892        _: &mut Window,
27893        cx: &mut Context<Self>,
27894    ) -> Option<UTF16Selection> {
27895        // Prevent the IME menu from appearing when holding down an alphabetic key
27896        // while input is disabled.
27897        if !ignore_disabled_input && !self.input_enabled {
27898            return None;
27899        }
27900
27901        let selection = self
27902            .selections
27903            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27904        let range = selection.range();
27905
27906        Some(UTF16Selection {
27907            range: range.start.0.0..range.end.0.0,
27908            reversed: selection.reversed,
27909        })
27910    }
27911
27912    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27913        let snapshot = self.buffer.read(cx).read(cx);
27914        let range = self
27915            .text_highlights(HighlightKey::InputComposition, cx)?
27916            .1
27917            .first()?;
27918        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27919    }
27920
27921    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27922        self.clear_highlights(HighlightKey::InputComposition, cx);
27923        self.ime_transaction.take();
27924    }
27925
27926    fn replace_text_in_range(
27927        &mut self,
27928        range_utf16: Option<Range<usize>>,
27929        text: &str,
27930        window: &mut Window,
27931        cx: &mut Context<Self>,
27932    ) {
27933        if !self.input_enabled {
27934            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27935            return;
27936        }
27937
27938        self.transact(window, cx, |this, window, cx| {
27939            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27940                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27941                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27942                Some(this.selection_replacement_ranges(range_utf16, cx))
27943            } else {
27944                this.marked_text_ranges(cx)
27945            };
27946
27947            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27948                let newest_selection_id = this.selections.newest_anchor().id;
27949                this.selections
27950                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27951                    .iter()
27952                    .zip(ranges_to_replace.iter())
27953                    .find_map(|(selection, range)| {
27954                        if selection.id == newest_selection_id {
27955                            Some(
27956                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27957                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27958                            )
27959                        } else {
27960                            None
27961                        }
27962                    })
27963            });
27964
27965            cx.emit(EditorEvent::InputHandled {
27966                utf16_range_to_replace: range_to_replace,
27967                text: text.into(),
27968            });
27969
27970            if let Some(new_selected_ranges) = new_selected_ranges {
27971                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27972                    selections.select_ranges(new_selected_ranges)
27973                });
27974                this.backspace(&Default::default(), window, cx);
27975            }
27976
27977            this.handle_input(text, window, cx);
27978        });
27979
27980        if let Some(transaction) = self.ime_transaction {
27981            self.buffer.update(cx, |buffer, cx| {
27982                buffer.group_until_transaction(transaction, cx);
27983            });
27984        }
27985
27986        self.unmark_text(window, cx);
27987    }
27988
27989    fn replace_and_mark_text_in_range(
27990        &mut self,
27991        range_utf16: Option<Range<usize>>,
27992        text: &str,
27993        new_selected_range_utf16: Option<Range<usize>>,
27994        window: &mut Window,
27995        cx: &mut Context<Self>,
27996    ) {
27997        if !self.input_enabled {
27998            return;
27999        }
28000
28001        let transaction = self.transact(window, cx, |this, window, cx| {
28002            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28003                let snapshot = this.buffer.read(cx).read(cx);
28004                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28005                    for marked_range in &mut marked_ranges {
28006                        marked_range.end = marked_range.start + relative_range_utf16.end;
28007                        marked_range.start += relative_range_utf16.start;
28008                        marked_range.start =
28009                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28010                        marked_range.end =
28011                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28012                    }
28013                }
28014                Some(marked_ranges)
28015            } else if let Some(range_utf16) = range_utf16 {
28016                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28017                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28018                Some(this.selection_replacement_ranges(range_utf16, cx))
28019            } else {
28020                None
28021            };
28022
28023            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28024                let newest_selection_id = this.selections.newest_anchor().id;
28025                this.selections
28026                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28027                    .iter()
28028                    .zip(ranges_to_replace.iter())
28029                    .find_map(|(selection, range)| {
28030                        if selection.id == newest_selection_id {
28031                            Some(
28032                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28033                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28034                            )
28035                        } else {
28036                            None
28037                        }
28038                    })
28039            });
28040
28041            cx.emit(EditorEvent::InputHandled {
28042                utf16_range_to_replace: range_to_replace,
28043                text: text.into(),
28044            });
28045
28046            if let Some(ranges) = ranges_to_replace {
28047                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28048                    s.select_ranges(ranges)
28049                });
28050            }
28051
28052            let marked_ranges = {
28053                let snapshot = this.buffer.read(cx).read(cx);
28054                this.selections
28055                    .disjoint_anchors_arc()
28056                    .iter()
28057                    .map(|selection| {
28058                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28059                    })
28060                    .collect::<Vec<_>>()
28061            };
28062
28063            if text.is_empty() {
28064                this.unmark_text(window, cx);
28065            } else {
28066                this.highlight_text(
28067                    HighlightKey::InputComposition,
28068                    marked_ranges.clone(),
28069                    HighlightStyle {
28070                        underline: Some(UnderlineStyle {
28071                            thickness: px(1.),
28072                            color: None,
28073                            wavy: false,
28074                        }),
28075                        ..Default::default()
28076                    },
28077                    cx,
28078                );
28079            }
28080
28081            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28082            let use_autoclose = this.use_autoclose;
28083            let use_auto_surround = this.use_auto_surround;
28084            this.set_use_autoclose(false);
28085            this.set_use_auto_surround(false);
28086            this.handle_input(text, window, cx);
28087            this.set_use_autoclose(use_autoclose);
28088            this.set_use_auto_surround(use_auto_surround);
28089
28090            if let Some(new_selected_range) = new_selected_range_utf16 {
28091                let snapshot = this.buffer.read(cx).read(cx);
28092                let new_selected_ranges = marked_ranges
28093                    .into_iter()
28094                    .map(|marked_range| {
28095                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28096                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28097                            insertion_start.0 + new_selected_range.start,
28098                        ));
28099                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28100                            insertion_start.0 + new_selected_range.end,
28101                        ));
28102                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28103                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28104                    })
28105                    .collect::<Vec<_>>();
28106
28107                drop(snapshot);
28108                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28109                    selections.select_ranges(new_selected_ranges)
28110                });
28111            }
28112        });
28113
28114        self.ime_transaction = self.ime_transaction.or(transaction);
28115        if let Some(transaction) = self.ime_transaction {
28116            self.buffer.update(cx, |buffer, cx| {
28117                buffer.group_until_transaction(transaction, cx);
28118            });
28119        }
28120
28121        if self
28122            .text_highlights(HighlightKey::InputComposition, cx)
28123            .is_none()
28124        {
28125            self.ime_transaction.take();
28126        }
28127    }
28128
28129    fn bounds_for_range(
28130        &mut self,
28131        range_utf16: Range<usize>,
28132        element_bounds: gpui::Bounds<Pixels>,
28133        window: &mut Window,
28134        cx: &mut Context<Self>,
28135    ) -> Option<gpui::Bounds<Pixels>> {
28136        let text_layout_details = self.text_layout_details(window, cx);
28137        let CharacterDimensions {
28138            em_width,
28139            em_advance,
28140            line_height,
28141        } = self.character_dimensions(window, cx);
28142
28143        let snapshot = self.snapshot(window, cx);
28144        let scroll_position = snapshot.scroll_position();
28145        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28146
28147        let start =
28148            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28149        let x = Pixels::from(
28150            ScrollOffset::from(
28151                snapshot.x_for_display_point(start, &text_layout_details)
28152                    + self.gutter_dimensions.full_width(),
28153            ) - scroll_left,
28154        );
28155        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28156
28157        Some(Bounds {
28158            origin: element_bounds.origin + point(x, y),
28159            size: size(em_width, line_height),
28160        })
28161    }
28162
28163    fn character_index_for_point(
28164        &mut self,
28165        point: gpui::Point<Pixels>,
28166        _window: &mut Window,
28167        _cx: &mut Context<Self>,
28168    ) -> Option<usize> {
28169        let position_map = self.last_position_map.as_ref()?;
28170        if !position_map.text_hitbox.contains(&point) {
28171            return None;
28172        }
28173        let display_point = position_map.point_for_position(point).previous_valid;
28174        let anchor = position_map
28175            .snapshot
28176            .display_point_to_anchor(display_point, Bias::Left);
28177        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28178        Some(utf16_offset.0.0)
28179    }
28180
28181    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28182        self.expects_character_input
28183    }
28184}
28185
28186trait SelectionExt {
28187    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28188    fn spanned_rows(
28189        &self,
28190        include_end_if_at_line_start: bool,
28191        map: &DisplaySnapshot,
28192    ) -> Range<MultiBufferRow>;
28193}
28194
28195impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28196    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28197        let start = self
28198            .start
28199            .to_point(map.buffer_snapshot())
28200            .to_display_point(map);
28201        let end = self
28202            .end
28203            .to_point(map.buffer_snapshot())
28204            .to_display_point(map);
28205        if self.reversed {
28206            end..start
28207        } else {
28208            start..end
28209        }
28210    }
28211
28212    fn spanned_rows(
28213        &self,
28214        include_end_if_at_line_start: bool,
28215        map: &DisplaySnapshot,
28216    ) -> Range<MultiBufferRow> {
28217        let start = self.start.to_point(map.buffer_snapshot());
28218        let mut end = self.end.to_point(map.buffer_snapshot());
28219        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28220            end.row -= 1;
28221        }
28222
28223        let buffer_start = map.prev_line_boundary(start).0;
28224        let buffer_end = map.next_line_boundary(end).0;
28225        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28226    }
28227}
28228
28229impl<T: InvalidationRegion> InvalidationStack<T> {
28230    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28231    where
28232        S: Clone + ToOffset,
28233    {
28234        while let Some(region) = self.last() {
28235            let all_selections_inside_invalidation_ranges =
28236                if selections.len() == region.ranges().len() {
28237                    selections
28238                        .iter()
28239                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28240                        .all(|(selection, invalidation_range)| {
28241                            let head = selection.head().to_offset(buffer);
28242                            invalidation_range.start <= head && invalidation_range.end >= head
28243                        })
28244                } else {
28245                    false
28246                };
28247
28248            if all_selections_inside_invalidation_ranges {
28249                break;
28250            } else {
28251                self.pop();
28252            }
28253        }
28254    }
28255}
28256
28257#[derive(Clone)]
28258struct ErasedEditorImpl(Entity<Editor>);
28259
28260impl ui_input::ErasedEditor for ErasedEditorImpl {
28261    fn text(&self, cx: &App) -> String {
28262        self.0.read(cx).text(cx)
28263    }
28264
28265    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28266        self.0.update(cx, |this, cx| {
28267            this.set_text(text, window, cx);
28268        })
28269    }
28270
28271    fn clear(&self, window: &mut Window, cx: &mut App) {
28272        self.0.update(cx, |this, cx| this.clear(window, cx));
28273    }
28274
28275    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28276        self.0.update(cx, |this, cx| {
28277            this.set_placeholder_text(text, window, cx);
28278        });
28279    }
28280
28281    fn focus_handle(&self, cx: &App) -> FocusHandle {
28282        self.0.read(cx).focus_handle(cx)
28283    }
28284
28285    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28286        let settings = ThemeSettings::get_global(cx);
28287        let theme_color = cx.theme().colors();
28288
28289        let text_style = TextStyle {
28290            font_family: settings.ui_font.family.clone(),
28291            font_features: settings.ui_font.features.clone(),
28292            font_size: rems(0.875).into(),
28293            font_weight: settings.ui_font.weight,
28294            font_style: FontStyle::Normal,
28295            line_height: relative(1.2),
28296            color: theme_color.text,
28297            ..Default::default()
28298        };
28299        let editor_style = EditorStyle {
28300            background: theme_color.ghost_element_background,
28301            local_player: cx.theme().players().local(),
28302            syntax: cx.theme().syntax().clone(),
28303            text: text_style,
28304            ..Default::default()
28305        };
28306        EditorElement::new(&self.0, editor_style).into_any()
28307    }
28308
28309    fn as_any(&self) -> &dyn Any {
28310        &self.0
28311    }
28312
28313    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28314        self.0.update(cx, |editor, cx| {
28315            let editor_offset = editor.buffer().read(cx).len(cx);
28316            editor.change_selections(
28317                SelectionEffects::scroll(Autoscroll::Next),
28318                window,
28319                cx,
28320                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28321            );
28322        });
28323    }
28324
28325    fn subscribe(
28326        &self,
28327        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28328        window: &mut Window,
28329        cx: &mut App,
28330    ) -> Subscription {
28331        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28332            let event = match event {
28333                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28334                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28335                _ => return,
28336            };
28337            (callback)(event, window, cx);
28338        })
28339    }
28340
28341    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28342        self.0.update(cx, |editor, cx| {
28343            editor.set_masked(masked, cx);
28344        });
28345    }
28346}
28347impl<T> Default for InvalidationStack<T> {
28348    fn default() -> Self {
28349        Self(Default::default())
28350    }
28351}
28352
28353impl<T> Deref for InvalidationStack<T> {
28354    type Target = Vec<T>;
28355
28356    fn deref(&self) -> &Self::Target {
28357        &self.0
28358    }
28359}
28360
28361impl<T> DerefMut for InvalidationStack<T> {
28362    fn deref_mut(&mut self) -> &mut Self::Target {
28363        &mut self.0
28364    }
28365}
28366
28367impl InvalidationRegion for SnippetState {
28368    fn ranges(&self) -> &[Range<Anchor>] {
28369        &self.ranges[self.active_index]
28370    }
28371}
28372
28373fn edit_prediction_edit_text(
28374    current_snapshot: &BufferSnapshot,
28375    edits: &[(Range<Anchor>, impl AsRef<str>)],
28376    edit_preview: &EditPreview,
28377    include_deletions: bool,
28378    cx: &App,
28379) -> HighlightedText {
28380    let edits = edits
28381        .iter()
28382        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28383        .collect::<Vec<_>>();
28384
28385    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28386}
28387
28388fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28389    // Fallback for providers that don't provide edit_preview (like Copilot)
28390    // Just show the raw edit text with basic styling
28391    let mut text = String::new();
28392    let mut highlights = Vec::new();
28393
28394    let insertion_highlight_style = HighlightStyle {
28395        color: Some(cx.theme().colors().text),
28396        ..Default::default()
28397    };
28398
28399    for (_, edit_text) in edits {
28400        let start_offset = text.len();
28401        text.push_str(edit_text);
28402        let end_offset = text.len();
28403
28404        if start_offset < end_offset {
28405            highlights.push((start_offset..end_offset, insertion_highlight_style));
28406        }
28407    }
28408
28409    HighlightedText {
28410        text: text.into(),
28411        highlights,
28412    }
28413}
28414
28415pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28416    match severity {
28417        lsp::DiagnosticSeverity::ERROR => colors.error,
28418        lsp::DiagnosticSeverity::WARNING => colors.warning,
28419        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28420        lsp::DiagnosticSeverity::HINT => colors.info,
28421        _ => colors.ignored,
28422    }
28423}
28424
28425pub fn styled_runs_for_code_label<'a>(
28426    label: &'a CodeLabel,
28427    syntax_theme: &'a theme::SyntaxTheme,
28428    local_player: &'a theme::PlayerColor,
28429) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28430    let fade_out = HighlightStyle {
28431        fade_out: Some(0.35),
28432        ..Default::default()
28433    };
28434
28435    let mut prev_end = label.filter_range.end;
28436    label
28437        .runs
28438        .iter()
28439        .enumerate()
28440        .flat_map(move |(ix, (range, highlight_id))| {
28441            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28442                HighlightStyle {
28443                    color: Some(local_player.cursor),
28444                    ..Default::default()
28445                }
28446            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28447                HighlightStyle {
28448                    background_color: Some(local_player.selection),
28449                    ..Default::default()
28450                }
28451            } else if let Some(style) = highlight_id.style(syntax_theme) {
28452                style
28453            } else {
28454                return Default::default();
28455            };
28456            let muted_style = style.highlight(fade_out);
28457
28458            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28459            if range.start >= label.filter_range.end {
28460                if range.start > prev_end {
28461                    runs.push((prev_end..range.start, fade_out));
28462                }
28463                runs.push((range.clone(), muted_style));
28464            } else if range.end <= label.filter_range.end {
28465                runs.push((range.clone(), style));
28466            } else {
28467                runs.push((range.start..label.filter_range.end, style));
28468                runs.push((label.filter_range.end..range.end, muted_style));
28469            }
28470            prev_end = cmp::max(prev_end, range.end);
28471
28472            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28473                runs.push((prev_end..label.text.len(), fade_out));
28474            }
28475
28476            runs
28477        })
28478}
28479
28480pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28481    let mut prev_index = 0;
28482    let mut prev_codepoint: Option<char> = None;
28483    text.char_indices()
28484        .chain([(text.len(), '\0')])
28485        .filter_map(move |(index, codepoint)| {
28486            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28487            let is_boundary = index == text.len()
28488                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28489                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28490            if is_boundary {
28491                let chunk = &text[prev_index..index];
28492                prev_index = index;
28493                Some(chunk)
28494            } else {
28495                None
28496            }
28497        })
28498}
28499
28500/// Given a string of text immediately before the cursor, iterates over possible
28501/// strings a snippet could match to. More precisely: returns an iterator over
28502/// suffixes of `text` created by splitting at word boundaries (before & after
28503/// every non-word character).
28504///
28505/// Shorter suffixes are returned first.
28506pub(crate) fn snippet_candidate_suffixes<'a>(
28507    text: &'a str,
28508    is_word_char: &'a dyn Fn(char) -> bool,
28509) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28510    let mut prev_index = text.len();
28511    let mut prev_codepoint = None;
28512    text.char_indices()
28513        .rev()
28514        .chain([(0, '\0')])
28515        .filter_map(move |(index, codepoint)| {
28516            let prev_index = std::mem::replace(&mut prev_index, index);
28517            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28518            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28519                None
28520            } else {
28521                let chunk = &text[prev_index..]; // go to end of string
28522                Some(chunk)
28523            }
28524        })
28525}
28526
28527pub trait RangeToAnchorExt: Sized {
28528    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28529
28530    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28531        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28532        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28533    }
28534}
28535
28536impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28537    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28538        let start_offset = self.start.to_offset(snapshot);
28539        let end_offset = self.end.to_offset(snapshot);
28540        if start_offset == end_offset {
28541            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28542        } else {
28543            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28544        }
28545    }
28546}
28547
28548pub trait RowExt {
28549    fn as_f64(&self) -> f64;
28550
28551    fn next_row(&self) -> Self;
28552
28553    fn previous_row(&self) -> Self;
28554
28555    fn minus(&self, other: Self) -> u32;
28556}
28557
28558impl RowExt for DisplayRow {
28559    fn as_f64(&self) -> f64 {
28560        self.0 as _
28561    }
28562
28563    fn next_row(&self) -> Self {
28564        Self(self.0 + 1)
28565    }
28566
28567    fn previous_row(&self) -> Self {
28568        Self(self.0.saturating_sub(1))
28569    }
28570
28571    fn minus(&self, other: Self) -> u32 {
28572        self.0 - other.0
28573    }
28574}
28575
28576impl RowExt for MultiBufferRow {
28577    fn as_f64(&self) -> f64 {
28578        self.0 as _
28579    }
28580
28581    fn next_row(&self) -> Self {
28582        Self(self.0 + 1)
28583    }
28584
28585    fn previous_row(&self) -> Self {
28586        Self(self.0.saturating_sub(1))
28587    }
28588
28589    fn minus(&self, other: Self) -> u32 {
28590        self.0 - other.0
28591    }
28592}
28593
28594trait RowRangeExt {
28595    type Row;
28596
28597    fn len(&self) -> usize;
28598
28599    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28600}
28601
28602impl RowRangeExt for Range<MultiBufferRow> {
28603    type Row = MultiBufferRow;
28604
28605    fn len(&self) -> usize {
28606        (self.end.0 - self.start.0) as usize
28607    }
28608
28609    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28610        (self.start.0..self.end.0).map(MultiBufferRow)
28611    }
28612}
28613
28614impl RowRangeExt for Range<DisplayRow> {
28615    type Row = DisplayRow;
28616
28617    fn len(&self) -> usize {
28618        (self.end.0 - self.start.0) as usize
28619    }
28620
28621    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28622        (self.start.0..self.end.0).map(DisplayRow)
28623    }
28624}
28625
28626/// If select range has more than one line, we
28627/// just point the cursor to range.start.
28628fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28629    if range.start.row == range.end.row {
28630        range
28631    } else {
28632        range.start..range.start
28633    }
28634}
28635pub struct KillRing(ClipboardItem);
28636impl Global for KillRing {}
28637
28638const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28639
28640enum BreakpointPromptEditAction {
28641    Log,
28642    Condition,
28643    HitCondition,
28644}
28645
28646struct BreakpointPromptEditor {
28647    pub(crate) prompt: Entity<Editor>,
28648    editor: WeakEntity<Editor>,
28649    breakpoint_anchor: Anchor,
28650    breakpoint: Breakpoint,
28651    edit_action: BreakpointPromptEditAction,
28652    block_ids: HashSet<CustomBlockId>,
28653    editor_margins: Arc<Mutex<EditorMargins>>,
28654    _subscriptions: Vec<Subscription>,
28655}
28656
28657impl BreakpointPromptEditor {
28658    const MAX_LINES: u8 = 4;
28659
28660    fn new(
28661        editor: WeakEntity<Editor>,
28662        breakpoint_anchor: Anchor,
28663        breakpoint: Breakpoint,
28664        edit_action: BreakpointPromptEditAction,
28665        window: &mut Window,
28666        cx: &mut Context<Self>,
28667    ) -> Self {
28668        let base_text = match edit_action {
28669            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28670            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28671            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28672        }
28673        .map(|msg| msg.to_string())
28674        .unwrap_or_default();
28675
28676        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28677        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28678
28679        let prompt = cx.new(|cx| {
28680            let mut prompt = Editor::new(
28681                EditorMode::AutoHeight {
28682                    min_lines: 1,
28683                    max_lines: Some(Self::MAX_LINES as usize),
28684                },
28685                buffer,
28686                None,
28687                window,
28688                cx,
28689            );
28690            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28691            prompt.set_show_cursor_when_unfocused(false, cx);
28692            prompt.set_placeholder_text(
28693                match edit_action {
28694                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28695                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28696                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28697                },
28698                window,
28699                cx,
28700            );
28701
28702            prompt
28703        });
28704
28705        Self {
28706            prompt,
28707            editor,
28708            breakpoint_anchor,
28709            breakpoint,
28710            edit_action,
28711            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28712            block_ids: Default::default(),
28713            _subscriptions: vec![],
28714        }
28715    }
28716
28717    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28718        self.block_ids.extend(block_ids)
28719    }
28720
28721    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28722        if let Some(editor) = self.editor.upgrade() {
28723            let message = self
28724                .prompt
28725                .read(cx)
28726                .buffer
28727                .read(cx)
28728                .as_singleton()
28729                .expect("A multi buffer in breakpoint prompt isn't possible")
28730                .read(cx)
28731                .as_rope()
28732                .to_string();
28733
28734            editor.update(cx, |editor, cx| {
28735                editor.edit_breakpoint_at_anchor(
28736                    self.breakpoint_anchor,
28737                    self.breakpoint.clone(),
28738                    match self.edit_action {
28739                        BreakpointPromptEditAction::Log => {
28740                            BreakpointEditAction::EditLogMessage(message.into())
28741                        }
28742                        BreakpointPromptEditAction::Condition => {
28743                            BreakpointEditAction::EditCondition(message.into())
28744                        }
28745                        BreakpointPromptEditAction::HitCondition => {
28746                            BreakpointEditAction::EditHitCondition(message.into())
28747                        }
28748                    },
28749                    cx,
28750                );
28751
28752                editor.remove_blocks(self.block_ids.clone(), None, cx);
28753                cx.focus_self(window);
28754            });
28755        }
28756    }
28757
28758    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28759        self.editor
28760            .update(cx, |editor, cx| {
28761                editor.remove_blocks(self.block_ids.clone(), None, cx);
28762                window.focus(&editor.focus_handle, cx);
28763            })
28764            .log_err();
28765    }
28766
28767    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28768        let settings = ThemeSettings::get_global(cx);
28769        let text_style = TextStyle {
28770            color: if self.prompt.read(cx).read_only(cx) {
28771                cx.theme().colors().text_disabled
28772            } else {
28773                cx.theme().colors().text
28774            },
28775            font_family: settings.buffer_font.family.clone(),
28776            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28777            font_size: settings.buffer_font_size(cx).into(),
28778            font_weight: settings.buffer_font.weight,
28779            line_height: relative(settings.buffer_line_height.value()),
28780            ..Default::default()
28781        };
28782        EditorElement::new(
28783            &self.prompt,
28784            EditorStyle {
28785                background: cx.theme().colors().editor_background,
28786                local_player: cx.theme().players().local(),
28787                text: text_style,
28788                ..Default::default()
28789            },
28790        )
28791    }
28792
28793    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
28794        let focus_handle = self.prompt.focus_handle(cx);
28795        IconButton::new("cancel", IconName::Close)
28796            .icon_color(Color::Muted)
28797            .shape(IconButtonShape::Square)
28798            .tooltip(move |_window, cx| {
28799                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
28800            })
28801            .on_click(cx.listener(|this, _, window, cx| {
28802                this.cancel(&menu::Cancel, window, cx);
28803            }))
28804    }
28805
28806    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
28807        let focus_handle = self.prompt.focus_handle(cx);
28808        IconButton::new("confirm", IconName::Return)
28809            .icon_color(Color::Muted)
28810            .shape(IconButtonShape::Square)
28811            .tooltip(move |_window, cx| {
28812                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
28813            })
28814            .on_click(cx.listener(|this, _, window, cx| {
28815                this.confirm(&menu::Confirm, window, cx);
28816            }))
28817    }
28818}
28819
28820impl Render for BreakpointPromptEditor {
28821    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28822        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
28823        let editor_margins = *self.editor_margins.lock();
28824        let gutter_dimensions = editor_margins.gutter;
28825        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
28826        let right_padding = editor_margins.right + px(9.);
28827        h_flex()
28828            .key_context("Editor")
28829            .bg(cx.theme().colors().editor_background)
28830            .border_y_1()
28831            .border_color(cx.theme().status().info_border)
28832            .size_full()
28833            .py(window.line_height() / 2.5)
28834            .pr(right_padding)
28835            .on_action(cx.listener(Self::confirm))
28836            .on_action(cx.listener(Self::cancel))
28837            .child(
28838                WithRemSize::new(ui_font_size)
28839                    .h_full()
28840                    .w(left_gutter_width)
28841                    .flex()
28842                    .flex_row()
28843                    .flex_shrink_0()
28844                    .items_center()
28845                    .justify_center()
28846                    .gap_1()
28847                    .child(self.render_close_button(cx)),
28848            )
28849            .child(
28850                h_flex()
28851                    .w_full()
28852                    .justify_between()
28853                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
28854                    .child(
28855                        WithRemSize::new(ui_font_size)
28856                            .flex()
28857                            .flex_row()
28858                            .items_center()
28859                            .child(self.render_confirm_button(cx)),
28860                    ),
28861            )
28862    }
28863}
28864
28865impl Focusable for BreakpointPromptEditor {
28866    fn focus_handle(&self, cx: &App) -> FocusHandle {
28867        self.prompt.focus_handle(cx)
28868    }
28869}
28870
28871fn all_edits_insertions_or_deletions(
28872    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28873    snapshot: &MultiBufferSnapshot,
28874) -> bool {
28875    let mut all_insertions = true;
28876    let mut all_deletions = true;
28877
28878    for (range, new_text) in edits.iter() {
28879        let range_is_empty = range.to_offset(snapshot).is_empty();
28880        let text_is_empty = new_text.is_empty();
28881
28882        if range_is_empty != text_is_empty {
28883            if range_is_empty {
28884                all_deletions = false;
28885            } else {
28886                all_insertions = false;
28887            }
28888        } else {
28889            return false;
28890        }
28891
28892        if !all_insertions && !all_deletions {
28893            return false;
28894        }
28895    }
28896    all_insertions || all_deletions
28897}
28898
28899struct MissingEditPredictionKeybindingTooltip;
28900
28901impl Render for MissingEditPredictionKeybindingTooltip {
28902    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28903        ui::tooltip_container(cx, |container, cx| {
28904            container
28905                .flex_shrink_0()
28906                .max_w_80()
28907                .min_h(rems_from_px(124.))
28908                .justify_between()
28909                .child(
28910                    v_flex()
28911                        .flex_1()
28912                        .text_ui_sm(cx)
28913                        .child(Label::new("Conflict with Accept Keybinding"))
28914                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28915                )
28916                .child(
28917                    h_flex()
28918                        .pb_1()
28919                        .gap_1()
28920                        .items_end()
28921                        .w_full()
28922                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28923                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28924                        }))
28925                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28926                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28927                        })),
28928                )
28929        })
28930    }
28931}
28932
28933#[derive(Debug, Clone, Copy, PartialEq)]
28934pub struct LineHighlight {
28935    pub background: Background,
28936    pub border: Option<gpui::Hsla>,
28937    pub include_gutter: bool,
28938    pub type_id: Option<TypeId>,
28939}
28940
28941struct LineManipulationResult {
28942    pub new_text: String,
28943    pub line_count_before: usize,
28944    pub line_count_after: usize,
28945}
28946
28947fn render_diff_hunk_controls(
28948    row: u32,
28949    status: &DiffHunkStatus,
28950    hunk_range: Range<Anchor>,
28951    is_created_file: bool,
28952    line_height: Pixels,
28953    editor: &Entity<Editor>,
28954    _window: &mut Window,
28955    cx: &mut App,
28956) -> AnyElement {
28957    h_flex()
28958        .h(line_height)
28959        .mr_1()
28960        .gap_1()
28961        .px_0p5()
28962        .pb_1()
28963        .border_x_1()
28964        .border_b_1()
28965        .border_color(cx.theme().colors().border_variant)
28966        .rounded_b_lg()
28967        .bg(cx.theme().colors().editor_background)
28968        .gap_1()
28969        .block_mouse_except_scroll()
28970        .shadow_md()
28971        .child(if status.has_secondary_hunk() {
28972            Button::new(("stage", row as u64), "Stage")
28973                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28974                .tooltip({
28975                    let focus_handle = editor.focus_handle(cx);
28976                    move |_window, cx| {
28977                        Tooltip::for_action_in(
28978                            "Stage Hunk",
28979                            &::git::ToggleStaged,
28980                            &focus_handle,
28981                            cx,
28982                        )
28983                    }
28984                })
28985                .on_click({
28986                    let editor = editor.clone();
28987                    move |_event, _window, cx| {
28988                        editor.update(cx, |editor, cx| {
28989                            editor.stage_or_unstage_diff_hunks(
28990                                true,
28991                                vec![hunk_range.start..hunk_range.start],
28992                                cx,
28993                            );
28994                        });
28995                    }
28996                })
28997        } else {
28998            Button::new(("unstage", row as u64), "Unstage")
28999                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29000                .tooltip({
29001                    let focus_handle = editor.focus_handle(cx);
29002                    move |_window, cx| {
29003                        Tooltip::for_action_in(
29004                            "Unstage Hunk",
29005                            &::git::ToggleStaged,
29006                            &focus_handle,
29007                            cx,
29008                        )
29009                    }
29010                })
29011                .on_click({
29012                    let editor = editor.clone();
29013                    move |_event, _window, cx| {
29014                        editor.update(cx, |editor, cx| {
29015                            editor.stage_or_unstage_diff_hunks(
29016                                false,
29017                                vec![hunk_range.start..hunk_range.start],
29018                                cx,
29019                            );
29020                        });
29021                    }
29022                })
29023        })
29024        .child(
29025            Button::new(("restore", row as u64), "Restore")
29026                .tooltip({
29027                    let focus_handle = editor.focus_handle(cx);
29028                    move |_window, cx| {
29029                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29030                    }
29031                })
29032                .on_click({
29033                    let editor = editor.clone();
29034                    move |_event, window, cx| {
29035                        editor.update(cx, |editor, cx| {
29036                            let snapshot = editor.snapshot(window, cx);
29037                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29038                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29039                        });
29040                    }
29041                })
29042                .disabled(is_created_file),
29043        )
29044        .when(
29045            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29046            |el| {
29047                el.child(
29048                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29049                        .shape(IconButtonShape::Square)
29050                        .icon_size(IconSize::Small)
29051                        // .disabled(!has_multiple_hunks)
29052                        .tooltip({
29053                            let focus_handle = editor.focus_handle(cx);
29054                            move |_window, cx| {
29055                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29056                            }
29057                        })
29058                        .on_click({
29059                            let editor = editor.clone();
29060                            move |_event, window, cx| {
29061                                editor.update(cx, |editor, cx| {
29062                                    let snapshot = editor.snapshot(window, cx);
29063                                    let position =
29064                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29065                                    editor.go_to_hunk_before_or_after_position(
29066                                        &snapshot,
29067                                        position,
29068                                        Direction::Next,
29069                                        true,
29070                                        window,
29071                                        cx,
29072                                    );
29073                                    editor.expand_selected_diff_hunks(cx);
29074                                });
29075                            }
29076                        }),
29077                )
29078                .child(
29079                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29080                        .shape(IconButtonShape::Square)
29081                        .icon_size(IconSize::Small)
29082                        // .disabled(!has_multiple_hunks)
29083                        .tooltip({
29084                            let focus_handle = editor.focus_handle(cx);
29085                            move |_window, cx| {
29086                                Tooltip::for_action_in(
29087                                    "Previous Hunk",
29088                                    &GoToPreviousHunk,
29089                                    &focus_handle,
29090                                    cx,
29091                                )
29092                            }
29093                        })
29094                        .on_click({
29095                            let editor = editor.clone();
29096                            move |_event, window, cx| {
29097                                editor.update(cx, |editor, cx| {
29098                                    let snapshot = editor.snapshot(window, cx);
29099                                    let point =
29100                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29101                                    editor.go_to_hunk_before_or_after_position(
29102                                        &snapshot,
29103                                        point,
29104                                        Direction::Prev,
29105                                        true,
29106                                        window,
29107                                        cx,
29108                                    );
29109                                    editor.expand_selected_diff_hunks(cx);
29110                                });
29111                            }
29112                        }),
29113                )
29114            },
29115        )
29116        .into_any_element()
29117}
29118
29119pub fn multibuffer_context_lines(cx: &App) -> u32 {
29120    EditorSettings::try_get(cx)
29121        .map(|settings| settings.excerpt_context_lines)
29122        .unwrap_or(2)
29123        .min(32)
29124}