editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod runnables;
   39mod rust_analyzer_ext;
   40pub mod scroll;
   41mod selections_collection;
   42pub mod semantic_tokens;
   43mod split;
   44pub mod split_editor_view;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
  137    WordsQuery,
  138    language_settings::{
  139        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  140        all_language_settings, language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::EditorDb;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::TaskVariables;
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207    ThemeSettings, observe_buffer_font_size_adjustment,
  208};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224pub use zed_actions::editor::RevealInFileManager;
  225use zed_actions::editor::{MoveDown, MoveUp};
  226
  227use crate::{
  228    code_context_menus::CompletionsMenuSource,
  229    editor_settings::MultiCursorModifier,
  230    hover_links::{find_url, find_url_from_range},
  231    inlays::{
  232        InlineValueCache,
  233        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  234    },
  235    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  236    scroll::{ScrollOffset, ScrollPixelOffset},
  237    selections_collection::resolve_selections_wrapping_blocks,
  238    semantic_tokens::SemanticTokenState,
  239    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  240};
  241
  242pub const FILE_HEADER_HEIGHT: u32 = 2;
  243pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  244pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  245const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  246const MAX_LINE_LEN: usize = 1024;
  247const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  248const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  249pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  250#[doc(hidden)]
  251pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  252pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  253
  254pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  256pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  257pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  258
  259pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  260pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  261
  262pub type RenderDiffHunkControlsFn = Arc<
  263    dyn Fn(
  264        u32,
  265        &DiffHunkStatus,
  266        Range<Anchor>,
  267        bool,
  268        Pixels,
  269        &Entity<Editor>,
  270        &mut Window,
  271        &mut App,
  272    ) -> AnyElement,
  273>;
  274
  275enum ReportEditorEvent {
  276    Saved { auto_saved: bool },
  277    EditorOpened,
  278    Closed,
  279}
  280
  281impl ReportEditorEvent {
  282    pub fn event_type(&self) -> &'static str {
  283        match self {
  284            Self::Saved { .. } => "Editor Saved",
  285            Self::EditorOpened => "Editor Opened",
  286            Self::Closed => "Editor Closed",
  287        }
  288    }
  289}
  290
  291pub enum ActiveDebugLine {}
  292pub enum DebugStackFrameLine {}
  293
  294pub enum ConflictsOuter {}
  295pub enum ConflictsOurs {}
  296pub enum ConflictsTheirs {}
  297pub enum ConflictsOursMarker {}
  298pub enum ConflictsTheirsMarker {}
  299
  300pub struct HunkAddedColor;
  301pub struct HunkRemovedColor;
  302
  303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  304pub enum Navigated {
  305    Yes,
  306    No,
  307}
  308
  309impl Navigated {
  310    pub fn from_bool(yes: bool) -> Navigated {
  311        if yes { Navigated::Yes } else { Navigated::No }
  312    }
  313}
  314
  315#[derive(Debug, Clone, PartialEq, Eq)]
  316enum DisplayDiffHunk {
  317    Folded {
  318        display_row: DisplayRow,
  319    },
  320    Unfolded {
  321        is_created_file: bool,
  322        diff_base_byte_range: Range<usize>,
  323        display_row_range: Range<DisplayRow>,
  324        multi_buffer_range: Range<Anchor>,
  325        status: DiffHunkStatus,
  326        word_diffs: Vec<Range<MultiBufferOffset>>,
  327    },
  328}
  329
  330pub enum HideMouseCursorOrigin {
  331    TypingAction,
  332    MovementAction,
  333}
  334
  335pub fn init(cx: &mut App) {
  336    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  337    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  338
  339    workspace::register_project_item::<Editor>(cx);
  340    workspace::FollowableViewRegistry::register::<Editor>(cx);
  341    workspace::register_serializable_item::<Editor>(cx);
  342
  343    cx.observe_new(
  344        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  345            workspace.register_action(Editor::new_file);
  346            workspace.register_action(Editor::new_file_split);
  347            workspace.register_action(Editor::new_file_vertical);
  348            workspace.register_action(Editor::new_file_horizontal);
  349            workspace.register_action(Editor::cancel_language_server_work);
  350            workspace.register_action(Editor::toggle_focus);
  351        },
  352    )
  353    .detach();
  354
  355    cx.on_action(move |_: &workspace::NewFile, cx| {
  356        let app_state = workspace::AppState::global(cx);
  357        if let Some(app_state) = app_state.upgrade() {
  358            workspace::open_new(
  359                Default::default(),
  360                app_state,
  361                cx,
  362                |workspace, window, cx| {
  363                    Editor::new_file(workspace, &Default::default(), window, cx)
  364                },
  365            )
  366            .detach_and_log_err(cx);
  367        }
  368    })
  369    .on_action(move |_: &workspace::NewWindow, cx| {
  370        let app_state = workspace::AppState::global(cx);
  371        if let Some(app_state) = app_state.upgrade() {
  372            workspace::open_new(
  373                Default::default(),
  374                app_state,
  375                cx,
  376                |workspace, window, cx| {
  377                    cx.activate(true);
  378                    Editor::new_file(workspace, &Default::default(), window, cx)
  379                },
  380            )
  381            .detach_and_log_err(cx);
  382        }
  383    });
  384    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  385        Arc::new(ErasedEditorImpl(
  386            cx.new(|cx| Editor::single_line(window, cx)),
  387        )) as Arc<dyn ErasedEditor>
  388    });
  389    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  390}
  391
  392pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  393    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  394}
  395
  396pub trait DiagnosticRenderer {
  397    fn render_group(
  398        &self,
  399        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  400        buffer_id: BufferId,
  401        snapshot: EditorSnapshot,
  402        editor: WeakEntity<Editor>,
  403        language_registry: Option<Arc<LanguageRegistry>>,
  404        cx: &mut App,
  405    ) -> Vec<BlockProperties<Anchor>>;
  406
  407    fn render_hover(
  408        &self,
  409        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  410        range: Range<Point>,
  411        buffer_id: BufferId,
  412        language_registry: Option<Arc<LanguageRegistry>>,
  413        cx: &mut App,
  414    ) -> Option<Entity<markdown::Markdown>>;
  415
  416    fn open_link(
  417        &self,
  418        editor: &mut Editor,
  419        link: SharedString,
  420        window: &mut Window,
  421        cx: &mut Context<Editor>,
  422    );
  423}
  424
  425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  426
  427impl GlobalDiagnosticRenderer {
  428    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  429        cx.try_global::<Self>().map(|g| g.0.clone())
  430    }
  431}
  432
  433impl gpui::Global for GlobalDiagnosticRenderer {}
  434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  435    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  436}
  437
  438pub struct SearchWithinRange;
  439
  440trait InvalidationRegion {
  441    fn ranges(&self) -> &[Range<Anchor>];
  442}
  443
  444#[derive(Clone, Debug, PartialEq)]
  445pub enum SelectPhase {
  446    Begin {
  447        position: DisplayPoint,
  448        add: bool,
  449        click_count: usize,
  450    },
  451    BeginColumnar {
  452        position: DisplayPoint,
  453        reset: bool,
  454        mode: ColumnarMode,
  455        goal_column: u32,
  456    },
  457    Extend {
  458        position: DisplayPoint,
  459        click_count: usize,
  460    },
  461    Update {
  462        position: DisplayPoint,
  463        goal_column: u32,
  464        scroll_delta: gpui::Point<f32>,
  465    },
  466    End,
  467}
  468
  469#[derive(Clone, Debug, PartialEq)]
  470pub enum ColumnarMode {
  471    FromMouse,
  472    FromSelection,
  473}
  474
  475#[derive(Clone, Debug)]
  476pub enum SelectMode {
  477    Character,
  478    Word(Range<Anchor>),
  479    Line(Range<Anchor>),
  480    All,
  481}
  482
  483#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  484pub enum SizingBehavior {
  485    /// The editor will layout itself using `size_full` and will include the vertical
  486    /// scroll margin as requested by user settings.
  487    #[default]
  488    Default,
  489    /// The editor will layout itself using `size_full`, but will not have any
  490    /// vertical overscroll.
  491    ExcludeOverscrollMargin,
  492    /// The editor will request a vertical size according to its content and will be
  493    /// layouted without a vertical scroll margin.
  494    SizeByContent,
  495}
  496
  497#[derive(Clone, PartialEq, Eq, Debug)]
  498pub enum EditorMode {
  499    SingleLine,
  500    AutoHeight {
  501        min_lines: usize,
  502        max_lines: Option<usize>,
  503    },
  504    Full {
  505        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  506        scale_ui_elements_with_buffer_font_size: bool,
  507        /// When set to `true`, the editor will render a background for the active line.
  508        show_active_line_background: bool,
  509        /// Determines the sizing behavior for this editor
  510        sizing_behavior: SizingBehavior,
  511    },
  512    Minimap {
  513        parent: WeakEntity<Editor>,
  514    },
  515}
  516
  517impl EditorMode {
  518    pub fn full() -> Self {
  519        Self::Full {
  520            scale_ui_elements_with_buffer_font_size: true,
  521            show_active_line_background: true,
  522            sizing_behavior: SizingBehavior::Default,
  523        }
  524    }
  525
  526    #[inline]
  527    pub fn is_full(&self) -> bool {
  528        matches!(self, Self::Full { .. })
  529    }
  530
  531    #[inline]
  532    pub fn is_single_line(&self) -> bool {
  533        matches!(self, Self::SingleLine { .. })
  534    }
  535
  536    #[inline]
  537    fn is_minimap(&self) -> bool {
  538        matches!(self, Self::Minimap { .. })
  539    }
  540}
  541
  542#[derive(Copy, Clone, Debug)]
  543pub enum SoftWrap {
  544    /// Prefer not to wrap at all.
  545    ///
  546    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  547    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  548    GitDiff,
  549    /// Prefer a single line generally, unless an overly long line is encountered.
  550    None,
  551    /// Soft wrap lines that exceed the editor width.
  552    EditorWidth,
  553    /// Soft wrap lines at the preferred line length.
  554    Column(u32),
  555    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  556    Bounded(u32),
  557}
  558
  559#[derive(Clone)]
  560pub struct EditorStyle {
  561    pub background: Hsla,
  562    pub border: Hsla,
  563    pub local_player: PlayerColor,
  564    pub text: TextStyle,
  565    pub scrollbar_width: Pixels,
  566    pub syntax: Arc<SyntaxTheme>,
  567    pub status: StatusColors,
  568    pub inlay_hints_style: HighlightStyle,
  569    pub edit_prediction_styles: EditPredictionStyles,
  570    pub unnecessary_code_fade: f32,
  571    pub show_underlines: bool,
  572}
  573
  574impl Default for EditorStyle {
  575    fn default() -> Self {
  576        Self {
  577            background: Hsla::default(),
  578            border: Hsla::default(),
  579            local_player: PlayerColor::default(),
  580            text: TextStyle::default(),
  581            scrollbar_width: Pixels::default(),
  582            syntax: Default::default(),
  583            // HACK: Status colors don't have a real default.
  584            // We should look into removing the status colors from the editor
  585            // style and retrieve them directly from the theme.
  586            status: StatusColors::dark(),
  587            inlay_hints_style: HighlightStyle::default(),
  588            edit_prediction_styles: EditPredictionStyles {
  589                insertion: HighlightStyle::default(),
  590                whitespace: HighlightStyle::default(),
  591            },
  592            unnecessary_code_fade: Default::default(),
  593            show_underlines: true,
  594        }
  595    }
  596}
  597
  598pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  599    let show_background = language_settings::language_settings(None, None, cx)
  600        .inlay_hints
  601        .show_background;
  602
  603    let mut style = cx.theme().syntax().get("hint");
  604
  605    if style.color.is_none() {
  606        style.color = Some(cx.theme().status().hint);
  607    }
  608
  609    if !show_background {
  610        style.background_color = None;
  611        return style;
  612    }
  613
  614    if style.background_color.is_none() {
  615        style.background_color = Some(cx.theme().status().hint_background);
  616    }
  617
  618    style
  619}
  620
  621pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  622    EditPredictionStyles {
  623        insertion: HighlightStyle {
  624            color: Some(cx.theme().status().predictive),
  625            ..HighlightStyle::default()
  626        },
  627        whitespace: HighlightStyle {
  628            background_color: Some(cx.theme().status().created_background),
  629            ..HighlightStyle::default()
  630        },
  631    }
  632}
  633
  634type CompletionId = usize;
  635
  636pub(crate) enum EditDisplayMode {
  637    TabAccept,
  638    DiffPopover,
  639    Inline,
  640}
  641
  642enum EditPrediction {
  643    Edit {
  644        edits: Vec<(Range<Anchor>, Arc<str>)>,
  645        /// Predicted cursor position as (anchor, offset_from_anchor).
  646        /// The anchor is in multibuffer coordinates; after applying edits,
  647        /// resolve the anchor and add the offset to get the final cursor position.
  648        cursor_position: Option<(Anchor, usize)>,
  649        edit_preview: Option<EditPreview>,
  650        display_mode: EditDisplayMode,
  651        snapshot: BufferSnapshot,
  652    },
  653    /// Move to a specific location in the active editor
  654    MoveWithin {
  655        target: Anchor,
  656        snapshot: BufferSnapshot,
  657    },
  658    /// Move to a specific location in a different editor (not the active one)
  659    MoveOutside {
  660        target: language::Anchor,
  661        snapshot: BufferSnapshot,
  662    },
  663}
  664
  665struct EditPredictionState {
  666    inlay_ids: Vec<InlayId>,
  667    completion: EditPrediction,
  668    completion_id: Option<SharedString>,
  669    invalidation_range: Option<Range<Anchor>>,
  670}
  671
  672enum EditPredictionSettings {
  673    Disabled,
  674    Enabled {
  675        show_in_menu: bool,
  676        preview_requires_modifier: bool,
  677    },
  678}
  679
  680#[derive(Debug, Clone)]
  681struct InlineDiagnostic {
  682    message: SharedString,
  683    group_id: usize,
  684    is_primary: bool,
  685    start: Point,
  686    severity: lsp::DiagnosticSeverity,
  687}
  688
  689pub enum MenuEditPredictionsPolicy {
  690    Never,
  691    ByProvider,
  692}
  693
  694pub enum EditPredictionPreview {
  695    /// Modifier is not pressed
  696    Inactive { released_too_fast: bool },
  697    /// Modifier pressed
  698    Active {
  699        since: Instant,
  700        previous_scroll_position: Option<SharedScrollAnchor>,
  701    },
  702}
  703
  704#[derive(Copy, Clone, Eq, PartialEq)]
  705enum EditPredictionKeybindSurface {
  706    Inline,
  707    CursorPopoverCompact,
  708    CursorPopoverExpanded,
  709}
  710
  711#[derive(Copy, Clone, Eq, PartialEq, Debug)]
  712enum EditPredictionKeybindAction {
  713    Accept,
  714    Preview,
  715}
  716
  717struct EditPredictionKeybindDisplay {
  718    #[cfg(test)]
  719    accept_keystroke: Option<gpui::KeybindingKeystroke>,
  720    #[cfg(test)]
  721    preview_keystroke: Option<gpui::KeybindingKeystroke>,
  722    displayed_keystroke: Option<gpui::KeybindingKeystroke>,
  723    action: EditPredictionKeybindAction,
  724    missing_accept_keystroke: bool,
  725    show_hold_label: bool,
  726}
  727
  728impl EditPredictionPreview {
  729    pub fn released_too_fast(&self) -> bool {
  730        match self {
  731            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  732            EditPredictionPreview::Active { .. } => false,
  733        }
  734    }
  735
  736    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  737        if let EditPredictionPreview::Active {
  738            previous_scroll_position,
  739            ..
  740        } = self
  741        {
  742            *previous_scroll_position = scroll_position;
  743        }
  744    }
  745}
  746
  747pub struct ContextMenuOptions {
  748    pub min_entries_visible: usize,
  749    pub max_entries_visible: usize,
  750    pub placement: Option<ContextMenuPlacement>,
  751}
  752
  753#[derive(Debug, Clone, PartialEq, Eq)]
  754pub enum ContextMenuPlacement {
  755    Above,
  756    Below,
  757}
  758
  759#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  760struct EditorActionId(usize);
  761
  762impl EditorActionId {
  763    pub fn post_inc(&mut self) -> Self {
  764        let answer = self.0;
  765
  766        *self = Self(answer + 1);
  767
  768        Self(answer)
  769    }
  770}
  771
  772// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  773// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  774
  775type BackgroundHighlight = (
  776    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  777    Arc<[Range<Anchor>]>,
  778);
  779type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  780
  781#[derive(Default)]
  782struct ScrollbarMarkerState {
  783    scrollbar_size: Size<Pixels>,
  784    dirty: bool,
  785    markers: Arc<[PaintQuad]>,
  786    pending_refresh: Option<Task<Result<()>>>,
  787}
  788
  789impl ScrollbarMarkerState {
  790    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  791        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  792    }
  793}
  794
  795#[derive(Clone, Copy, PartialEq, Eq)]
  796pub enum MinimapVisibility {
  797    Disabled,
  798    Enabled {
  799        /// The configuration currently present in the users settings.
  800        setting_configuration: bool,
  801        /// Whether to override the currently set visibility from the users setting.
  802        toggle_override: bool,
  803    },
  804}
  805
  806impl MinimapVisibility {
  807    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  808        if mode.is_full() {
  809            Self::Enabled {
  810                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  811                toggle_override: false,
  812            }
  813        } else {
  814            Self::Disabled
  815        }
  816    }
  817
  818    fn hidden(&self) -> Self {
  819        match *self {
  820            Self::Enabled {
  821                setting_configuration,
  822                ..
  823            } => Self::Enabled {
  824                setting_configuration,
  825                toggle_override: setting_configuration,
  826            },
  827            Self::Disabled => Self::Disabled,
  828        }
  829    }
  830
  831    fn disabled(&self) -> bool {
  832        matches!(*self, Self::Disabled)
  833    }
  834
  835    fn settings_visibility(&self) -> bool {
  836        match *self {
  837            Self::Enabled {
  838                setting_configuration,
  839                ..
  840            } => setting_configuration,
  841            _ => false,
  842        }
  843    }
  844
  845    fn visible(&self) -> bool {
  846        match *self {
  847            Self::Enabled {
  848                setting_configuration,
  849                toggle_override,
  850            } => setting_configuration ^ toggle_override,
  851            _ => false,
  852        }
  853    }
  854
  855    fn toggle_visibility(&self) -> Self {
  856        match *self {
  857            Self::Enabled {
  858                toggle_override,
  859                setting_configuration,
  860            } => Self::Enabled {
  861                setting_configuration,
  862                toggle_override: !toggle_override,
  863            },
  864            Self::Disabled => Self::Disabled,
  865        }
  866    }
  867}
  868
  869#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  870pub enum BufferSerialization {
  871    All,
  872    NonDirtyBuffers,
  873}
  874
  875impl BufferSerialization {
  876    fn new(restore_unsaved_buffers: bool) -> Self {
  877        if restore_unsaved_buffers {
  878            Self::All
  879        } else {
  880            Self::NonDirtyBuffers
  881        }
  882    }
  883}
  884
  885/// Addons allow storing per-editor state in other crates (e.g. Vim)
  886pub trait Addon: 'static {
  887    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  888
  889    fn render_buffer_header_controls(
  890        &self,
  891        _: &ExcerptInfo,
  892        _: &Window,
  893        _: &App,
  894    ) -> Option<AnyElement> {
  895        None
  896    }
  897
  898    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  899        None
  900    }
  901
  902    fn to_any(&self) -> &dyn std::any::Any;
  903
  904    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  905        None
  906    }
  907}
  908
  909struct ChangeLocation {
  910    current: Option<Vec<Anchor>>,
  911    original: Vec<Anchor>,
  912}
  913impl ChangeLocation {
  914    fn locations(&self) -> &[Anchor] {
  915        self.current.as_ref().unwrap_or(&self.original)
  916    }
  917}
  918
  919/// A set of caret positions, registered when the editor was edited.
  920pub struct ChangeList {
  921    changes: Vec<ChangeLocation>,
  922    /// Currently "selected" change.
  923    position: Option<usize>,
  924}
  925
  926impl ChangeList {
  927    pub fn new() -> Self {
  928        Self {
  929            changes: Vec::new(),
  930            position: None,
  931        }
  932    }
  933
  934    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  935    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  936    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  937        if self.changes.is_empty() {
  938            return None;
  939        }
  940
  941        let prev = self.position.unwrap_or(self.changes.len());
  942        let next = if direction == Direction::Prev {
  943            prev.saturating_sub(count)
  944        } else {
  945            (prev + count).min(self.changes.len() - 1)
  946        };
  947        self.position = Some(next);
  948        self.changes.get(next).map(|change| change.locations())
  949    }
  950
  951    /// Adds a new change to the list, resetting the change list position.
  952    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  953        self.position.take();
  954        if let Some(last) = self.changes.last_mut()
  955            && group
  956        {
  957            last.current = Some(new_positions)
  958        } else {
  959            self.changes.push(ChangeLocation {
  960                original: new_positions,
  961                current: None,
  962            });
  963        }
  964    }
  965
  966    pub fn last(&self) -> Option<&[Anchor]> {
  967        self.changes.last().map(|change| change.locations())
  968    }
  969
  970    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  971        self.changes.last().map(|change| change.original.as_slice())
  972    }
  973
  974    pub fn invert_last_group(&mut self) {
  975        if let Some(last) = self.changes.last_mut()
  976            && let Some(current) = last.current.as_mut()
  977        {
  978            mem::swap(&mut last.original, current);
  979        }
  980    }
  981}
  982
  983#[derive(Clone)]
  984struct InlineBlamePopoverState {
  985    scroll_handle: ScrollHandle,
  986    commit_message: Option<ParsedCommitMessage>,
  987    markdown: Entity<Markdown>,
  988}
  989
  990struct InlineBlamePopover {
  991    position: gpui::Point<Pixels>,
  992    hide_task: Option<Task<()>>,
  993    popover_bounds: Option<Bounds<Pixels>>,
  994    popover_state: InlineBlamePopoverState,
  995    keyboard_grace: bool,
  996}
  997
  998enum SelectionDragState {
  999    /// State when no drag related activity is detected.
 1000    None,
 1001    /// State when the mouse is down on a selection that is about to be dragged.
 1002    ReadyToDrag {
 1003        selection: Selection<Anchor>,
 1004        click_position: gpui::Point<Pixels>,
 1005        mouse_down_time: Instant,
 1006    },
 1007    /// State when the mouse is dragging the selection in the editor.
 1008    Dragging {
 1009        selection: Selection<Anchor>,
 1010        drop_cursor: Selection<Anchor>,
 1011        hide_drop_cursor: bool,
 1012    },
 1013}
 1014
 1015enum ColumnarSelectionState {
 1016    FromMouse {
 1017        selection_tail: Anchor,
 1018        display_point: Option<DisplayPoint>,
 1019    },
 1020    FromSelection {
 1021        selection_tail: Anchor,
 1022    },
 1023}
 1024
 1025/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1026/// a breakpoint on them.
 1027#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1028struct PhantomBreakpointIndicator {
 1029    display_row: DisplayRow,
 1030    /// There's a small debounce between hovering over the line and showing the indicator.
 1031    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1032    is_active: bool,
 1033    collides_with_existing_breakpoint: bool,
 1034}
 1035
 1036/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1037/// in diff view mode.
 1038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1039pub(crate) struct PhantomDiffReviewIndicator {
 1040    /// The starting anchor of the selection (or the only row if not dragging).
 1041    pub start: Anchor,
 1042    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1043    pub end: Anchor,
 1044    /// There's a small debounce between hovering over the line and showing the indicator.
 1045    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1046    pub is_active: bool,
 1047}
 1048
 1049#[derive(Clone, Debug)]
 1050pub(crate) struct DiffReviewDragState {
 1051    pub start_anchor: Anchor,
 1052    pub current_anchor: Anchor,
 1053}
 1054
 1055impl DiffReviewDragState {
 1056    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1057        let start = self.start_anchor.to_display_point(snapshot).row();
 1058        let current = self.current_anchor.to_display_point(snapshot).row();
 1059
 1060        (start..=current).sorted()
 1061    }
 1062}
 1063
 1064/// Identifies a specific hunk in the diff buffer.
 1065/// Used as a key to group comments by their location.
 1066#[derive(Clone, Debug)]
 1067pub struct DiffHunkKey {
 1068    /// The file path (relative to worktree) this hunk belongs to.
 1069    pub file_path: Arc<util::rel_path::RelPath>,
 1070    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1071    pub hunk_start_anchor: Anchor,
 1072}
 1073
 1074/// A review comment stored locally before being sent to the Agent panel.
 1075#[derive(Clone)]
 1076pub struct StoredReviewComment {
 1077    /// Unique identifier for this comment (for edit/delete operations).
 1078    pub id: usize,
 1079    /// The comment text entered by the user.
 1080    pub comment: String,
 1081    /// Anchors for the code range being reviewed.
 1082    pub range: Range<Anchor>,
 1083    /// Timestamp when the comment was created (for chronological ordering).
 1084    pub created_at: Instant,
 1085    /// Whether this comment is currently being edited inline.
 1086    pub is_editing: bool,
 1087}
 1088
 1089impl StoredReviewComment {
 1090    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1091        Self {
 1092            id,
 1093            comment,
 1094            range: anchor_range,
 1095            created_at: Instant::now(),
 1096            is_editing: false,
 1097        }
 1098    }
 1099}
 1100
 1101/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1102pub(crate) struct DiffReviewOverlay {
 1103    pub anchor_range: Range<Anchor>,
 1104    /// The block ID for the overlay.
 1105    pub block_id: CustomBlockId,
 1106    /// The editor entity for the review input.
 1107    pub prompt_editor: Entity<Editor>,
 1108    /// The hunk key this overlay belongs to.
 1109    pub hunk_key: DiffHunkKey,
 1110    /// Whether the comments section is expanded.
 1111    pub comments_expanded: bool,
 1112    /// Editors for comments currently being edited inline.
 1113    /// Key: comment ID, Value: Editor entity for inline editing.
 1114    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1115    /// Subscriptions for inline edit editors' action handlers.
 1116    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1117    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1118    /// The current user's avatar URI for display in comment rows.
 1119    pub user_avatar_uri: Option<SharedUri>,
 1120    /// Subscription to keep the action handler alive.
 1121    _subscription: Subscription,
 1122}
 1123
 1124/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1125///
 1126/// See the [module level documentation](self) for more information.
 1127pub struct Editor {
 1128    focus_handle: FocusHandle,
 1129    last_focused_descendant: Option<WeakFocusHandle>,
 1130    /// The text buffer being edited
 1131    buffer: Entity<MultiBuffer>,
 1132    /// Map of how text in the buffer should be displayed.
 1133    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1134    pub display_map: Entity<DisplayMap>,
 1135    placeholder_display_map: Option<Entity<DisplayMap>>,
 1136    pub selections: SelectionsCollection,
 1137    pub scroll_manager: ScrollManager,
 1138    /// When inline assist editors are linked, they all render cursors because
 1139    /// typing enters text into each of them, even the ones that aren't focused.
 1140    pub(crate) show_cursor_when_unfocused: bool,
 1141    columnar_selection_state: Option<ColumnarSelectionState>,
 1142    add_selections_state: Option<AddSelectionsState>,
 1143    select_next_state: Option<SelectNextState>,
 1144    select_prev_state: Option<SelectNextState>,
 1145    selection_history: SelectionHistory,
 1146    defer_selection_effects: bool,
 1147    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1148    autoclose_regions: Vec<AutocloseRegion>,
 1149    snippet_stack: InvalidationStack<SnippetState>,
 1150    select_syntax_node_history: SelectSyntaxNodeHistory,
 1151    ime_transaction: Option<TransactionId>,
 1152    pub diagnostics_max_severity: DiagnosticSeverity,
 1153    active_diagnostics: ActiveDiagnostic,
 1154    show_inline_diagnostics: bool,
 1155    inline_diagnostics_update: Task<()>,
 1156    inline_diagnostics_enabled: bool,
 1157    diagnostics_enabled: bool,
 1158    word_completions_enabled: bool,
 1159    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1160    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1161    hard_wrap: Option<usize>,
 1162    project: Option<Entity<Project>>,
 1163    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1164    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1165    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1166    blink_manager: Entity<BlinkManager>,
 1167    show_cursor_names: bool,
 1168    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1169    pub show_local_selections: bool,
 1170    mode: EditorMode,
 1171    show_breadcrumbs: bool,
 1172    show_gutter: bool,
 1173    show_scrollbars: ScrollbarAxes,
 1174    minimap_visibility: MinimapVisibility,
 1175    offset_content: bool,
 1176    disable_expand_excerpt_buttons: bool,
 1177    delegate_expand_excerpts: bool,
 1178    delegate_stage_and_restore: bool,
 1179    delegate_open_excerpts: bool,
 1180    enable_lsp_data: bool,
 1181    enable_runnables: bool,
 1182    show_line_numbers: Option<bool>,
 1183    use_relative_line_numbers: Option<bool>,
 1184    show_git_diff_gutter: Option<bool>,
 1185    show_code_actions: Option<bool>,
 1186    show_runnables: Option<bool>,
 1187    show_breakpoints: Option<bool>,
 1188    show_diff_review_button: bool,
 1189    show_wrap_guides: Option<bool>,
 1190    show_indent_guides: Option<bool>,
 1191    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1192    highlight_order: usize,
 1193    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1194    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1195    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1196    scrollbar_marker_state: ScrollbarMarkerState,
 1197    active_indent_guides_state: ActiveIndentGuidesState,
 1198    nav_history: Option<ItemNavHistory>,
 1199    context_menu: RefCell<Option<CodeContextMenu>>,
 1200    context_menu_options: Option<ContextMenuOptions>,
 1201    mouse_context_menu: Option<MouseContextMenu>,
 1202    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1203    inline_blame_popover: Option<InlineBlamePopover>,
 1204    inline_blame_popover_show_task: Option<Task<()>>,
 1205    signature_help_state: SignatureHelpState,
 1206    auto_signature_help: Option<bool>,
 1207    find_all_references_task_sources: Vec<Anchor>,
 1208    next_completion_id: CompletionId,
 1209    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1210    code_actions_task: Option<Task<Result<()>>>,
 1211    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1212    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1213    debounced_selection_highlight_complete: bool,
 1214    document_highlights_task: Option<Task<()>>,
 1215    linked_editing_range_task: Option<Task<Option<()>>>,
 1216    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1217    pending_rename: Option<RenameState>,
 1218    searchable: bool,
 1219    cursor_shape: CursorShape,
 1220    /// Whether the cursor is offset one character to the left when something is
 1221    /// selected (needed for vim visual mode)
 1222    cursor_offset_on_selection: bool,
 1223    current_line_highlight: Option<CurrentLineHighlight>,
 1224    /// Whether to collapse search match ranges to just their start position.
 1225    /// When true, navigating to a match positions the cursor at the match
 1226    /// without selecting the matched text.
 1227    collapse_matches: bool,
 1228    autoindent_mode: Option<AutoindentMode>,
 1229    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1230    input_enabled: bool,
 1231    expects_character_input: bool,
 1232    use_modal_editing: bool,
 1233    read_only: bool,
 1234    leader_id: Option<CollaboratorId>,
 1235    remote_id: Option<ViewId>,
 1236    pub hover_state: HoverState,
 1237    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1238    prev_pressure_stage: Option<PressureStage>,
 1239    gutter_hovered: bool,
 1240    hovered_link_state: Option<HoveredLinkState>,
 1241    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1242    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1243    active_edit_prediction: Option<EditPredictionState>,
 1244    /// Used to prevent flickering as the user types while the menu is open
 1245    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1246    edit_prediction_settings: EditPredictionSettings,
 1247    edit_predictions_hidden_for_vim_mode: bool,
 1248    show_edit_predictions_override: Option<bool>,
 1249    show_completions_on_input_override: Option<bool>,
 1250    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1251    edit_prediction_preview: EditPredictionPreview,
 1252    in_leading_whitespace: bool,
 1253    next_inlay_id: usize,
 1254    next_color_inlay_id: usize,
 1255    _subscriptions: Vec<Subscription>,
 1256    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1257    gutter_dimensions: GutterDimensions,
 1258    style: Option<EditorStyle>,
 1259    text_style_refinement: Option<TextStyleRefinement>,
 1260    next_editor_action_id: EditorActionId,
 1261    editor_actions: Rc<
 1262        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1263    >,
 1264    use_autoclose: bool,
 1265    use_auto_surround: bool,
 1266    auto_replace_emoji_shortcode: bool,
 1267    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1268    show_git_blame_gutter: bool,
 1269    show_git_blame_inline: bool,
 1270    show_git_blame_inline_delay_task: Option<Task<()>>,
 1271    git_blame_inline_enabled: bool,
 1272    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1273    buffer_serialization: Option<BufferSerialization>,
 1274    show_selection_menu: Option<bool>,
 1275    blame: Option<Entity<GitBlame>>,
 1276    blame_subscription: Option<Subscription>,
 1277    custom_context_menu: Option<
 1278        Box<
 1279            dyn 'static
 1280                + Fn(
 1281                    &mut Self,
 1282                    DisplayPoint,
 1283                    &mut Window,
 1284                    &mut Context<Self>,
 1285                ) -> Option<Entity<ui::ContextMenu>>,
 1286        >,
 1287    >,
 1288    last_bounds: Option<Bounds<Pixels>>,
 1289    last_position_map: Option<Rc<PositionMap>>,
 1290    expect_bounds_change: Option<Bounds<Pixels>>,
 1291    runnables: RunnableData,
 1292    breakpoint_store: Option<Entity<BreakpointStore>>,
 1293    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1294    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1295    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1296    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1297    /// when hunks have comments stored.
 1298    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1299    /// Stored review comments grouped by hunk.
 1300    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1301    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1302    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1303    /// Counter for generating unique comment IDs.
 1304    next_review_comment_id: usize,
 1305    hovered_diff_hunk_row: Option<DisplayRow>,
 1306    pull_diagnostics_task: Task<()>,
 1307    in_project_search: bool,
 1308    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1309    breadcrumb_header: Option<String>,
 1310    focused_block: Option<FocusedBlock>,
 1311    next_scroll_position: NextScrollCursorCenterTopBottom,
 1312    addons: HashMap<TypeId, Box<dyn Addon>>,
 1313    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1314    load_diff_task: Option<Shared<Task<()>>>,
 1315    /// Whether we are temporarily displaying a diff other than git's
 1316    temporary_diff_override: bool,
 1317    selection_mark_mode: bool,
 1318    toggle_fold_multiple_buffers: Task<()>,
 1319    _scroll_cursor_center_top_bottom_task: Task<()>,
 1320    serialize_selections: Task<()>,
 1321    serialize_folds: Task<()>,
 1322    mouse_cursor_hidden: bool,
 1323    minimap: Option<Entity<Self>>,
 1324    hide_mouse_mode: HideMouseMode,
 1325    pub change_list: ChangeList,
 1326    inline_value_cache: InlineValueCache,
 1327    number_deleted_lines: bool,
 1328
 1329    selection_drag_state: SelectionDragState,
 1330    colors: Option<LspColorData>,
 1331    post_scroll_update: Task<()>,
 1332    refresh_colors_task: Task<()>,
 1333    use_document_folding_ranges: bool,
 1334    refresh_folding_ranges_task: Task<()>,
 1335    inlay_hints: Option<LspInlayHintData>,
 1336    folding_newlines: Task<()>,
 1337    select_next_is_case_sensitive: Option<bool>,
 1338    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1339    on_local_selections_changed:
 1340        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1341    suppress_selection_callback: bool,
 1342    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1343    accent_data: Option<AccentData>,
 1344    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1345    semantic_token_state: SemanticTokenState,
 1346    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1347    refresh_document_symbols_task: Shared<Task<()>>,
 1348    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1349    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1350    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1351    sticky_headers_task: Task<()>,
 1352    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1353    pub(crate) colorize_brackets_task: Task<()>,
 1354}
 1355
 1356#[derive(Debug, PartialEq)]
 1357struct AccentData {
 1358    colors: AccentColors,
 1359    overrides: Vec<SharedString>,
 1360}
 1361
 1362fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1363    if debounce_ms > 0 {
 1364        Some(Duration::from_millis(debounce_ms))
 1365    } else {
 1366        None
 1367    }
 1368}
 1369
 1370#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1371enum NextScrollCursorCenterTopBottom {
 1372    #[default]
 1373    Center,
 1374    Top,
 1375    Bottom,
 1376}
 1377
 1378impl NextScrollCursorCenterTopBottom {
 1379    fn next(&self) -> Self {
 1380        match self {
 1381            Self::Center => Self::Top,
 1382            Self::Top => Self::Bottom,
 1383            Self::Bottom => Self::Center,
 1384        }
 1385    }
 1386}
 1387
 1388#[derive(Clone)]
 1389pub struct EditorSnapshot {
 1390    pub mode: EditorMode,
 1391    show_gutter: bool,
 1392    offset_content: bool,
 1393    show_line_numbers: Option<bool>,
 1394    number_deleted_lines: bool,
 1395    show_git_diff_gutter: Option<bool>,
 1396    show_code_actions: Option<bool>,
 1397    show_runnables: Option<bool>,
 1398    show_breakpoints: Option<bool>,
 1399    git_blame_gutter_max_author_length: Option<usize>,
 1400    pub display_snapshot: DisplaySnapshot,
 1401    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1402    is_focused: bool,
 1403    scroll_anchor: SharedScrollAnchor,
 1404    ongoing_scroll: OngoingScroll,
 1405    current_line_highlight: CurrentLineHighlight,
 1406    gutter_hovered: bool,
 1407    semantic_tokens_enabled: bool,
 1408}
 1409
 1410#[derive(Default, Debug, Clone, Copy)]
 1411pub struct GutterDimensions {
 1412    pub left_padding: Pixels,
 1413    pub right_padding: Pixels,
 1414    pub width: Pixels,
 1415    pub margin: Pixels,
 1416    pub git_blame_entries_width: Option<Pixels>,
 1417}
 1418
 1419impl GutterDimensions {
 1420    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1421        Self {
 1422            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1423            ..Default::default()
 1424        }
 1425    }
 1426
 1427    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1428        -cx.text_system().descent(font_id, font_size)
 1429    }
 1430    /// The full width of the space taken up by the gutter.
 1431    pub fn full_width(&self) -> Pixels {
 1432        self.margin + self.width
 1433    }
 1434
 1435    /// The width of the space reserved for the fold indicators,
 1436    /// use alongside 'justify_end' and `gutter_width` to
 1437    /// right align content with the line numbers
 1438    pub fn fold_area_width(&self) -> Pixels {
 1439        self.margin + self.right_padding
 1440    }
 1441}
 1442
 1443struct CharacterDimensions {
 1444    em_width: Pixels,
 1445    em_advance: Pixels,
 1446    line_height: Pixels,
 1447}
 1448
 1449#[derive(Debug)]
 1450pub struct RemoteSelection {
 1451    pub replica_id: ReplicaId,
 1452    pub selection: Selection<Anchor>,
 1453    pub cursor_shape: CursorShape,
 1454    pub collaborator_id: CollaboratorId,
 1455    pub line_mode: bool,
 1456    pub user_name: Option<SharedString>,
 1457    pub color: PlayerColor,
 1458}
 1459
 1460#[derive(Clone, Debug)]
 1461struct SelectionHistoryEntry {
 1462    selections: Arc<[Selection<Anchor>]>,
 1463    select_next_state: Option<SelectNextState>,
 1464    select_prev_state: Option<SelectNextState>,
 1465    add_selections_state: Option<AddSelectionsState>,
 1466}
 1467
 1468#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1469enum SelectionHistoryMode {
 1470    #[default]
 1471    Normal,
 1472    Undoing,
 1473    Redoing,
 1474    Skipping,
 1475}
 1476
 1477#[derive(Clone, PartialEq, Eq, Hash)]
 1478struct HoveredCursor {
 1479    replica_id: ReplicaId,
 1480    selection_id: usize,
 1481}
 1482
 1483#[derive(Debug)]
 1484/// SelectionEffects controls the side-effects of updating the selection.
 1485///
 1486/// The default behaviour does "what you mostly want":
 1487/// - it pushes to the nav history if the cursor moved by >10 lines
 1488/// - it re-triggers completion requests
 1489/// - it scrolls to fit
 1490///
 1491/// You might want to modify these behaviours. For example when doing a "jump"
 1492/// like go to definition, we always want to add to nav history; but when scrolling
 1493/// in vim mode we never do.
 1494///
 1495/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1496/// move.
 1497#[derive(Clone)]
 1498pub struct SelectionEffects {
 1499    nav_history: Option<bool>,
 1500    completions: bool,
 1501    scroll: Option<Autoscroll>,
 1502}
 1503
 1504impl Default for SelectionEffects {
 1505    fn default() -> Self {
 1506        Self {
 1507            nav_history: None,
 1508            completions: true,
 1509            scroll: Some(Autoscroll::fit()),
 1510        }
 1511    }
 1512}
 1513impl SelectionEffects {
 1514    pub fn scroll(scroll: Autoscroll) -> Self {
 1515        Self {
 1516            scroll: Some(scroll),
 1517            ..Default::default()
 1518        }
 1519    }
 1520
 1521    pub fn no_scroll() -> Self {
 1522        Self {
 1523            scroll: None,
 1524            ..Default::default()
 1525        }
 1526    }
 1527
 1528    pub fn completions(self, completions: bool) -> Self {
 1529        Self {
 1530            completions,
 1531            ..self
 1532        }
 1533    }
 1534
 1535    pub fn nav_history(self, nav_history: bool) -> Self {
 1536        Self {
 1537            nav_history: Some(nav_history),
 1538            ..self
 1539        }
 1540    }
 1541}
 1542
 1543struct DeferredSelectionEffectsState {
 1544    changed: bool,
 1545    effects: SelectionEffects,
 1546    old_cursor_position: Anchor,
 1547    history_entry: SelectionHistoryEntry,
 1548}
 1549
 1550#[derive(Default)]
 1551struct SelectionHistory {
 1552    #[allow(clippy::type_complexity)]
 1553    selections_by_transaction:
 1554        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1555    mode: SelectionHistoryMode,
 1556    undo_stack: VecDeque<SelectionHistoryEntry>,
 1557    redo_stack: VecDeque<SelectionHistoryEntry>,
 1558}
 1559
 1560impl SelectionHistory {
 1561    #[track_caller]
 1562    fn insert_transaction(
 1563        &mut self,
 1564        transaction_id: TransactionId,
 1565        selections: Arc<[Selection<Anchor>]>,
 1566    ) {
 1567        if selections.is_empty() {
 1568            log::error!(
 1569                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1570                std::panic::Location::caller()
 1571            );
 1572            return;
 1573        }
 1574        self.selections_by_transaction
 1575            .insert(transaction_id, (selections, None));
 1576    }
 1577
 1578    #[allow(clippy::type_complexity)]
 1579    fn transaction(
 1580        &self,
 1581        transaction_id: TransactionId,
 1582    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1583        self.selections_by_transaction.get(&transaction_id)
 1584    }
 1585
 1586    #[allow(clippy::type_complexity)]
 1587    fn transaction_mut(
 1588        &mut self,
 1589        transaction_id: TransactionId,
 1590    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1591        self.selections_by_transaction.get_mut(&transaction_id)
 1592    }
 1593
 1594    fn push(&mut self, entry: SelectionHistoryEntry) {
 1595        if !entry.selections.is_empty() {
 1596            match self.mode {
 1597                SelectionHistoryMode::Normal => {
 1598                    self.push_undo(entry);
 1599                    self.redo_stack.clear();
 1600                }
 1601                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1602                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1603                SelectionHistoryMode::Skipping => {}
 1604            }
 1605        }
 1606    }
 1607
 1608    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1609        if self
 1610            .undo_stack
 1611            .back()
 1612            .is_none_or(|e| e.selections != entry.selections)
 1613        {
 1614            self.undo_stack.push_back(entry);
 1615            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1616                self.undo_stack.pop_front();
 1617            }
 1618        }
 1619    }
 1620
 1621    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1622        if self
 1623            .redo_stack
 1624            .back()
 1625            .is_none_or(|e| e.selections != entry.selections)
 1626        {
 1627            self.redo_stack.push_back(entry);
 1628            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1629                self.redo_stack.pop_front();
 1630            }
 1631        }
 1632    }
 1633}
 1634
 1635#[derive(Clone, Copy)]
 1636pub struct RowHighlightOptions {
 1637    pub autoscroll: bool,
 1638    pub include_gutter: bool,
 1639}
 1640
 1641impl Default for RowHighlightOptions {
 1642    fn default() -> Self {
 1643        Self {
 1644            autoscroll: Default::default(),
 1645            include_gutter: true,
 1646        }
 1647    }
 1648}
 1649
 1650struct RowHighlight {
 1651    index: usize,
 1652    range: Range<Anchor>,
 1653    color: Hsla,
 1654    options: RowHighlightOptions,
 1655    type_id: TypeId,
 1656}
 1657
 1658#[derive(Clone, Debug)]
 1659struct AddSelectionsState {
 1660    groups: Vec<AddSelectionsGroup>,
 1661}
 1662
 1663#[derive(Clone, Debug)]
 1664struct AddSelectionsGroup {
 1665    above: bool,
 1666    stack: Vec<usize>,
 1667}
 1668
 1669#[derive(Clone)]
 1670struct SelectNextState {
 1671    query: AhoCorasick,
 1672    wordwise: bool,
 1673    done: bool,
 1674}
 1675
 1676impl std::fmt::Debug for SelectNextState {
 1677    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1678        f.debug_struct(std::any::type_name::<Self>())
 1679            .field("wordwise", &self.wordwise)
 1680            .field("done", &self.done)
 1681            .finish()
 1682    }
 1683}
 1684
 1685#[derive(Debug)]
 1686struct AutocloseRegion {
 1687    selection_id: usize,
 1688    range: Range<Anchor>,
 1689    pair: BracketPair,
 1690}
 1691
 1692#[derive(Debug)]
 1693struct SnippetState {
 1694    ranges: Vec<Vec<Range<Anchor>>>,
 1695    active_index: usize,
 1696    choices: Vec<Option<Vec<String>>>,
 1697}
 1698
 1699#[doc(hidden)]
 1700pub struct RenameState {
 1701    pub range: Range<Anchor>,
 1702    pub old_name: Arc<str>,
 1703    pub editor: Entity<Editor>,
 1704    block_id: CustomBlockId,
 1705}
 1706
 1707struct InvalidationStack<T>(Vec<T>);
 1708
 1709struct RegisteredEditPredictionDelegate {
 1710    provider: Arc<dyn EditPredictionDelegateHandle>,
 1711    _subscription: Subscription,
 1712}
 1713
 1714#[derive(Debug, PartialEq, Eq)]
 1715pub struct ActiveDiagnosticGroup {
 1716    pub active_range: Range<Anchor>,
 1717    pub active_message: String,
 1718    pub group_id: usize,
 1719    pub blocks: HashSet<CustomBlockId>,
 1720}
 1721
 1722#[derive(Debug, PartialEq, Eq)]
 1723
 1724pub(crate) enum ActiveDiagnostic {
 1725    None,
 1726    All,
 1727    Group(ActiveDiagnosticGroup),
 1728}
 1729
 1730#[derive(Serialize, Deserialize, Clone, Debug)]
 1731pub struct ClipboardSelection {
 1732    /// The number of bytes in this selection.
 1733    pub len: usize,
 1734    /// Whether this was a full-line selection.
 1735    pub is_entire_line: bool,
 1736    /// The indentation of the first line when this content was originally copied.
 1737    pub first_line_indent: u32,
 1738    #[serde(default)]
 1739    pub file_path: Option<PathBuf>,
 1740    #[serde(default)]
 1741    pub line_range: Option<RangeInclusive<u32>>,
 1742}
 1743
 1744impl ClipboardSelection {
 1745    pub fn for_buffer(
 1746        len: usize,
 1747        is_entire_line: bool,
 1748        range: Range<Point>,
 1749        buffer: &MultiBufferSnapshot,
 1750        project: Option<&Entity<Project>>,
 1751        cx: &App,
 1752    ) -> Self {
 1753        let first_line_indent = buffer
 1754            .indent_size_for_line(MultiBufferRow(range.start.row))
 1755            .len;
 1756
 1757        let file_path = util::maybe!({
 1758            let project = project?.read(cx);
 1759            let file = buffer.file_at(range.start)?;
 1760            let project_path = ProjectPath {
 1761                worktree_id: file.worktree_id(cx),
 1762                path: file.path().clone(),
 1763            };
 1764            project.absolute_path(&project_path, cx)
 1765        });
 1766
 1767        let line_range = file_path.as_ref().and_then(|_| {
 1768            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1769            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1770            if start_excerpt_id == end_excerpt_id {
 1771                Some(start_point.row..=end_point.row)
 1772            } else {
 1773                None
 1774            }
 1775        });
 1776
 1777        Self {
 1778            len,
 1779            is_entire_line,
 1780            first_line_indent,
 1781            file_path,
 1782            line_range,
 1783        }
 1784    }
 1785}
 1786
 1787// selections, scroll behavior, was newest selection reversed
 1788type SelectSyntaxNodeHistoryState = (
 1789    Box<[Selection<Anchor>]>,
 1790    SelectSyntaxNodeScrollBehavior,
 1791    bool,
 1792);
 1793
 1794#[derive(Default)]
 1795struct SelectSyntaxNodeHistory {
 1796    stack: Vec<SelectSyntaxNodeHistoryState>,
 1797    // disable temporarily to allow changing selections without losing the stack
 1798    pub disable_clearing: bool,
 1799}
 1800
 1801impl SelectSyntaxNodeHistory {
 1802    pub fn try_clear(&mut self) {
 1803        if !self.disable_clearing {
 1804            self.stack.clear();
 1805        }
 1806    }
 1807
 1808    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1809        self.stack.push(selection);
 1810    }
 1811
 1812    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1813        self.stack.pop()
 1814    }
 1815}
 1816
 1817enum SelectSyntaxNodeScrollBehavior {
 1818    CursorTop,
 1819    FitSelection,
 1820    CursorBottom,
 1821}
 1822
 1823#[derive(Debug, Clone, Copy)]
 1824pub(crate) struct NavigationData {
 1825    cursor_anchor: Anchor,
 1826    cursor_position: Point,
 1827    scroll_anchor: ScrollAnchor,
 1828    scroll_top_row: u32,
 1829}
 1830
 1831#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1832pub enum GotoDefinitionKind {
 1833    Symbol,
 1834    Declaration,
 1835    Type,
 1836    Implementation,
 1837}
 1838
 1839pub enum FormatTarget {
 1840    Buffers(HashSet<Entity<Buffer>>),
 1841    Ranges(Vec<Range<MultiBufferPoint>>),
 1842}
 1843
 1844pub(crate) struct FocusedBlock {
 1845    id: BlockId,
 1846    focus_handle: WeakFocusHandle,
 1847}
 1848
 1849#[derive(Clone, Debug)]
 1850pub enum JumpData {
 1851    MultiBufferRow {
 1852        row: MultiBufferRow,
 1853        line_offset_from_top: u32,
 1854    },
 1855    MultiBufferPoint {
 1856        excerpt_id: ExcerptId,
 1857        position: Point,
 1858        anchor: text::Anchor,
 1859        line_offset_from_top: u32,
 1860    },
 1861}
 1862
 1863pub enum MultibufferSelectionMode {
 1864    First,
 1865    All,
 1866}
 1867
 1868#[derive(Clone, Copy, Debug, Default)]
 1869pub struct RewrapOptions {
 1870    pub override_language_settings: bool,
 1871    pub preserve_existing_whitespace: bool,
 1872}
 1873
 1874impl Editor {
 1875    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1876        let buffer = cx.new(|cx| Buffer::local("", cx));
 1877        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1878        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1879    }
 1880
 1881    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1882        let buffer = cx.new(|cx| Buffer::local("", cx));
 1883        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1884        Self::new(EditorMode::full(), buffer, None, window, cx)
 1885    }
 1886
 1887    pub fn auto_height(
 1888        min_lines: usize,
 1889        max_lines: usize,
 1890        window: &mut Window,
 1891        cx: &mut Context<Self>,
 1892    ) -> Self {
 1893        let buffer = cx.new(|cx| Buffer::local("", cx));
 1894        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1895        Self::new(
 1896            EditorMode::AutoHeight {
 1897                min_lines,
 1898                max_lines: Some(max_lines),
 1899            },
 1900            buffer,
 1901            None,
 1902            window,
 1903            cx,
 1904        )
 1905    }
 1906
 1907    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1908    /// The editor grows as tall as needed to fit its content.
 1909    pub fn auto_height_unbounded(
 1910        min_lines: usize,
 1911        window: &mut Window,
 1912        cx: &mut Context<Self>,
 1913    ) -> Self {
 1914        let buffer = cx.new(|cx| Buffer::local("", cx));
 1915        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1916        Self::new(
 1917            EditorMode::AutoHeight {
 1918                min_lines,
 1919                max_lines: None,
 1920            },
 1921            buffer,
 1922            None,
 1923            window,
 1924            cx,
 1925        )
 1926    }
 1927
 1928    pub fn for_buffer(
 1929        buffer: Entity<Buffer>,
 1930        project: Option<Entity<Project>>,
 1931        window: &mut Window,
 1932        cx: &mut Context<Self>,
 1933    ) -> Self {
 1934        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1935        Self::new(EditorMode::full(), buffer, project, window, cx)
 1936    }
 1937
 1938    pub fn for_multibuffer(
 1939        buffer: Entity<MultiBuffer>,
 1940        project: Option<Entity<Project>>,
 1941        window: &mut Window,
 1942        cx: &mut Context<Self>,
 1943    ) -> Self {
 1944        Self::new(EditorMode::full(), buffer, project, window, cx)
 1945    }
 1946
 1947    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1948        let mut clone = Self::new(
 1949            self.mode.clone(),
 1950            self.buffer.clone(),
 1951            self.project.clone(),
 1952            window,
 1953            cx,
 1954        );
 1955        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1956            let snapshot = display_map.snapshot(cx);
 1957            clone.display_map.update(cx, |display_map, cx| {
 1958                display_map.set_state(&snapshot, cx);
 1959            });
 1960            snapshot
 1961        });
 1962        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1963        clone.folds_did_change(cx);
 1964        clone.selections.clone_state(&self.selections);
 1965        clone
 1966            .scroll_manager
 1967            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1968        clone.searchable = self.searchable;
 1969        clone.read_only = self.read_only;
 1970        clone.buffers_with_disabled_indent_guides =
 1971            self.buffers_with_disabled_indent_guides.clone();
 1972        clone
 1973    }
 1974
 1975    pub fn new(
 1976        mode: EditorMode,
 1977        buffer: Entity<MultiBuffer>,
 1978        project: Option<Entity<Project>>,
 1979        window: &mut Window,
 1980        cx: &mut Context<Self>,
 1981    ) -> Self {
 1982        Editor::new_internal(mode, buffer, project, None, window, cx)
 1983    }
 1984
 1985    pub fn refresh_sticky_headers(
 1986        &mut self,
 1987        display_snapshot: &DisplaySnapshot,
 1988        cx: &mut Context<Editor>,
 1989    ) {
 1990        if !self.mode.is_full() {
 1991            return;
 1992        }
 1993        let multi_buffer = display_snapshot.buffer_snapshot();
 1994        let scroll_anchor = self
 1995            .scroll_manager
 1996            .native_anchor(display_snapshot, cx)
 1997            .anchor;
 1998        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 1999            return;
 2000        };
 2001        let buffer = buffer.clone();
 2002
 2003        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2004        let max_row = buffer.max_point().row;
 2005        let start_row = buffer_visible_start.row.min(max_row);
 2006        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2007
 2008        let syntax = self.style(cx).syntax.clone();
 2009        let background_task = cx.background_spawn(async move {
 2010            buffer
 2011                .outline_items_containing(
 2012                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2013                    true,
 2014                    Some(syntax.as_ref()),
 2015                )
 2016                .into_iter()
 2017                .map(|outline_item| OutlineItem {
 2018                    depth: outline_item.depth,
 2019                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2020                    source_range_for_text: Anchor::range_in_buffer(
 2021                        excerpt_id,
 2022                        outline_item.source_range_for_text,
 2023                    ),
 2024                    text: outline_item.text,
 2025                    highlight_ranges: outline_item.highlight_ranges,
 2026                    name_ranges: outline_item.name_ranges,
 2027                    body_range: outline_item
 2028                        .body_range
 2029                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2030                    annotation_range: outline_item
 2031                        .annotation_range
 2032                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2033                })
 2034                .collect()
 2035        });
 2036        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2037            let sticky_headers = background_task.await;
 2038            this.update(cx, |this, cx| {
 2039                this.sticky_headers = Some(sticky_headers);
 2040                cx.notify();
 2041            })
 2042            .ok();
 2043        });
 2044    }
 2045
 2046    fn new_internal(
 2047        mode: EditorMode,
 2048        multi_buffer: Entity<MultiBuffer>,
 2049        project: Option<Entity<Project>>,
 2050        display_map: Option<Entity<DisplayMap>>,
 2051        window: &mut Window,
 2052        cx: &mut Context<Self>,
 2053    ) -> Self {
 2054        debug_assert!(
 2055            display_map.is_none() || mode.is_minimap(),
 2056            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2057        );
 2058
 2059        let full_mode = mode.is_full();
 2060        let is_minimap = mode.is_minimap();
 2061        let diagnostics_max_severity = if full_mode {
 2062            EditorSettings::get_global(cx)
 2063                .diagnostics_max_severity
 2064                .unwrap_or(DiagnosticSeverity::Hint)
 2065        } else {
 2066            DiagnosticSeverity::Off
 2067        };
 2068        let style = window.text_style();
 2069        let font_size = style.font_size.to_pixels(window.rem_size());
 2070        let editor = cx.entity().downgrade();
 2071        let fold_placeholder = FoldPlaceholder {
 2072            constrain_width: false,
 2073            render: Arc::new(move |fold_id, fold_range, cx| {
 2074                let editor = editor.clone();
 2075                FoldPlaceholder::fold_element(fold_id, cx)
 2076                    .cursor_pointer()
 2077                    .child("")
 2078                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2079                    .on_click(move |_, _window, cx| {
 2080                        editor
 2081                            .update(cx, |editor, cx| {
 2082                                editor.unfold_ranges(
 2083                                    &[fold_range.start..fold_range.end],
 2084                                    true,
 2085                                    false,
 2086                                    cx,
 2087                                );
 2088                                cx.stop_propagation();
 2089                            })
 2090                            .ok();
 2091                    })
 2092                    .into_any()
 2093            }),
 2094            merge_adjacent: true,
 2095            ..FoldPlaceholder::default()
 2096        };
 2097        let display_map = display_map.unwrap_or_else(|| {
 2098            cx.new(|cx| {
 2099                DisplayMap::new(
 2100                    multi_buffer.clone(),
 2101                    style.font(),
 2102                    font_size,
 2103                    None,
 2104                    FILE_HEADER_HEIGHT,
 2105                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2106                    fold_placeholder,
 2107                    diagnostics_max_severity,
 2108                    cx,
 2109                )
 2110            })
 2111        });
 2112
 2113        let selections = SelectionsCollection::new();
 2114
 2115        let blink_manager = cx.new(|cx| {
 2116            let mut blink_manager = BlinkManager::new(
 2117                CURSOR_BLINK_INTERVAL,
 2118                |cx| EditorSettings::get_global(cx).cursor_blink,
 2119                cx,
 2120            );
 2121            if is_minimap {
 2122                blink_manager.disable(cx);
 2123            }
 2124            blink_manager
 2125        });
 2126
 2127        let soft_wrap_mode_override =
 2128            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2129
 2130        let mut project_subscriptions = Vec::new();
 2131        if full_mode && let Some(project) = project.as_ref() {
 2132            project_subscriptions.push(cx.subscribe_in(
 2133                project,
 2134                window,
 2135                |editor, _, event, window, cx| match event {
 2136                    project::Event::RefreshCodeLens => {
 2137                        // we always query lens with actions, without storing them, always refreshing them
 2138                    }
 2139                    project::Event::RefreshInlayHints {
 2140                        server_id,
 2141                        request_id,
 2142                    } => {
 2143                        editor.refresh_inlay_hints(
 2144                            InlayHintRefreshReason::RefreshRequested {
 2145                                server_id: *server_id,
 2146                                request_id: *request_id,
 2147                            },
 2148                            cx,
 2149                        );
 2150                    }
 2151                    project::Event::RefreshSemanticTokens {
 2152                        server_id,
 2153                        request_id,
 2154                    } => {
 2155                        editor.refresh_semantic_tokens(
 2156                            None,
 2157                            Some(RefreshForServer {
 2158                                server_id: *server_id,
 2159                                request_id: *request_id,
 2160                            }),
 2161                            cx,
 2162                        );
 2163                    }
 2164                    project::Event::LanguageServerRemoved(_) => {
 2165                        editor.registered_buffers.clear();
 2166                        editor.register_visible_buffers(cx);
 2167                        editor.invalidate_semantic_tokens(None);
 2168                        editor.refresh_runnables(None, window, cx);
 2169                        editor.update_lsp_data(None, window, cx);
 2170                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2171                    }
 2172                    project::Event::SnippetEdit(id, snippet_edits) => {
 2173                        // todo(lw): Non singletons
 2174                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2175                            let snapshot = buffer.read(cx).snapshot();
 2176                            let focus_handle = editor.focus_handle(cx);
 2177                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2178                                for (range, snippet) in snippet_edits {
 2179                                    let buffer_range =
 2180                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2181                                    editor
 2182                                        .insert_snippet(
 2183                                            &[MultiBufferOffset(buffer_range.start)
 2184                                                ..MultiBufferOffset(buffer_range.end)],
 2185                                            snippet.clone(),
 2186                                            window,
 2187                                            cx,
 2188                                        )
 2189                                        .ok();
 2190                                }
 2191                            }
 2192                        }
 2193                    }
 2194                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2195                        let buffer_id = *buffer_id;
 2196                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2197                            editor.register_buffer(buffer_id, cx);
 2198                            editor.refresh_runnables(Some(buffer_id), window, cx);
 2199                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2200                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2201                            refresh_linked_ranges(editor, window, cx);
 2202                            editor.refresh_code_actions(window, cx);
 2203                            editor.refresh_document_highlights(cx);
 2204                        }
 2205                    }
 2206
 2207                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2208                        let Some(workspace) = editor.workspace() else {
 2209                            return;
 2210                        };
 2211                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2212                        else {
 2213                            return;
 2214                        };
 2215
 2216                        if active_editor.entity_id() == cx.entity_id() {
 2217                            let entity_id = cx.entity_id();
 2218                            workspace.update(cx, |this, cx| {
 2219                                this.panes_mut()
 2220                                    .iter_mut()
 2221                                    .filter(|pane| pane.entity_id() != entity_id)
 2222                                    .for_each(|p| {
 2223                                        p.update(cx, |pane, _| {
 2224                                            pane.nav_history_mut().rename_item(
 2225                                                entity_id,
 2226                                                project_path.clone(),
 2227                                                abs_path.clone().into(),
 2228                                            );
 2229                                        })
 2230                                    });
 2231                            });
 2232
 2233                            Self::open_transaction_for_hidden_buffers(
 2234                                workspace,
 2235                                transaction.clone(),
 2236                                "Rename".to_string(),
 2237                                window,
 2238                                cx,
 2239                            );
 2240                        }
 2241                    }
 2242
 2243                    project::Event::WorkspaceEditApplied(transaction) => {
 2244                        let Some(workspace) = editor.workspace() else {
 2245                            return;
 2246                        };
 2247                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2248                        else {
 2249                            return;
 2250                        };
 2251
 2252                        if active_editor.entity_id() == cx.entity_id() {
 2253                            Self::open_transaction_for_hidden_buffers(
 2254                                workspace,
 2255                                transaction.clone(),
 2256                                "LSP Edit".to_string(),
 2257                                window,
 2258                                cx,
 2259                            );
 2260                        }
 2261                    }
 2262
 2263                    _ => {}
 2264                },
 2265            ));
 2266            if let Some(task_inventory) = project
 2267                .read(cx)
 2268                .task_store()
 2269                .read(cx)
 2270                .task_inventory()
 2271                .cloned()
 2272            {
 2273                project_subscriptions.push(cx.observe_in(
 2274                    &task_inventory,
 2275                    window,
 2276                    |editor, _, window, cx| {
 2277                        editor.refresh_runnables(None, window, cx);
 2278                    },
 2279                ));
 2280            };
 2281
 2282            project_subscriptions.push(cx.subscribe_in(
 2283                &project.read(cx).breakpoint_store(),
 2284                window,
 2285                |editor, _, event, window, cx| match event {
 2286                    BreakpointStoreEvent::ClearDebugLines => {
 2287                        editor.clear_row_highlights::<ActiveDebugLine>();
 2288                        editor.refresh_inline_values(cx);
 2289                    }
 2290                    BreakpointStoreEvent::SetDebugLine => {
 2291                        if editor.go_to_active_debug_line(window, cx) {
 2292                            cx.stop_propagation();
 2293                        }
 2294
 2295                        editor.refresh_inline_values(cx);
 2296                    }
 2297                    _ => {}
 2298                },
 2299            ));
 2300            let git_store = project.read(cx).git_store().clone();
 2301            let project = project.clone();
 2302            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2303                if let GitStoreEvent::RepositoryAdded = event {
 2304                    this.load_diff_task = Some(
 2305                        update_uncommitted_diff_for_buffer(
 2306                            cx.entity(),
 2307                            &project,
 2308                            this.buffer.read(cx).all_buffers(),
 2309                            this.buffer.clone(),
 2310                            cx,
 2311                        )
 2312                        .shared(),
 2313                    );
 2314                }
 2315            }));
 2316        }
 2317
 2318        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2319
 2320        let inlay_hint_settings =
 2321            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2322        let focus_handle = cx.focus_handle();
 2323        if !is_minimap {
 2324            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2325                .detach();
 2326            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2327                .detach();
 2328            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2329                .detach();
 2330            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2331                .detach();
 2332            cx.observe_pending_input(window, Self::observe_pending_input)
 2333                .detach();
 2334        }
 2335
 2336        let show_indent_guides =
 2337            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2338                Some(false)
 2339            } else {
 2340                None
 2341            };
 2342
 2343        let breakpoint_store = match (&mode, project.as_ref()) {
 2344            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2345            _ => None,
 2346        };
 2347
 2348        let mut code_action_providers = Vec::new();
 2349        let mut load_uncommitted_diff = None;
 2350        if let Some(project) = project.clone() {
 2351            load_uncommitted_diff = Some(
 2352                update_uncommitted_diff_for_buffer(
 2353                    cx.entity(),
 2354                    &project,
 2355                    multi_buffer.read(cx).all_buffers(),
 2356                    multi_buffer.clone(),
 2357                    cx,
 2358                )
 2359                .shared(),
 2360            );
 2361            code_action_providers.push(Rc::new(project) as Rc<_>);
 2362        }
 2363
 2364        let mut editor = Self {
 2365            focus_handle,
 2366            show_cursor_when_unfocused: false,
 2367            last_focused_descendant: None,
 2368            buffer: multi_buffer.clone(),
 2369            display_map: display_map.clone(),
 2370            placeholder_display_map: None,
 2371            selections,
 2372            scroll_manager: ScrollManager::new(cx),
 2373            columnar_selection_state: None,
 2374            add_selections_state: None,
 2375            select_next_state: None,
 2376            select_prev_state: None,
 2377            selection_history: SelectionHistory::default(),
 2378            defer_selection_effects: false,
 2379            deferred_selection_effects_state: None,
 2380            autoclose_regions: Vec::new(),
 2381            snippet_stack: InvalidationStack::default(),
 2382            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2383            ime_transaction: None,
 2384            active_diagnostics: ActiveDiagnostic::None,
 2385            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2386            inline_diagnostics_update: Task::ready(()),
 2387            inline_diagnostics: Vec::new(),
 2388            soft_wrap_mode_override,
 2389            diagnostics_max_severity,
 2390            hard_wrap: None,
 2391            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2392            semantics_provider: project
 2393                .as_ref()
 2394                .map(|project| Rc::new(project.downgrade()) as _),
 2395            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2396            project,
 2397            blink_manager: blink_manager.clone(),
 2398            show_local_selections: true,
 2399            show_scrollbars: ScrollbarAxes {
 2400                horizontal: full_mode,
 2401                vertical: full_mode,
 2402            },
 2403            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2404            offset_content: !matches!(mode, EditorMode::SingleLine),
 2405            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2406            show_gutter: full_mode,
 2407            show_line_numbers: (!full_mode).then_some(false),
 2408            use_relative_line_numbers: None,
 2409            disable_expand_excerpt_buttons: !full_mode,
 2410            delegate_expand_excerpts: false,
 2411            delegate_stage_and_restore: false,
 2412            delegate_open_excerpts: false,
 2413            enable_lsp_data: true,
 2414            enable_runnables: true,
 2415            show_git_diff_gutter: None,
 2416            show_code_actions: None,
 2417            show_runnables: None,
 2418            show_breakpoints: None,
 2419            show_diff_review_button: false,
 2420            show_wrap_guides: None,
 2421            show_indent_guides,
 2422            buffers_with_disabled_indent_guides: HashSet::default(),
 2423            highlight_order: 0,
 2424            highlighted_rows: HashMap::default(),
 2425            background_highlights: HashMap::default(),
 2426            gutter_highlights: HashMap::default(),
 2427            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2428            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2429            nav_history: None,
 2430            context_menu: RefCell::new(None),
 2431            context_menu_options: None,
 2432            mouse_context_menu: None,
 2433            completion_tasks: Vec::new(),
 2434            inline_blame_popover: None,
 2435            inline_blame_popover_show_task: None,
 2436            signature_help_state: SignatureHelpState::default(),
 2437            auto_signature_help: None,
 2438            find_all_references_task_sources: Vec::new(),
 2439            next_completion_id: 0,
 2440            next_inlay_id: 0,
 2441            code_action_providers,
 2442            available_code_actions: None,
 2443            code_actions_task: None,
 2444            quick_selection_highlight_task: None,
 2445            debounced_selection_highlight_task: None,
 2446            debounced_selection_highlight_complete: false,
 2447            document_highlights_task: None,
 2448            linked_editing_range_task: None,
 2449            pending_rename: None,
 2450            searchable: !is_minimap,
 2451            cursor_shape: EditorSettings::get_global(cx)
 2452                .cursor_shape
 2453                .unwrap_or_default(),
 2454            cursor_offset_on_selection: false,
 2455            current_line_highlight: None,
 2456            autoindent_mode: Some(AutoindentMode::EachLine),
 2457            collapse_matches: false,
 2458            workspace: None,
 2459            input_enabled: !is_minimap,
 2460            expects_character_input: !is_minimap,
 2461            use_modal_editing: full_mode,
 2462            read_only: is_minimap,
 2463            use_autoclose: true,
 2464            use_auto_surround: true,
 2465            auto_replace_emoji_shortcode: false,
 2466            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2467            leader_id: None,
 2468            remote_id: None,
 2469            hover_state: HoverState::default(),
 2470            pending_mouse_down: None,
 2471            prev_pressure_stage: None,
 2472            hovered_link_state: None,
 2473            edit_prediction_provider: None,
 2474            active_edit_prediction: None,
 2475            stale_edit_prediction_in_menu: None,
 2476            edit_prediction_preview: EditPredictionPreview::Inactive {
 2477                released_too_fast: false,
 2478            },
 2479            inline_diagnostics_enabled: full_mode,
 2480            diagnostics_enabled: full_mode,
 2481            word_completions_enabled: full_mode,
 2482            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2483            gutter_hovered: false,
 2484            pixel_position_of_newest_cursor: None,
 2485            last_bounds: None,
 2486            last_position_map: None,
 2487            expect_bounds_change: None,
 2488            gutter_dimensions: GutterDimensions::default(),
 2489            style: None,
 2490            show_cursor_names: false,
 2491            hovered_cursors: HashMap::default(),
 2492            next_editor_action_id: EditorActionId::default(),
 2493            editor_actions: Rc::default(),
 2494            edit_predictions_hidden_for_vim_mode: false,
 2495            show_edit_predictions_override: None,
 2496            show_completions_on_input_override: None,
 2497            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2498            edit_prediction_settings: EditPredictionSettings::Disabled,
 2499            in_leading_whitespace: false,
 2500            custom_context_menu: None,
 2501            show_git_blame_gutter: false,
 2502            show_git_blame_inline: false,
 2503            show_selection_menu: None,
 2504            show_git_blame_inline_delay_task: None,
 2505            git_blame_inline_enabled: full_mode
 2506                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2507            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2508            buffer_serialization: is_minimap.not().then(|| {
 2509                BufferSerialization::new(
 2510                    ProjectSettings::get_global(cx)
 2511                        .session
 2512                        .restore_unsaved_buffers,
 2513                )
 2514            }),
 2515            blame: None,
 2516            blame_subscription: None,
 2517
 2518            breakpoint_store,
 2519            gutter_breakpoint_indicator: (None, None),
 2520            gutter_diff_review_indicator: (None, None),
 2521            diff_review_drag_state: None,
 2522            diff_review_overlays: Vec::new(),
 2523            stored_review_comments: Vec::new(),
 2524            next_review_comment_id: 0,
 2525            hovered_diff_hunk_row: None,
 2526            _subscriptions: (!is_minimap)
 2527                .then(|| {
 2528                    vec![
 2529                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2530                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2531                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2532                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2533                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2534                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2535                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2536                        cx.observe_window_activation(window, |editor, window, cx| {
 2537                            let active = window.is_window_active();
 2538                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2539                                if active {
 2540                                    blink_manager.enable(cx);
 2541                                } else {
 2542                                    blink_manager.disable(cx);
 2543                                }
 2544                            });
 2545                            if active {
 2546                                editor.show_mouse_cursor(cx);
 2547                            }
 2548                        }),
 2549                    ]
 2550                })
 2551                .unwrap_or_default(),
 2552            runnables: RunnableData::new(),
 2553            pull_diagnostics_task: Task::ready(()),
 2554            colors: None,
 2555            refresh_colors_task: Task::ready(()),
 2556            use_document_folding_ranges: false,
 2557            refresh_folding_ranges_task: Task::ready(()),
 2558            inlay_hints: None,
 2559            next_color_inlay_id: 0,
 2560            post_scroll_update: Task::ready(()),
 2561            linked_edit_ranges: Default::default(),
 2562            in_project_search: false,
 2563            previous_search_ranges: None,
 2564            breadcrumb_header: None,
 2565            focused_block: None,
 2566            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2567            addons: HashMap::default(),
 2568            registered_buffers: HashMap::default(),
 2569            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2570            selection_mark_mode: false,
 2571            toggle_fold_multiple_buffers: Task::ready(()),
 2572            serialize_selections: Task::ready(()),
 2573            serialize_folds: Task::ready(()),
 2574            text_style_refinement: None,
 2575            load_diff_task: load_uncommitted_diff,
 2576            temporary_diff_override: false,
 2577            mouse_cursor_hidden: false,
 2578            minimap: None,
 2579            hide_mouse_mode: EditorSettings::get_global(cx)
 2580                .hide_mouse
 2581                .unwrap_or_default(),
 2582            change_list: ChangeList::new(),
 2583            mode,
 2584            selection_drag_state: SelectionDragState::None,
 2585            folding_newlines: Task::ready(()),
 2586            lookup_key: None,
 2587            select_next_is_case_sensitive: None,
 2588            on_local_selections_changed: None,
 2589            suppress_selection_callback: false,
 2590            applicable_language_settings: HashMap::default(),
 2591            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2592            accent_data: None,
 2593            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2594            number_deleted_lines: false,
 2595            refresh_matching_bracket_highlights_task: Task::ready(()),
 2596            refresh_document_symbols_task: Task::ready(()).shared(),
 2597            lsp_document_symbols: HashMap::default(),
 2598            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2599            outline_symbols_at_cursor: None,
 2600            sticky_headers_task: Task::ready(()),
 2601            sticky_headers: None,
 2602            colorize_brackets_task: Task::ready(()),
 2603        };
 2604
 2605        if is_minimap {
 2606            return editor;
 2607        }
 2608
 2609        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2610        editor.accent_data = editor.fetch_accent_data(cx);
 2611
 2612        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2613            editor
 2614                ._subscriptions
 2615                .push(cx.observe(breakpoints, |_, _, cx| {
 2616                    cx.notify();
 2617                }));
 2618        }
 2619        editor._subscriptions.extend(project_subscriptions);
 2620
 2621        editor._subscriptions.push(cx.subscribe_in(
 2622            &cx.entity(),
 2623            window,
 2624            |editor, _, e: &EditorEvent, window, cx| match e {
 2625                EditorEvent::ScrollPositionChanged { local, .. } => {
 2626                    if *local {
 2627                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2628                        editor.inline_blame_popover.take();
 2629                        let snapshot = editor.snapshot(window, cx);
 2630                        let new_anchor = editor
 2631                            .scroll_manager
 2632                            .native_anchor(&snapshot.display_snapshot, cx);
 2633                        editor.update_restoration_data(cx, move |data| {
 2634                            data.scroll_position = (
 2635                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2636                                new_anchor.offset,
 2637                            );
 2638                        });
 2639
 2640                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2641                            cx.background_executor()
 2642                                .timer(Duration::from_millis(50))
 2643                                .await;
 2644                            editor
 2645                                .update_in(cx, |editor, window, cx| {
 2646                                    editor.update_data_on_scroll(window, cx)
 2647                                })
 2648                                .ok();
 2649                        });
 2650                    }
 2651                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2652                }
 2653                EditorEvent::Edited { .. } => {
 2654                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2655                        .map(|vim_mode| vim_mode.0)
 2656                        .unwrap_or(false);
 2657                    if !vim_mode {
 2658                        let display_map = editor.display_snapshot(cx);
 2659                        let selections = editor.selections.all_adjusted_display(&display_map);
 2660                        let pop_state = editor
 2661                            .change_list
 2662                            .last()
 2663                            .map(|previous| {
 2664                                previous.len() == selections.len()
 2665                                    && previous.iter().enumerate().all(|(ix, p)| {
 2666                                        p.to_display_point(&display_map).row()
 2667                                            == selections[ix].head().row()
 2668                                    })
 2669                            })
 2670                            .unwrap_or(false);
 2671                        let new_positions = selections
 2672                            .into_iter()
 2673                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2674                            .collect();
 2675                        editor
 2676                            .change_list
 2677                            .push_to_change_list(pop_state, new_positions);
 2678                    }
 2679                }
 2680                _ => (),
 2681            },
 2682        ));
 2683
 2684        if let Some(dap_store) = editor
 2685            .project
 2686            .as_ref()
 2687            .map(|project| project.read(cx).dap_store())
 2688        {
 2689            let weak_editor = cx.weak_entity();
 2690
 2691            editor
 2692                ._subscriptions
 2693                .push(
 2694                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2695                        let session_entity = cx.entity();
 2696                        weak_editor
 2697                            .update(cx, |editor, cx| {
 2698                                editor._subscriptions.push(
 2699                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2700                                );
 2701                            })
 2702                            .ok();
 2703                    }),
 2704                );
 2705
 2706            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2707                editor
 2708                    ._subscriptions
 2709                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2710            }
 2711        }
 2712
 2713        // skip adding the initial selection to selection history
 2714        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2715        editor.end_selection(window, cx);
 2716        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2717
 2718        editor.scroll_manager.show_scrollbars(window, cx);
 2719        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2720
 2721        if full_mode {
 2722            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2723            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2724
 2725            if editor.git_blame_inline_enabled {
 2726                editor.start_git_blame_inline(false, window, cx);
 2727            }
 2728
 2729            editor.go_to_active_debug_line(window, cx);
 2730
 2731            editor.minimap =
 2732                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2733            editor.colors = Some(LspColorData::new(cx));
 2734            editor.use_document_folding_ranges = true;
 2735            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2736
 2737            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2738                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2739            }
 2740            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2741        }
 2742
 2743        editor
 2744    }
 2745
 2746    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2747        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2748    }
 2749
 2750    pub fn deploy_mouse_context_menu(
 2751        &mut self,
 2752        position: gpui::Point<Pixels>,
 2753        context_menu: Entity<ContextMenu>,
 2754        window: &mut Window,
 2755        cx: &mut Context<Self>,
 2756    ) {
 2757        self.mouse_context_menu = Some(MouseContextMenu::new(
 2758            self,
 2759            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2760            context_menu,
 2761            window,
 2762            cx,
 2763        ));
 2764    }
 2765
 2766    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2767        self.mouse_context_menu
 2768            .as_ref()
 2769            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2770    }
 2771
 2772    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2773        if self
 2774            .selections
 2775            .pending_anchor()
 2776            .is_some_and(|pending_selection| {
 2777                let snapshot = self.buffer().read(cx).snapshot(cx);
 2778                pending_selection.range().includes(range, &snapshot)
 2779            })
 2780        {
 2781            return true;
 2782        }
 2783
 2784        self.selections
 2785            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2786            .into_iter()
 2787            .any(|selection| {
 2788                // This is needed to cover a corner case, if we just check for an existing
 2789                // selection in the fold range, having a cursor at the start of the fold
 2790                // marks it as selected. Non-empty selections don't cause this.
 2791                let length = selection.end - selection.start;
 2792                length > 0
 2793            })
 2794    }
 2795
 2796    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2797        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2798    }
 2799
 2800    fn key_context_internal(
 2801        &self,
 2802        has_active_edit_prediction: bool,
 2803        window: &mut Window,
 2804        cx: &mut App,
 2805    ) -> KeyContext {
 2806        let mut key_context = KeyContext::new_with_defaults();
 2807        key_context.add("Editor");
 2808        let mode = match self.mode {
 2809            EditorMode::SingleLine => "single_line",
 2810            EditorMode::AutoHeight { .. } => "auto_height",
 2811            EditorMode::Minimap { .. } => "minimap",
 2812            EditorMode::Full { .. } => "full",
 2813        };
 2814
 2815        if EditorSettings::jupyter_enabled(cx) {
 2816            key_context.add("jupyter");
 2817        }
 2818
 2819        key_context.set("mode", mode);
 2820        if self.pending_rename.is_some() {
 2821            key_context.add("renaming");
 2822        }
 2823
 2824        if let Some(snippet_stack) = self.snippet_stack.last() {
 2825            key_context.add("in_snippet");
 2826
 2827            if snippet_stack.active_index > 0 {
 2828                key_context.add("has_previous_tabstop");
 2829            }
 2830
 2831            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2832                key_context.add("has_next_tabstop");
 2833            }
 2834        }
 2835
 2836        match self.context_menu.borrow().as_ref() {
 2837            Some(CodeContextMenu::Completions(menu)) => {
 2838                if menu.visible() {
 2839                    key_context.add("menu");
 2840                    key_context.add("showing_completions");
 2841                }
 2842            }
 2843            Some(CodeContextMenu::CodeActions(menu)) => {
 2844                if menu.visible() {
 2845                    key_context.add("menu");
 2846                    key_context.add("showing_code_actions")
 2847                }
 2848            }
 2849            None => {}
 2850        }
 2851
 2852        if self.signature_help_state.has_multiple_signatures() {
 2853            key_context.add("showing_signature_help");
 2854        }
 2855
 2856        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2857        if !self.focus_handle(cx).contains_focused(window, cx)
 2858            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2859        {
 2860            for addon in self.addons.values() {
 2861                addon.extend_key_context(&mut key_context, cx)
 2862            }
 2863        }
 2864
 2865        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2866            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2867                Some(
 2868                    file.full_path(cx)
 2869                        .extension()?
 2870                        .to_string_lossy()
 2871                        .to_lowercase(),
 2872                )
 2873            }) {
 2874                key_context.set("extension", extension);
 2875            }
 2876        } else {
 2877            key_context.add("multibuffer");
 2878        }
 2879
 2880        if has_active_edit_prediction {
 2881            key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2882            key_context.add("copilot_suggestion");
 2883        }
 2884
 2885        if self.in_leading_whitespace {
 2886            key_context.add("in_leading_whitespace");
 2887        }
 2888
 2889        if self.selection_mark_mode {
 2890            key_context.add("selection_mode");
 2891        }
 2892
 2893        let disjoint = self.selections.disjoint_anchors();
 2894        let snapshot = self.snapshot(window, cx);
 2895        let snapshot = snapshot.buffer_snapshot();
 2896        if self.mode == EditorMode::SingleLine
 2897            && let [selection] = disjoint
 2898            && selection.start == selection.end
 2899            && selection.end.to_offset(snapshot) == snapshot.len()
 2900        {
 2901            key_context.add("end_of_input");
 2902        }
 2903
 2904        if self.has_any_expanded_diff_hunks(cx) {
 2905            key_context.add("diffs_expanded");
 2906        }
 2907
 2908        key_context
 2909    }
 2910
 2911    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2912        self.last_bounds.as_ref()
 2913    }
 2914
 2915    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2916        if self.mouse_cursor_hidden {
 2917            self.mouse_cursor_hidden = false;
 2918            cx.notify();
 2919        }
 2920    }
 2921
 2922    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2923        let hide_mouse_cursor = match origin {
 2924            HideMouseCursorOrigin::TypingAction => {
 2925                matches!(
 2926                    self.hide_mouse_mode,
 2927                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2928                )
 2929            }
 2930            HideMouseCursorOrigin::MovementAction => {
 2931                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2932            }
 2933        };
 2934        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2935            self.mouse_cursor_hidden = hide_mouse_cursor;
 2936            cx.notify();
 2937        }
 2938    }
 2939
 2940    fn accept_edit_prediction_keystroke(
 2941        &self,
 2942        granularity: EditPredictionGranularity,
 2943        window: &mut Window,
 2944        cx: &mut App,
 2945    ) -> Option<gpui::KeybindingKeystroke> {
 2946        let key_context = self.key_context_internal(self.has_active_edit_prediction(), window, cx);
 2947
 2948        let bindings =
 2949            match granularity {
 2950                EditPredictionGranularity::Word => window
 2951                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2952                EditPredictionGranularity::Line => window
 2953                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2954                EditPredictionGranularity::Full => {
 2955                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2956                }
 2957            };
 2958
 2959        bindings
 2960            .into_iter()
 2961            .rev()
 2962            .find_map(|binding| match binding.keystrokes() {
 2963                [keystroke, ..] => Some(keystroke.clone()),
 2964                _ => None,
 2965            })
 2966    }
 2967
 2968    fn preview_edit_prediction_keystroke(
 2969        &self,
 2970        window: &mut Window,
 2971        cx: &mut App,
 2972    ) -> Option<gpui::KeybindingKeystroke> {
 2973        let key_context = self.key_context_internal(self.has_active_edit_prediction(), window, cx);
 2974        let bindings = window.bindings_for_action_in_context(&AcceptEditPrediction, key_context);
 2975        bindings
 2976            .into_iter()
 2977            .rev()
 2978            .find_map(|binding| match binding.keystrokes() {
 2979                [keystroke, ..] if keystroke.modifiers().modified() => Some(keystroke.clone()),
 2980                _ => None,
 2981            })
 2982    }
 2983
 2984    fn edit_prediction_cursor_popover_prefers_preview(
 2985        &self,
 2986        completion: &EditPredictionState,
 2987    ) -> bool {
 2988        match &completion.completion {
 2989            EditPrediction::Edit {
 2990                edits, snapshot, ..
 2991            } => {
 2992                let mut start_row: Option<u32> = None;
 2993                let mut end_row: Option<u32> = None;
 2994
 2995                for (range, text) in edits {
 2996                    let edit_start_row = range.start.text_anchor.to_point(snapshot).row;
 2997                    let old_end_row = range.end.text_anchor.to_point(snapshot).row;
 2998                    let inserted_newline_count = text
 2999                        .as_ref()
 3000                        .chars()
 3001                        .filter(|character| *character == '\n')
 3002                        .count() as u32;
 3003                    let deleted_newline_count = old_end_row - edit_start_row;
 3004                    let preview_end_row = edit_start_row + inserted_newline_count;
 3005
 3006                    start_row =
 3007                        Some(start_row.map_or(edit_start_row, |row| row.min(edit_start_row)));
 3008                    end_row = Some(end_row.map_or(preview_end_row, |row| row.max(preview_end_row)));
 3009
 3010                    if deleted_newline_count > 1 {
 3011                        end_row = Some(end_row.map_or(old_end_row, |row| row.max(old_end_row)));
 3012                    }
 3013                }
 3014
 3015                start_row
 3016                    .zip(end_row)
 3017                    .is_some_and(|(start_row, end_row)| end_row > start_row)
 3018            }
 3019            EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => false,
 3020        }
 3021    }
 3022
 3023    fn edit_prediction_keybind_display(
 3024        &self,
 3025        surface: EditPredictionKeybindSurface,
 3026        window: &mut Window,
 3027        cx: &mut App,
 3028    ) -> EditPredictionKeybindDisplay {
 3029        let accept_keystroke =
 3030            self.accept_edit_prediction_keystroke(EditPredictionGranularity::Full, window, cx);
 3031        let preview_keystroke = self.preview_edit_prediction_keystroke(window, cx);
 3032
 3033        let action = match surface {
 3034            EditPredictionKeybindSurface::Inline
 3035            | EditPredictionKeybindSurface::CursorPopoverCompact => {
 3036                if self.edit_prediction_requires_modifier() {
 3037                    EditPredictionKeybindAction::Preview
 3038                } else {
 3039                    EditPredictionKeybindAction::Accept
 3040                }
 3041            }
 3042            EditPredictionKeybindSurface::CursorPopoverExpanded => self
 3043                .active_edit_prediction
 3044                .as_ref()
 3045                .filter(|completion| {
 3046                    self.edit_prediction_cursor_popover_prefers_preview(completion)
 3047                })
 3048                .map_or(EditPredictionKeybindAction::Accept, |_| {
 3049                    EditPredictionKeybindAction::Preview
 3050                }),
 3051        };
 3052        #[cfg(test)]
 3053        let preview_copy = preview_keystroke.clone();
 3054        #[cfg(test)]
 3055        let accept_copy = accept_keystroke.clone();
 3056
 3057        let displayed_keystroke = match surface {
 3058            EditPredictionKeybindSurface::Inline => match action {
 3059                EditPredictionKeybindAction::Accept => accept_keystroke,
 3060                EditPredictionKeybindAction::Preview => preview_keystroke,
 3061            },
 3062            EditPredictionKeybindSurface::CursorPopoverCompact
 3063            | EditPredictionKeybindSurface::CursorPopoverExpanded => match action {
 3064                EditPredictionKeybindAction::Accept => accept_keystroke,
 3065                EditPredictionKeybindAction::Preview => {
 3066                    preview_keystroke.or_else(|| accept_keystroke.clone())
 3067                }
 3068            },
 3069        };
 3070
 3071        let missing_accept_keystroke = displayed_keystroke.is_none();
 3072
 3073        EditPredictionKeybindDisplay {
 3074            #[cfg(test)]
 3075            accept_keystroke: accept_copy,
 3076            #[cfg(test)]
 3077            preview_keystroke: preview_copy,
 3078            displayed_keystroke,
 3079            action,
 3080            missing_accept_keystroke,
 3081            show_hold_label: matches!(surface, EditPredictionKeybindSurface::CursorPopoverCompact)
 3082                && self.edit_prediction_preview.released_too_fast(),
 3083        }
 3084    }
 3085
 3086    pub fn new_file(
 3087        workspace: &mut Workspace,
 3088        _: &workspace::NewFile,
 3089        window: &mut Window,
 3090        cx: &mut Context<Workspace>,
 3091    ) {
 3092        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3093            "Failed to create buffer",
 3094            window,
 3095            cx,
 3096            |e, _, _| match e.error_code() {
 3097                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3098                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3099                e.error_tag("required").unwrap_or("the latest version")
 3100            )),
 3101                _ => None,
 3102            },
 3103        );
 3104    }
 3105
 3106    pub fn new_in_workspace(
 3107        workspace: &mut Workspace,
 3108        window: &mut Window,
 3109        cx: &mut Context<Workspace>,
 3110    ) -> Task<Result<Entity<Editor>>> {
 3111        let project = workspace.project().clone();
 3112        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3113
 3114        cx.spawn_in(window, async move |workspace, cx| {
 3115            let buffer = create.await?;
 3116            workspace.update_in(cx, |workspace, window, cx| {
 3117                let editor =
 3118                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3119                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3120                editor
 3121            })
 3122        })
 3123    }
 3124
 3125    fn new_file_vertical(
 3126        workspace: &mut Workspace,
 3127        _: &workspace::NewFileSplitVertical,
 3128        window: &mut Window,
 3129        cx: &mut Context<Workspace>,
 3130    ) {
 3131        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3132    }
 3133
 3134    fn new_file_horizontal(
 3135        workspace: &mut Workspace,
 3136        _: &workspace::NewFileSplitHorizontal,
 3137        window: &mut Window,
 3138        cx: &mut Context<Workspace>,
 3139    ) {
 3140        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3141    }
 3142
 3143    fn new_file_split(
 3144        workspace: &mut Workspace,
 3145        action: &workspace::NewFileSplit,
 3146        window: &mut Window,
 3147        cx: &mut Context<Workspace>,
 3148    ) {
 3149        Self::new_file_in_direction(workspace, action.0, window, cx)
 3150    }
 3151
 3152    fn new_file_in_direction(
 3153        workspace: &mut Workspace,
 3154        direction: SplitDirection,
 3155        window: &mut Window,
 3156        cx: &mut Context<Workspace>,
 3157    ) {
 3158        let project = workspace.project().clone();
 3159        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3160
 3161        cx.spawn_in(window, async move |workspace, cx| {
 3162            let buffer = create.await?;
 3163            workspace.update_in(cx, move |workspace, window, cx| {
 3164                workspace.split_item(
 3165                    direction,
 3166                    Box::new(
 3167                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3168                    ),
 3169                    window,
 3170                    cx,
 3171                )
 3172            })?;
 3173            anyhow::Ok(())
 3174        })
 3175        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3176            match e.error_code() {
 3177                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3178                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3179                e.error_tag("required").unwrap_or("the latest version")
 3180            )),
 3181                _ => None,
 3182            }
 3183        });
 3184    }
 3185
 3186    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3187        self.leader_id
 3188    }
 3189
 3190    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3191        &self.buffer
 3192    }
 3193
 3194    pub fn project(&self) -> Option<&Entity<Project>> {
 3195        self.project.as_ref()
 3196    }
 3197
 3198    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3199        self.workspace.as_ref()?.0.upgrade()
 3200    }
 3201
 3202    /// Detaches a task and shows an error notification in the workspace if available,
 3203    /// otherwise just logs the error.
 3204    pub fn detach_and_notify_err<R, E>(
 3205        &self,
 3206        task: Task<Result<R, E>>,
 3207        window: &mut Window,
 3208        cx: &mut App,
 3209    ) where
 3210        E: std::fmt::Debug + std::fmt::Display + 'static,
 3211        R: 'static,
 3212    {
 3213        if let Some(workspace) = self.workspace() {
 3214            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3215        } else {
 3216            task.detach_and_log_err(cx);
 3217        }
 3218    }
 3219
 3220    /// Returns the workspace serialization ID if this editor should be serialized.
 3221    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3222        self.workspace
 3223            .as_ref()
 3224            .filter(|_| self.should_serialize_buffer())
 3225            .and_then(|workspace| workspace.1)
 3226    }
 3227
 3228    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3229        self.buffer().read(cx).title(cx)
 3230    }
 3231
 3232    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3233        let git_blame_gutter_max_author_length = self
 3234            .render_git_blame_gutter(cx)
 3235            .then(|| {
 3236                if let Some(blame) = self.blame.as_ref() {
 3237                    let max_author_length =
 3238                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3239                    Some(max_author_length)
 3240                } else {
 3241                    None
 3242                }
 3243            })
 3244            .flatten();
 3245
 3246        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3247
 3248        EditorSnapshot {
 3249            mode: self.mode.clone(),
 3250            show_gutter: self.show_gutter,
 3251            offset_content: self.offset_content,
 3252            show_line_numbers: self.show_line_numbers,
 3253            number_deleted_lines: self.number_deleted_lines,
 3254            show_git_diff_gutter: self.show_git_diff_gutter,
 3255            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3256            show_code_actions: self.show_code_actions,
 3257            show_runnables: self.show_runnables,
 3258            show_breakpoints: self.show_breakpoints,
 3259            git_blame_gutter_max_author_length,
 3260            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3261            display_snapshot,
 3262            placeholder_display_snapshot: self
 3263                .placeholder_display_map
 3264                .as_ref()
 3265                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3266            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3267            is_focused: self.focus_handle.is_focused(window),
 3268            current_line_highlight: self
 3269                .current_line_highlight
 3270                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3271            gutter_hovered: self.gutter_hovered,
 3272        }
 3273    }
 3274
 3275    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3276        self.buffer.read(cx).language_at(point, cx)
 3277    }
 3278
 3279    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3280        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3281    }
 3282
 3283    pub fn active_excerpt(
 3284        &self,
 3285        cx: &App,
 3286    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3287        self.buffer
 3288            .read(cx)
 3289            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3290    }
 3291
 3292    pub fn mode(&self) -> &EditorMode {
 3293        &self.mode
 3294    }
 3295
 3296    pub fn set_mode(&mut self, mode: EditorMode) {
 3297        self.mode = mode;
 3298    }
 3299
 3300    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3301        self.collaboration_hub.as_deref()
 3302    }
 3303
 3304    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3305        self.collaboration_hub = Some(hub);
 3306    }
 3307
 3308    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3309        self.in_project_search = in_project_search;
 3310    }
 3311
 3312    pub fn set_custom_context_menu(
 3313        &mut self,
 3314        f: impl 'static
 3315        + Fn(
 3316            &mut Self,
 3317            DisplayPoint,
 3318            &mut Window,
 3319            &mut Context<Self>,
 3320        ) -> Option<Entity<ui::ContextMenu>>,
 3321    ) {
 3322        self.custom_context_menu = Some(Box::new(f))
 3323    }
 3324
 3325    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3326        self.completion_provider = provider;
 3327    }
 3328
 3329    #[cfg(any(test, feature = "test-support"))]
 3330    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3331        self.completion_provider.clone()
 3332    }
 3333
 3334    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3335        self.semantics_provider.clone()
 3336    }
 3337
 3338    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3339        self.semantics_provider = provider;
 3340    }
 3341
 3342    pub fn set_edit_prediction_provider<T>(
 3343        &mut self,
 3344        provider: Option<Entity<T>>,
 3345        window: &mut Window,
 3346        cx: &mut Context<Self>,
 3347    ) where
 3348        T: EditPredictionDelegate,
 3349    {
 3350        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3351            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3352                if this.focus_handle.is_focused(window) {
 3353                    this.update_visible_edit_prediction(window, cx);
 3354                }
 3355            }),
 3356            provider: Arc::new(provider),
 3357        });
 3358        self.update_edit_prediction_settings(cx);
 3359        self.refresh_edit_prediction(false, false, window, cx);
 3360    }
 3361
 3362    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3363        self.placeholder_display_map
 3364            .as_ref()
 3365            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3366    }
 3367
 3368    pub fn set_placeholder_text(
 3369        &mut self,
 3370        placeholder_text: &str,
 3371        window: &mut Window,
 3372        cx: &mut Context<Self>,
 3373    ) {
 3374        let multibuffer = cx
 3375            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3376
 3377        let style = window.text_style();
 3378
 3379        self.placeholder_display_map = Some(cx.new(|cx| {
 3380            DisplayMap::new(
 3381                multibuffer,
 3382                style.font(),
 3383                style.font_size.to_pixels(window.rem_size()),
 3384                None,
 3385                FILE_HEADER_HEIGHT,
 3386                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3387                Default::default(),
 3388                DiagnosticSeverity::Off,
 3389                cx,
 3390            )
 3391        }));
 3392        cx.notify();
 3393    }
 3394
 3395    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3396        self.cursor_shape = cursor_shape;
 3397
 3398        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3399        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3400
 3401        cx.notify();
 3402    }
 3403
 3404    pub fn cursor_shape(&self) -> CursorShape {
 3405        self.cursor_shape
 3406    }
 3407
 3408    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3409        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3410    }
 3411
 3412    pub fn set_current_line_highlight(
 3413        &mut self,
 3414        current_line_highlight: Option<CurrentLineHighlight>,
 3415    ) {
 3416        self.current_line_highlight = current_line_highlight;
 3417    }
 3418
 3419    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3420        self.collapse_matches = collapse_matches;
 3421    }
 3422
 3423    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3424        if self.collapse_matches {
 3425            return range.start..range.start;
 3426        }
 3427        range.clone()
 3428    }
 3429
 3430    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3431        self.display_map.read(cx).clip_at_line_ends
 3432    }
 3433
 3434    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3435        if self.display_map.read(cx).clip_at_line_ends != clip {
 3436            self.display_map
 3437                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3438        }
 3439    }
 3440
 3441    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3442        self.input_enabled = input_enabled;
 3443    }
 3444
 3445    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3446        self.expects_character_input = expects_character_input;
 3447    }
 3448
 3449    pub fn set_edit_predictions_hidden_for_vim_mode(
 3450        &mut self,
 3451        hidden: bool,
 3452        window: &mut Window,
 3453        cx: &mut Context<Self>,
 3454    ) {
 3455        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3456            self.edit_predictions_hidden_for_vim_mode = hidden;
 3457            if hidden {
 3458                self.update_visible_edit_prediction(window, cx);
 3459            } else {
 3460                self.refresh_edit_prediction(true, false, window, cx);
 3461            }
 3462        }
 3463    }
 3464
 3465    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3466        self.menu_edit_predictions_policy = value;
 3467    }
 3468
 3469    pub fn set_autoindent(&mut self, autoindent: bool) {
 3470        if autoindent {
 3471            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3472        } else {
 3473            self.autoindent_mode = None;
 3474        }
 3475    }
 3476
 3477    pub fn capability(&self, cx: &App) -> Capability {
 3478        if self.read_only {
 3479            Capability::ReadOnly
 3480        } else {
 3481            self.buffer.read(cx).capability()
 3482        }
 3483    }
 3484
 3485    pub fn read_only(&self, cx: &App) -> bool {
 3486        self.read_only || self.buffer.read(cx).read_only()
 3487    }
 3488
 3489    pub fn set_read_only(&mut self, read_only: bool) {
 3490        self.read_only = read_only;
 3491    }
 3492
 3493    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3494        self.use_autoclose = autoclose;
 3495    }
 3496
 3497    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3498        self.use_auto_surround = auto_surround;
 3499    }
 3500
 3501    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3502        self.auto_replace_emoji_shortcode = auto_replace;
 3503    }
 3504
 3505    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3506        self.buffer_serialization = should_serialize.then(|| {
 3507            BufferSerialization::new(
 3508                ProjectSettings::get_global(cx)
 3509                    .session
 3510                    .restore_unsaved_buffers,
 3511            )
 3512        })
 3513    }
 3514
 3515    fn should_serialize_buffer(&self) -> bool {
 3516        self.buffer_serialization.is_some()
 3517    }
 3518
 3519    pub fn toggle_edit_predictions(
 3520        &mut self,
 3521        _: &ToggleEditPrediction,
 3522        window: &mut Window,
 3523        cx: &mut Context<Self>,
 3524    ) {
 3525        if self.show_edit_predictions_override.is_some() {
 3526            self.set_show_edit_predictions(None, window, cx);
 3527        } else {
 3528            let show_edit_predictions = !self.edit_predictions_enabled();
 3529            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3530        }
 3531    }
 3532
 3533    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3534        self.show_completions_on_input_override = show_completions_on_input;
 3535    }
 3536
 3537    pub fn set_show_edit_predictions(
 3538        &mut self,
 3539        show_edit_predictions: Option<bool>,
 3540        window: &mut Window,
 3541        cx: &mut Context<Self>,
 3542    ) {
 3543        self.show_edit_predictions_override = show_edit_predictions;
 3544        self.update_edit_prediction_settings(cx);
 3545
 3546        if let Some(false) = show_edit_predictions {
 3547            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3548        } else {
 3549            self.refresh_edit_prediction(false, true, window, cx);
 3550        }
 3551    }
 3552
 3553    fn edit_predictions_disabled_in_scope(
 3554        &self,
 3555        buffer: &Entity<Buffer>,
 3556        buffer_position: language::Anchor,
 3557        cx: &App,
 3558    ) -> bool {
 3559        let snapshot = buffer.read(cx).snapshot();
 3560        let settings = snapshot.settings_at(buffer_position, cx);
 3561
 3562        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3563            return false;
 3564        };
 3565
 3566        scope.override_name().is_some_and(|scope_name| {
 3567            settings
 3568                .edit_predictions_disabled_in
 3569                .iter()
 3570                .any(|s| s == scope_name)
 3571        })
 3572    }
 3573
 3574    pub fn set_use_modal_editing(&mut self, to: bool) {
 3575        self.use_modal_editing = to;
 3576    }
 3577
 3578    pub fn use_modal_editing(&self) -> bool {
 3579        self.use_modal_editing
 3580    }
 3581
 3582    fn selections_did_change(
 3583        &mut self,
 3584        local: bool,
 3585        old_cursor_position: &Anchor,
 3586        effects: SelectionEffects,
 3587        window: &mut Window,
 3588        cx: &mut Context<Self>,
 3589    ) {
 3590        window.invalidate_character_coordinates();
 3591
 3592        // Copy selections to primary selection buffer
 3593        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3594        if local {
 3595            let selections = self
 3596                .selections
 3597                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3598            let buffer_handle = self.buffer.read(cx).read(cx);
 3599
 3600            let mut text = String::new();
 3601            for (index, selection) in selections.iter().enumerate() {
 3602                let text_for_selection = buffer_handle
 3603                    .text_for_range(selection.start..selection.end)
 3604                    .collect::<String>();
 3605
 3606                text.push_str(&text_for_selection);
 3607                if index != selections.len() - 1 {
 3608                    text.push('\n');
 3609                }
 3610            }
 3611
 3612            if !text.is_empty() {
 3613                cx.write_to_primary(ClipboardItem::new_string(text));
 3614            }
 3615        }
 3616
 3617        let selection_anchors = self.selections.disjoint_anchors_arc();
 3618
 3619        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3620            self.buffer.update(cx, |buffer, cx| {
 3621                buffer.set_active_selections(
 3622                    &selection_anchors,
 3623                    self.selections.line_mode(),
 3624                    self.cursor_shape,
 3625                    cx,
 3626                )
 3627            });
 3628        }
 3629        let display_map = self
 3630            .display_map
 3631            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3632        let buffer = display_map.buffer_snapshot();
 3633        if self.selections.count() == 1 {
 3634            self.add_selections_state = None;
 3635        }
 3636        self.select_next_state = None;
 3637        self.select_prev_state = None;
 3638        self.select_syntax_node_history.try_clear();
 3639        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3640        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3641        self.take_rename(false, window, cx);
 3642
 3643        let newest_selection = self.selections.newest_anchor();
 3644        let new_cursor_position = newest_selection.head();
 3645        let selection_start = newest_selection.start;
 3646
 3647        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3648            self.push_to_nav_history(
 3649                *old_cursor_position,
 3650                Some(new_cursor_position.to_point(buffer)),
 3651                false,
 3652                effects.nav_history == Some(true),
 3653                cx,
 3654            );
 3655        }
 3656
 3657        if local {
 3658            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3659                self.register_buffer(buffer_id, cx);
 3660            }
 3661
 3662            let mut context_menu = self.context_menu.borrow_mut();
 3663            let completion_menu = match context_menu.as_ref() {
 3664                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3665                Some(CodeContextMenu::CodeActions(_)) => {
 3666                    *context_menu = None;
 3667                    None
 3668                }
 3669                None => None,
 3670            };
 3671            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3672            drop(context_menu);
 3673
 3674            if effects.completions
 3675                && let Some(completion_position) = completion_position
 3676            {
 3677                let start_offset = selection_start.to_offset(buffer);
 3678                let position_matches = start_offset == completion_position.to_offset(buffer);
 3679                let continue_showing = if let Some((snap, ..)) =
 3680                    buffer.point_to_buffer_offset(completion_position)
 3681                    && !snap.capability.editable()
 3682                {
 3683                    false
 3684                } else if position_matches {
 3685                    if self.snippet_stack.is_empty() {
 3686                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3687                            == Some(CharKind::Word)
 3688                    } else {
 3689                        // Snippet choices can be shown even when the cursor is in whitespace.
 3690                        // Dismissing the menu with actions like backspace is handled by
 3691                        // invalidation regions.
 3692                        true
 3693                    }
 3694                } else {
 3695                    false
 3696                };
 3697
 3698                if continue_showing {
 3699                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3700                } else {
 3701                    self.hide_context_menu(window, cx);
 3702                }
 3703            }
 3704
 3705            hide_hover(self, cx);
 3706
 3707            if old_cursor_position.to_display_point(&display_map).row()
 3708                != new_cursor_position.to_display_point(&display_map).row()
 3709            {
 3710                self.available_code_actions.take();
 3711            }
 3712            self.refresh_code_actions(window, cx);
 3713            self.refresh_document_highlights(cx);
 3714            refresh_linked_ranges(self, window, cx);
 3715
 3716            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3717            self.refresh_matching_bracket_highlights(&display_map, cx);
 3718            self.refresh_outline_symbols_at_cursor(cx);
 3719            self.update_visible_edit_prediction(window, cx);
 3720            self.inline_blame_popover.take();
 3721            if self.git_blame_inline_enabled {
 3722                self.start_inline_blame_timer(window, cx);
 3723            }
 3724        }
 3725
 3726        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3727
 3728        if local && !self.suppress_selection_callback {
 3729            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3730                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3731                callback(cursor_position, window, cx);
 3732            }
 3733        }
 3734
 3735        cx.emit(EditorEvent::SelectionsChanged { local });
 3736
 3737        let selections = &self.selections.disjoint_anchors_arc();
 3738        if selections.len() == 1 {
 3739            cx.emit(SearchEvent::ActiveMatchChanged)
 3740        }
 3741        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3742            let inmemory_selections = selections
 3743                .iter()
 3744                .map(|s| {
 3745                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3746                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3747                })
 3748                .collect();
 3749            self.update_restoration_data(cx, |data| {
 3750                data.selections = inmemory_selections;
 3751            });
 3752
 3753            if WorkspaceSettings::get(None, cx).restore_on_startup
 3754                != RestoreOnStartupBehavior::EmptyTab
 3755                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3756            {
 3757                let snapshot = self.buffer().read(cx).snapshot(cx);
 3758                let selections = selections.clone();
 3759                let background_executor = cx.background_executor().clone();
 3760                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3761                let db = EditorDb::global(cx);
 3762                self.serialize_selections = cx.background_spawn(async move {
 3763                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3764                    let db_selections = selections
 3765                        .iter()
 3766                        .map(|selection| {
 3767                            (
 3768                                selection.start.to_offset(&snapshot).0,
 3769                                selection.end.to_offset(&snapshot).0,
 3770                            )
 3771                        })
 3772                        .collect();
 3773
 3774                    db.save_editor_selections(editor_id, workspace_id, db_selections)
 3775                        .await
 3776                        .with_context(|| {
 3777                            format!(
 3778                                "persisting editor selections for editor {editor_id}, \
 3779                                workspace {workspace_id:?}"
 3780                            )
 3781                        })
 3782                        .log_err();
 3783                });
 3784            }
 3785        }
 3786
 3787        cx.notify();
 3788    }
 3789
 3790    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3791        use text::ToOffset as _;
 3792        use text::ToPoint as _;
 3793
 3794        if self.mode.is_minimap()
 3795            || WorkspaceSettings::get(None, cx).restore_on_startup
 3796                == RestoreOnStartupBehavior::EmptyTab
 3797        {
 3798            return;
 3799        }
 3800
 3801        if !self.buffer().read(cx).is_singleton() {
 3802            return;
 3803        }
 3804
 3805        let display_snapshot = self
 3806            .display_map
 3807            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3808        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3809            return;
 3810        };
 3811        let inmemory_folds = display_snapshot
 3812            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3813            .map(|fold| {
 3814                fold.range.start.text_anchor.to_point(&snapshot)
 3815                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3816            })
 3817            .collect();
 3818        self.update_restoration_data(cx, |data| {
 3819            data.folds = inmemory_folds;
 3820        });
 3821
 3822        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3823            return;
 3824        };
 3825
 3826        // Get file path for path-based fold storage (survives tab close)
 3827        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3828            project::File::from_dyn(buffer.read(cx).file())
 3829                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3830        }) else {
 3831            return;
 3832        };
 3833
 3834        let background_executor = cx.background_executor().clone();
 3835        const FINGERPRINT_LEN: usize = 32;
 3836        let db_folds = display_snapshot
 3837            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3838            .map(|fold| {
 3839                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3840                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3841
 3842                // Extract fingerprints - content at fold boundaries for validation on restore
 3843                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3844                // content that might change independently.
 3845                // start_fp: first min(32, fold_len) bytes of fold content
 3846                // end_fp: last min(32, fold_len) bytes of fold content
 3847                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3848                let fold_len = end - start;
 3849                let start_fp_end = snapshot
 3850                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3851                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3852                let end_fp_start = snapshot
 3853                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3854                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3855
 3856                (start, end, start_fp, end_fp)
 3857            })
 3858            .collect::<Vec<_>>();
 3859        let db = EditorDb::global(cx);
 3860        self.serialize_folds = cx.background_spawn(async move {
 3861            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3862            if db_folds.is_empty() {
 3863                // No folds - delete any persisted folds for this file
 3864                db.delete_file_folds(workspace_id, file_path)
 3865                    .await
 3866                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3867                    .log_err();
 3868            } else {
 3869                db.save_file_folds(workspace_id, file_path, db_folds)
 3870                    .await
 3871                    .with_context(|| {
 3872                        format!("persisting file folds for workspace {workspace_id:?}")
 3873                    })
 3874                    .log_err();
 3875            }
 3876        });
 3877    }
 3878
 3879    pub fn sync_selections(
 3880        &mut self,
 3881        other: Entity<Editor>,
 3882        cx: &mut Context<Self>,
 3883    ) -> gpui::Subscription {
 3884        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3885        if !other_selections.is_empty() {
 3886            self.selections
 3887                .change_with(&self.display_snapshot(cx), |selections| {
 3888                    selections.select_anchors(other_selections);
 3889                });
 3890        }
 3891
 3892        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3893            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3894                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3895                if other_selections.is_empty() {
 3896                    return;
 3897                }
 3898                let snapshot = this.display_snapshot(cx);
 3899                this.selections.change_with(&snapshot, |selections| {
 3900                    selections.select_anchors(other_selections);
 3901                });
 3902            }
 3903        });
 3904
 3905        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3906            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3907                let these_selections = this.selections.disjoint_anchors().to_vec();
 3908                if these_selections.is_empty() {
 3909                    return;
 3910                }
 3911                other.update(cx, |other_editor, cx| {
 3912                    let snapshot = other_editor.display_snapshot(cx);
 3913                    other_editor
 3914                        .selections
 3915                        .change_with(&snapshot, |selections| {
 3916                            selections.select_anchors(these_selections);
 3917                        })
 3918                });
 3919            }
 3920        });
 3921
 3922        Subscription::join(other_subscription, this_subscription)
 3923    }
 3924
 3925    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3926        if self.buffer().read(cx).is_singleton() {
 3927            return;
 3928        }
 3929        let snapshot = self.buffer.read(cx).snapshot(cx);
 3930        let buffer_ids: HashSet<BufferId> = self
 3931            .selections
 3932            .disjoint_anchor_ranges()
 3933            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3934            .collect();
 3935        for buffer_id in buffer_ids {
 3936            self.unfold_buffer(buffer_id, cx);
 3937        }
 3938    }
 3939
 3940    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3941    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3942    /// effects of selection change occur at the end of the transaction.
 3943    pub fn change_selections<R>(
 3944        &mut self,
 3945        effects: SelectionEffects,
 3946        window: &mut Window,
 3947        cx: &mut Context<Self>,
 3948        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3949    ) -> R {
 3950        let snapshot = self.display_snapshot(cx);
 3951        if let Some(state) = &mut self.deferred_selection_effects_state {
 3952            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3953            state.effects.completions = effects.completions;
 3954            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3955            let (changed, result) = self.selections.change_with(&snapshot, change);
 3956            state.changed |= changed;
 3957            return result;
 3958        }
 3959        let mut state = DeferredSelectionEffectsState {
 3960            changed: false,
 3961            effects,
 3962            old_cursor_position: self.selections.newest_anchor().head(),
 3963            history_entry: SelectionHistoryEntry {
 3964                selections: self.selections.disjoint_anchors_arc(),
 3965                select_next_state: self.select_next_state.clone(),
 3966                select_prev_state: self.select_prev_state.clone(),
 3967                add_selections_state: self.add_selections_state.clone(),
 3968            },
 3969        };
 3970        let (changed, result) = self.selections.change_with(&snapshot, change);
 3971        state.changed = state.changed || changed;
 3972        if self.defer_selection_effects {
 3973            self.deferred_selection_effects_state = Some(state);
 3974        } else {
 3975            self.apply_selection_effects(state, window, cx);
 3976        }
 3977        result
 3978    }
 3979
 3980    /// Defers the effects of selection change, so that the effects of multiple calls to
 3981    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3982    /// to selection history and the state of popovers based on selection position aren't
 3983    /// erroneously updated.
 3984    pub fn with_selection_effects_deferred<R>(
 3985        &mut self,
 3986        window: &mut Window,
 3987        cx: &mut Context<Self>,
 3988        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3989    ) -> R {
 3990        let already_deferred = self.defer_selection_effects;
 3991        self.defer_selection_effects = true;
 3992        let result = update(self, window, cx);
 3993        if !already_deferred {
 3994            self.defer_selection_effects = false;
 3995            if let Some(state) = self.deferred_selection_effects_state.take() {
 3996                self.apply_selection_effects(state, window, cx);
 3997            }
 3998        }
 3999        result
 4000    }
 4001
 4002    fn apply_selection_effects(
 4003        &mut self,
 4004        state: DeferredSelectionEffectsState,
 4005        window: &mut Window,
 4006        cx: &mut Context<Self>,
 4007    ) {
 4008        if state.changed {
 4009            self.selection_history.push(state.history_entry);
 4010
 4011            if let Some(autoscroll) = state.effects.scroll {
 4012                self.request_autoscroll(autoscroll, cx);
 4013            }
 4014
 4015            let old_cursor_position = &state.old_cursor_position;
 4016
 4017            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 4018
 4019            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 4020                self.show_signature_help_auto(window, cx);
 4021            }
 4022        }
 4023    }
 4024
 4025    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4026    where
 4027        I: IntoIterator<Item = (Range<S>, T)>,
 4028        S: ToOffset,
 4029        T: Into<Arc<str>>,
 4030    {
 4031        if self.read_only(cx) {
 4032            return;
 4033        }
 4034
 4035        self.buffer
 4036            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 4037    }
 4038
 4039    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4040    where
 4041        I: IntoIterator<Item = (Range<S>, T)>,
 4042        S: ToOffset,
 4043        T: Into<Arc<str>>,
 4044    {
 4045        if self.read_only(cx) {
 4046            return;
 4047        }
 4048
 4049        self.buffer.update(cx, |buffer, cx| {
 4050            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 4051        });
 4052    }
 4053
 4054    pub fn edit_with_block_indent<I, S, T>(
 4055        &mut self,
 4056        edits: I,
 4057        original_indent_columns: Vec<Option<u32>>,
 4058        cx: &mut Context<Self>,
 4059    ) where
 4060        I: IntoIterator<Item = (Range<S>, T)>,
 4061        S: ToOffset,
 4062        T: Into<Arc<str>>,
 4063    {
 4064        if self.read_only(cx) {
 4065            return;
 4066        }
 4067
 4068        self.buffer.update(cx, |buffer, cx| {
 4069            buffer.edit(
 4070                edits,
 4071                Some(AutoindentMode::Block {
 4072                    original_indent_columns,
 4073                }),
 4074                cx,
 4075            )
 4076        });
 4077    }
 4078
 4079    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 4080        self.hide_context_menu(window, cx);
 4081
 4082        match phase {
 4083            SelectPhase::Begin {
 4084                position,
 4085                add,
 4086                click_count,
 4087            } => self.begin_selection(position, add, click_count, window, cx),
 4088            SelectPhase::BeginColumnar {
 4089                position,
 4090                goal_column,
 4091                reset,
 4092                mode,
 4093            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4094            SelectPhase::Extend {
 4095                position,
 4096                click_count,
 4097            } => self.extend_selection(position, click_count, window, cx),
 4098            SelectPhase::Update {
 4099                position,
 4100                goal_column,
 4101                scroll_delta,
 4102            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4103            SelectPhase::End => self.end_selection(window, cx),
 4104        }
 4105    }
 4106
 4107    fn extend_selection(
 4108        &mut self,
 4109        position: DisplayPoint,
 4110        click_count: usize,
 4111        window: &mut Window,
 4112        cx: &mut Context<Self>,
 4113    ) {
 4114        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4115        let tail = self
 4116            .selections
 4117            .newest::<MultiBufferOffset>(&display_map)
 4118            .tail();
 4119        let click_count = click_count.max(match self.selections.select_mode() {
 4120            SelectMode::Character => 1,
 4121            SelectMode::Word(_) => 2,
 4122            SelectMode::Line(_) => 3,
 4123            SelectMode::All => 4,
 4124        });
 4125        self.begin_selection(position, false, click_count, window, cx);
 4126
 4127        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4128
 4129        let current_selection = match self.selections.select_mode() {
 4130            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4131            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4132        };
 4133
 4134        let mut pending_selection = self
 4135            .selections
 4136            .pending_anchor()
 4137            .cloned()
 4138            .expect("extend_selection not called with pending selection");
 4139
 4140        if pending_selection
 4141            .start
 4142            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4143            == Ordering::Greater
 4144        {
 4145            pending_selection.start = current_selection.start;
 4146        }
 4147        if pending_selection
 4148            .end
 4149            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4150            == Ordering::Less
 4151        {
 4152            pending_selection.end = current_selection.end;
 4153            pending_selection.reversed = true;
 4154        }
 4155
 4156        let mut pending_mode = self.selections.pending_mode().unwrap();
 4157        match &mut pending_mode {
 4158            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4159            _ => {}
 4160        }
 4161
 4162        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4163            SelectionEffects::scroll(Autoscroll::fit())
 4164        } else {
 4165            SelectionEffects::no_scroll()
 4166        };
 4167
 4168        self.change_selections(effects, window, cx, |s| {
 4169            s.set_pending(pending_selection.clone(), pending_mode);
 4170            s.set_is_extending(true);
 4171        });
 4172    }
 4173
 4174    fn begin_selection(
 4175        &mut self,
 4176        position: DisplayPoint,
 4177        add: bool,
 4178        click_count: usize,
 4179        window: &mut Window,
 4180        cx: &mut Context<Self>,
 4181    ) {
 4182        if !self.focus_handle.is_focused(window) {
 4183            self.last_focused_descendant = None;
 4184            window.focus(&self.focus_handle, cx);
 4185        }
 4186
 4187        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4188        let buffer = display_map.buffer_snapshot();
 4189        let position = display_map.clip_point(position, Bias::Left);
 4190
 4191        let start;
 4192        let end;
 4193        let mode;
 4194        let mut auto_scroll;
 4195        match click_count {
 4196            1 => {
 4197                start = buffer.anchor_before(position.to_point(&display_map));
 4198                end = start;
 4199                mode = SelectMode::Character;
 4200                auto_scroll = true;
 4201            }
 4202            2 => {
 4203                let position = display_map
 4204                    .clip_point(position, Bias::Left)
 4205                    .to_offset(&display_map, Bias::Left);
 4206                let (range, _) = buffer.surrounding_word(position, None);
 4207                start = buffer.anchor_before(range.start);
 4208                end = buffer.anchor_before(range.end);
 4209                mode = SelectMode::Word(start..end);
 4210                auto_scroll = true;
 4211            }
 4212            3 => {
 4213                let position = display_map
 4214                    .clip_point(position, Bias::Left)
 4215                    .to_point(&display_map);
 4216                let line_start = display_map.prev_line_boundary(position).0;
 4217                let next_line_start = buffer.clip_point(
 4218                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4219                    Bias::Left,
 4220                );
 4221                start = buffer.anchor_before(line_start);
 4222                end = buffer.anchor_before(next_line_start);
 4223                mode = SelectMode::Line(start..end);
 4224                auto_scroll = true;
 4225            }
 4226            _ => {
 4227                start = buffer.anchor_before(MultiBufferOffset(0));
 4228                end = buffer.anchor_before(buffer.len());
 4229                mode = SelectMode::All;
 4230                auto_scroll = false;
 4231            }
 4232        }
 4233        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4234
 4235        let point_to_delete: Option<usize> = {
 4236            let selected_points: Vec<Selection<Point>> =
 4237                self.selections.disjoint_in_range(start..end, &display_map);
 4238
 4239            if !add || click_count > 1 {
 4240                None
 4241            } else if !selected_points.is_empty() {
 4242                Some(selected_points[0].id)
 4243            } else {
 4244                let clicked_point_already_selected =
 4245                    self.selections.disjoint_anchors().iter().find(|selection| {
 4246                        selection.start.to_point(buffer) == start.to_point(buffer)
 4247                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4248                    });
 4249
 4250                clicked_point_already_selected.map(|selection| selection.id)
 4251            }
 4252        };
 4253
 4254        let selections_count = self.selections.count();
 4255        let effects = if auto_scroll {
 4256            SelectionEffects::default()
 4257        } else {
 4258            SelectionEffects::no_scroll()
 4259        };
 4260
 4261        self.change_selections(effects, window, cx, |s| {
 4262            if let Some(point_to_delete) = point_to_delete {
 4263                s.delete(point_to_delete);
 4264
 4265                if selections_count == 1 {
 4266                    s.set_pending_anchor_range(start..end, mode);
 4267                }
 4268            } else {
 4269                if !add {
 4270                    s.clear_disjoint();
 4271                }
 4272
 4273                s.set_pending_anchor_range(start..end, mode);
 4274            }
 4275        });
 4276    }
 4277
 4278    fn begin_columnar_selection(
 4279        &mut self,
 4280        position: DisplayPoint,
 4281        goal_column: u32,
 4282        reset: bool,
 4283        mode: ColumnarMode,
 4284        window: &mut Window,
 4285        cx: &mut Context<Self>,
 4286    ) {
 4287        if !self.focus_handle.is_focused(window) {
 4288            self.last_focused_descendant = None;
 4289            window.focus(&self.focus_handle, cx);
 4290        }
 4291
 4292        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4293
 4294        if reset {
 4295            let pointer_position = display_map
 4296                .buffer_snapshot()
 4297                .anchor_before(position.to_point(&display_map));
 4298
 4299            self.change_selections(
 4300                SelectionEffects::scroll(Autoscroll::newest()),
 4301                window,
 4302                cx,
 4303                |s| {
 4304                    s.clear_disjoint();
 4305                    s.set_pending_anchor_range(
 4306                        pointer_position..pointer_position,
 4307                        SelectMode::Character,
 4308                    );
 4309                },
 4310            );
 4311        };
 4312
 4313        let tail = self.selections.newest::<Point>(&display_map).tail();
 4314        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4315        self.columnar_selection_state = match mode {
 4316            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4317                selection_tail: selection_anchor,
 4318                display_point: if reset {
 4319                    if position.column() != goal_column {
 4320                        Some(DisplayPoint::new(position.row(), goal_column))
 4321                    } else {
 4322                        None
 4323                    }
 4324                } else {
 4325                    None
 4326                },
 4327            }),
 4328            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4329                selection_tail: selection_anchor,
 4330            }),
 4331        };
 4332
 4333        if !reset {
 4334            self.select_columns(position, goal_column, &display_map, window, cx);
 4335        }
 4336    }
 4337
 4338    fn update_selection(
 4339        &mut self,
 4340        position: DisplayPoint,
 4341        goal_column: u32,
 4342        scroll_delta: gpui::Point<f32>,
 4343        window: &mut Window,
 4344        cx: &mut Context<Self>,
 4345    ) {
 4346        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4347
 4348        if self.columnar_selection_state.is_some() {
 4349            self.select_columns(position, goal_column, &display_map, window, cx);
 4350        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4351            let buffer = display_map.buffer_snapshot();
 4352            let head;
 4353            let tail;
 4354            let mode = self.selections.pending_mode().unwrap();
 4355            match &mode {
 4356                SelectMode::Character => {
 4357                    head = position.to_point(&display_map);
 4358                    tail = pending.tail().to_point(buffer);
 4359                }
 4360                SelectMode::Word(original_range) => {
 4361                    let offset = display_map
 4362                        .clip_point(position, Bias::Left)
 4363                        .to_offset(&display_map, Bias::Left);
 4364                    let original_range = original_range.to_offset(buffer);
 4365
 4366                    let head_offset = if buffer.is_inside_word(offset, None)
 4367                        || original_range.contains(&offset)
 4368                    {
 4369                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4370                        if word_range.start < original_range.start {
 4371                            word_range.start
 4372                        } else {
 4373                            word_range.end
 4374                        }
 4375                    } else {
 4376                        offset
 4377                    };
 4378
 4379                    head = head_offset.to_point(buffer);
 4380                    if head_offset <= original_range.start {
 4381                        tail = original_range.end.to_point(buffer);
 4382                    } else {
 4383                        tail = original_range.start.to_point(buffer);
 4384                    }
 4385                }
 4386                SelectMode::Line(original_range) => {
 4387                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4388
 4389                    let position = display_map
 4390                        .clip_point(position, Bias::Left)
 4391                        .to_point(&display_map);
 4392                    let line_start = display_map.prev_line_boundary(position).0;
 4393                    let next_line_start = buffer.clip_point(
 4394                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4395                        Bias::Left,
 4396                    );
 4397
 4398                    if line_start < original_range.start {
 4399                        head = line_start
 4400                    } else {
 4401                        head = next_line_start
 4402                    }
 4403
 4404                    if head <= original_range.start {
 4405                        tail = original_range.end;
 4406                    } else {
 4407                        tail = original_range.start;
 4408                    }
 4409                }
 4410                SelectMode::All => {
 4411                    return;
 4412                }
 4413            };
 4414
 4415            if head < tail {
 4416                pending.start = buffer.anchor_before(head);
 4417                pending.end = buffer.anchor_before(tail);
 4418                pending.reversed = true;
 4419            } else {
 4420                pending.start = buffer.anchor_before(tail);
 4421                pending.end = buffer.anchor_before(head);
 4422                pending.reversed = false;
 4423            }
 4424
 4425            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4426                s.set_pending(pending.clone(), mode);
 4427            });
 4428        } else {
 4429            log::error!("update_selection dispatched with no pending selection");
 4430            return;
 4431        }
 4432
 4433        self.apply_scroll_delta(scroll_delta, window, cx);
 4434        cx.notify();
 4435    }
 4436
 4437    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4438        self.columnar_selection_state.take();
 4439        if let Some(pending_mode) = self.selections.pending_mode() {
 4440            let selections = self
 4441                .selections
 4442                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4443            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4444                s.select(selections);
 4445                s.clear_pending();
 4446                if s.is_extending() {
 4447                    s.set_is_extending(false);
 4448                } else {
 4449                    s.set_select_mode(pending_mode);
 4450                }
 4451            });
 4452        }
 4453    }
 4454
 4455    fn select_columns(
 4456        &mut self,
 4457        head: DisplayPoint,
 4458        goal_column: u32,
 4459        display_map: &DisplaySnapshot,
 4460        window: &mut Window,
 4461        cx: &mut Context<Self>,
 4462    ) {
 4463        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4464            return;
 4465        };
 4466
 4467        let tail = match columnar_state {
 4468            ColumnarSelectionState::FromMouse {
 4469                selection_tail,
 4470                display_point,
 4471            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4472            ColumnarSelectionState::FromSelection { selection_tail } => {
 4473                selection_tail.to_display_point(display_map)
 4474            }
 4475        };
 4476
 4477        let start_row = cmp::min(tail.row(), head.row());
 4478        let end_row = cmp::max(tail.row(), head.row());
 4479        let start_column = cmp::min(tail.column(), goal_column);
 4480        let end_column = cmp::max(tail.column(), goal_column);
 4481        let reversed = start_column < tail.column();
 4482
 4483        let selection_ranges = (start_row.0..=end_row.0)
 4484            .map(DisplayRow)
 4485            .filter_map(|row| {
 4486                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4487                    || start_column <= display_map.line_len(row))
 4488                    && !display_map.is_block_line(row)
 4489                {
 4490                    let start = display_map
 4491                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4492                        .to_point(display_map);
 4493                    let end = display_map
 4494                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4495                        .to_point(display_map);
 4496                    if reversed {
 4497                        Some(end..start)
 4498                    } else {
 4499                        Some(start..end)
 4500                    }
 4501                } else {
 4502                    None
 4503                }
 4504            })
 4505            .collect::<Vec<_>>();
 4506        if selection_ranges.is_empty() {
 4507            return;
 4508        }
 4509
 4510        let ranges = match columnar_state {
 4511            ColumnarSelectionState::FromMouse { .. } => {
 4512                let mut non_empty_ranges = selection_ranges
 4513                    .iter()
 4514                    .filter(|selection_range| selection_range.start != selection_range.end)
 4515                    .peekable();
 4516                if non_empty_ranges.peek().is_some() {
 4517                    non_empty_ranges.cloned().collect()
 4518                } else {
 4519                    selection_ranges
 4520                }
 4521            }
 4522            _ => selection_ranges,
 4523        };
 4524
 4525        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4526            s.select_ranges(ranges);
 4527        });
 4528        cx.notify();
 4529    }
 4530
 4531    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4532        self.selections
 4533            .all_adjusted(snapshot)
 4534            .iter()
 4535            .any(|selection| !selection.is_empty())
 4536    }
 4537
 4538    pub fn has_pending_nonempty_selection(&self) -> bool {
 4539        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4540            Some(Selection { start, end, .. }) => start != end,
 4541            None => false,
 4542        };
 4543
 4544        pending_nonempty_selection
 4545            || (self.columnar_selection_state.is_some()
 4546                && self.selections.disjoint_anchors().len() > 1)
 4547    }
 4548
 4549    pub fn has_pending_selection(&self) -> bool {
 4550        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4551    }
 4552
 4553    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4554        self.selection_mark_mode = false;
 4555        self.selection_drag_state = SelectionDragState::None;
 4556
 4557        if self.dismiss_menus_and_popups(true, window, cx) {
 4558            cx.notify();
 4559            return;
 4560        }
 4561        if self.clear_expanded_diff_hunks(cx) {
 4562            cx.notify();
 4563            return;
 4564        }
 4565        if self.show_git_blame_gutter {
 4566            self.show_git_blame_gutter = false;
 4567            cx.notify();
 4568            return;
 4569        }
 4570
 4571        if self.mode.is_full()
 4572            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4573        {
 4574            cx.notify();
 4575            return;
 4576        }
 4577
 4578        cx.propagate();
 4579    }
 4580
 4581    pub fn dismiss_menus_and_popups(
 4582        &mut self,
 4583        is_user_requested: bool,
 4584        window: &mut Window,
 4585        cx: &mut Context<Self>,
 4586    ) -> bool {
 4587        let mut dismissed = false;
 4588
 4589        dismissed |= self.take_rename(false, window, cx).is_some();
 4590        dismissed |= self.hide_blame_popover(true, cx);
 4591        dismissed |= hide_hover(self, cx);
 4592        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4593        dismissed |= self.hide_context_menu(window, cx).is_some();
 4594        dismissed |= self.mouse_context_menu.take().is_some();
 4595        dismissed |= is_user_requested
 4596            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4597        dismissed |= self.snippet_stack.pop().is_some();
 4598        if self.diff_review_drag_state.is_some() {
 4599            self.cancel_diff_review_drag(cx);
 4600            dismissed = true;
 4601        }
 4602        if !self.diff_review_overlays.is_empty() {
 4603            self.dismiss_all_diff_review_overlays(cx);
 4604            dismissed = true;
 4605        }
 4606
 4607        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4608            self.dismiss_diagnostics(cx);
 4609            dismissed = true;
 4610        }
 4611
 4612        dismissed
 4613    }
 4614
 4615    fn linked_editing_ranges_for(
 4616        &self,
 4617        selection: Range<text::Anchor>,
 4618        cx: &App,
 4619    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4620        if self.linked_edit_ranges.is_empty() {
 4621            return None;
 4622        }
 4623        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4624            selection.end.buffer_id.and_then(|end_buffer_id| {
 4625                if selection.start.buffer_id != Some(end_buffer_id) {
 4626                    return None;
 4627                }
 4628                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4629                let snapshot = buffer.read(cx).snapshot();
 4630                self.linked_edit_ranges
 4631                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4632                    .map(|ranges| (ranges, snapshot, buffer))
 4633            })?;
 4634        use text::ToOffset as TO;
 4635        // find offset from the start of current range to current cursor position
 4636        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4637
 4638        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4639        let start_difference = start_offset - start_byte_offset;
 4640        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4641        let end_difference = end_offset - start_byte_offset;
 4642
 4643        // Current range has associated linked ranges.
 4644        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4645        for range in linked_ranges.iter() {
 4646            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4647            let end_offset = start_offset + end_difference;
 4648            let start_offset = start_offset + start_difference;
 4649            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4650                continue;
 4651            }
 4652            if self.selections.disjoint_anchor_ranges().any(|s| {
 4653                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4654                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4655                {
 4656                    return false;
 4657                }
 4658                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4659                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4660            }) {
 4661                continue;
 4662            }
 4663            let start = buffer_snapshot.anchor_after(start_offset);
 4664            let end = buffer_snapshot.anchor_after(end_offset);
 4665            linked_edits
 4666                .entry(buffer.clone())
 4667                .or_default()
 4668                .push(start..end);
 4669        }
 4670        Some(linked_edits)
 4671    }
 4672
 4673    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4674        let text: Arc<str> = text.into();
 4675
 4676        if self.read_only(cx) {
 4677            return;
 4678        }
 4679
 4680        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4681
 4682        self.unfold_buffers_with_selections(cx);
 4683
 4684        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4685        let mut bracket_inserted = false;
 4686        let mut edits = Vec::new();
 4687        let mut linked_edits = LinkedEdits::new();
 4688        let mut new_selections = Vec::with_capacity(selections.len());
 4689        let mut new_autoclose_regions = Vec::new();
 4690        let snapshot = self.buffer.read(cx).read(cx);
 4691        let mut clear_linked_edit_ranges = false;
 4692        let mut all_selections_read_only = true;
 4693        let mut has_adjacent_edits = false;
 4694        let mut in_adjacent_group = false;
 4695
 4696        let mut regions = self
 4697            .selections_with_autoclose_regions(selections, &snapshot)
 4698            .peekable();
 4699
 4700        while let Some((selection, autoclose_region)) = regions.next() {
 4701            if snapshot
 4702                .point_to_buffer_point(selection.head())
 4703                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4704            {
 4705                continue;
 4706            }
 4707            if snapshot
 4708                .point_to_buffer_point(selection.tail())
 4709                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4710            {
 4711                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4712                continue;
 4713            }
 4714            all_selections_read_only = false;
 4715
 4716            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4717                // Determine if the inserted text matches the opening or closing
 4718                // bracket of any of this language's bracket pairs.
 4719                let mut bracket_pair = None;
 4720                let mut is_bracket_pair_start = false;
 4721                let mut is_bracket_pair_end = false;
 4722                if !text.is_empty() {
 4723                    let mut bracket_pair_matching_end = None;
 4724                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4725                    //  and they are removing the character that triggered IME popup.
 4726                    for (pair, enabled) in scope.brackets() {
 4727                        if !pair.close && !pair.surround {
 4728                            continue;
 4729                        }
 4730
 4731                        if enabled && pair.start.ends_with(text.as_ref()) {
 4732                            let prefix_len = pair.start.len() - text.len();
 4733                            let preceding_text_matches_prefix = prefix_len == 0
 4734                                || (selection.start.column >= (prefix_len as u32)
 4735                                    && snapshot.contains_str_at(
 4736                                        Point::new(
 4737                                            selection.start.row,
 4738                                            selection.start.column - (prefix_len as u32),
 4739                                        ),
 4740                                        &pair.start[..prefix_len],
 4741                                    ));
 4742                            if preceding_text_matches_prefix {
 4743                                bracket_pair = Some(pair.clone());
 4744                                is_bracket_pair_start = true;
 4745                                break;
 4746                            }
 4747                        }
 4748                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4749                        {
 4750                            // take first bracket pair matching end, but don't break in case a later bracket
 4751                            // pair matches start
 4752                            bracket_pair_matching_end = Some(pair.clone());
 4753                        }
 4754                    }
 4755                    if let Some(end) = bracket_pair_matching_end
 4756                        && bracket_pair.is_none()
 4757                    {
 4758                        bracket_pair = Some(end);
 4759                        is_bracket_pair_end = true;
 4760                    }
 4761                }
 4762
 4763                if let Some(bracket_pair) = bracket_pair {
 4764                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4765                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4766                    let auto_surround =
 4767                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4768                    if selection.is_empty() {
 4769                        if is_bracket_pair_start {
 4770                            // If the inserted text is a suffix of an opening bracket and the
 4771                            // selection is preceded by the rest of the opening bracket, then
 4772                            // insert the closing bracket.
 4773                            let following_text_allows_autoclose = snapshot
 4774                                .chars_at(selection.start)
 4775                                .next()
 4776                                .is_none_or(|c| scope.should_autoclose_before(c));
 4777
 4778                            let preceding_text_allows_autoclose = selection.start.column == 0
 4779                                || snapshot
 4780                                    .reversed_chars_at(selection.start)
 4781                                    .next()
 4782                                    .is_none_or(|c| {
 4783                                        bracket_pair.start != bracket_pair.end
 4784                                            || !snapshot
 4785                                                .char_classifier_at(selection.start)
 4786                                                .is_word(c)
 4787                                    });
 4788
 4789                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4790                                && bracket_pair.start.len() == 1
 4791                            {
 4792                                let target = bracket_pair.start.chars().next().unwrap();
 4793                                let mut byte_offset = 0u32;
 4794                                let current_line_count = snapshot
 4795                                    .reversed_chars_at(selection.start)
 4796                                    .take_while(|&c| c != '\n')
 4797                                    .filter(|c| {
 4798                                        byte_offset += c.len_utf8() as u32;
 4799                                        if *c != target {
 4800                                            return false;
 4801                                        }
 4802
 4803                                        let point = Point::new(
 4804                                            selection.start.row,
 4805                                            selection.start.column.saturating_sub(byte_offset),
 4806                                        );
 4807
 4808                                        let is_enabled = snapshot
 4809                                            .language_scope_at(point)
 4810                                            .and_then(|scope| {
 4811                                                scope
 4812                                                    .brackets()
 4813                                                    .find(|(pair, _)| {
 4814                                                        pair.start == bracket_pair.start
 4815                                                    })
 4816                                                    .map(|(_, enabled)| enabled)
 4817                                            })
 4818                                            .unwrap_or(true);
 4819
 4820                                        let is_delimiter = snapshot
 4821                                            .language_scope_at(Point::new(
 4822                                                point.row,
 4823                                                point.column + 1,
 4824                                            ))
 4825                                            .and_then(|scope| {
 4826                                                scope
 4827                                                    .brackets()
 4828                                                    .find(|(pair, _)| {
 4829                                                        pair.start == bracket_pair.start
 4830                                                    })
 4831                                                    .map(|(_, enabled)| !enabled)
 4832                                            })
 4833                                            .unwrap_or(false);
 4834
 4835                                        is_enabled && !is_delimiter
 4836                                    })
 4837                                    .count();
 4838                                current_line_count % 2 == 1
 4839                            } else {
 4840                                false
 4841                            };
 4842
 4843                            if autoclose
 4844                                && bracket_pair.close
 4845                                && following_text_allows_autoclose
 4846                                && preceding_text_allows_autoclose
 4847                                && !is_closing_quote
 4848                            {
 4849                                let anchor = snapshot.anchor_before(selection.end);
 4850                                new_selections.push((selection.map(|_| anchor), text.len()));
 4851                                new_autoclose_regions.push((
 4852                                    anchor,
 4853                                    text.len(),
 4854                                    selection.id,
 4855                                    bracket_pair.clone(),
 4856                                ));
 4857                                edits.push((
 4858                                    selection.range(),
 4859                                    format!("{}{}", text, bracket_pair.end).into(),
 4860                                ));
 4861                                bracket_inserted = true;
 4862                                continue;
 4863                            }
 4864                        }
 4865
 4866                        if let Some(region) = autoclose_region {
 4867                            // If the selection is followed by an auto-inserted closing bracket,
 4868                            // then don't insert that closing bracket again; just move the selection
 4869                            // past the closing bracket.
 4870                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4871                                && text.as_ref() == region.pair.end.as_str()
 4872                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4873                            if should_skip {
 4874                                let anchor = snapshot.anchor_after(selection.end);
 4875                                new_selections
 4876                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4877                                continue;
 4878                            }
 4879                        }
 4880
 4881                        let always_treat_brackets_as_autoclosed = snapshot
 4882                            .language_settings_at(selection.start, cx)
 4883                            .always_treat_brackets_as_autoclosed;
 4884                        if always_treat_brackets_as_autoclosed
 4885                            && is_bracket_pair_end
 4886                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4887                        {
 4888                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4889                            // and the inserted text is a closing bracket and the selection is followed
 4890                            // by the closing bracket then move the selection past the closing bracket.
 4891                            let anchor = snapshot.anchor_after(selection.end);
 4892                            new_selections.push((selection.map(|_| anchor), text.len()));
 4893                            continue;
 4894                        }
 4895                    }
 4896                    // If an opening bracket is 1 character long and is typed while
 4897                    // text is selected, then surround that text with the bracket pair.
 4898                    else if auto_surround
 4899                        && bracket_pair.surround
 4900                        && is_bracket_pair_start
 4901                        && bracket_pair.start.chars().count() == 1
 4902                    {
 4903                        edits.push((selection.start..selection.start, text.clone()));
 4904                        edits.push((
 4905                            selection.end..selection.end,
 4906                            bracket_pair.end.as_str().into(),
 4907                        ));
 4908                        bracket_inserted = true;
 4909                        new_selections.push((
 4910                            Selection {
 4911                                id: selection.id,
 4912                                start: snapshot.anchor_after(selection.start),
 4913                                end: snapshot.anchor_before(selection.end),
 4914                                reversed: selection.reversed,
 4915                                goal: selection.goal,
 4916                            },
 4917                            0,
 4918                        ));
 4919                        continue;
 4920                    }
 4921                }
 4922            }
 4923
 4924            if self.auto_replace_emoji_shortcode
 4925                && selection.is_empty()
 4926                && text.as_ref().ends_with(':')
 4927                && let Some(possible_emoji_short_code) =
 4928                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4929                && !possible_emoji_short_code.is_empty()
 4930                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4931            {
 4932                let emoji_shortcode_start = Point::new(
 4933                    selection.start.row,
 4934                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4935                );
 4936
 4937                // Remove shortcode from buffer
 4938                edits.push((
 4939                    emoji_shortcode_start..selection.start,
 4940                    "".to_string().into(),
 4941                ));
 4942                new_selections.push((
 4943                    Selection {
 4944                        id: selection.id,
 4945                        start: snapshot.anchor_after(emoji_shortcode_start),
 4946                        end: snapshot.anchor_before(selection.start),
 4947                        reversed: selection.reversed,
 4948                        goal: selection.goal,
 4949                    },
 4950                    0,
 4951                ));
 4952
 4953                // Insert emoji
 4954                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4955                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4956                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4957
 4958                continue;
 4959            }
 4960
 4961            let next_is_adjacent = regions
 4962                .peek()
 4963                .is_some_and(|(next, _)| selection.end == next.start);
 4964
 4965            // If not handling any auto-close operation, then just replace the selected
 4966            // text with the given input and move the selection to the end of the
 4967            // newly inserted text.
 4968            let anchor = if in_adjacent_group || next_is_adjacent {
 4969                // After edits the right bias would shift those anchor to the next visible fragment
 4970                // but we want to resolve to the previous one
 4971                snapshot.anchor_before(selection.end)
 4972            } else {
 4973                snapshot.anchor_after(selection.end)
 4974            };
 4975
 4976            if !self.linked_edit_ranges.is_empty() {
 4977                let start_anchor = snapshot.anchor_before(selection.start);
 4978
 4979                let is_word_char = text.chars().next().is_none_or(|char| {
 4980                    let classifier = snapshot
 4981                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4982                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4983                    classifier.is_word(char)
 4984                });
 4985                let is_dot = text.as_ref() == ".";
 4986                let should_apply_linked_edit = is_word_char || is_dot;
 4987
 4988                if should_apply_linked_edit {
 4989                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4990                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4991                } else {
 4992                    clear_linked_edit_ranges = true;
 4993                }
 4994            }
 4995
 4996            new_selections.push((selection.map(|_| anchor), 0));
 4997            edits.push((selection.start..selection.end, text.clone()));
 4998
 4999            has_adjacent_edits |= next_is_adjacent;
 5000            in_adjacent_group = next_is_adjacent;
 5001        }
 5002
 5003        if all_selections_read_only {
 5004            return;
 5005        }
 5006
 5007        drop(regions);
 5008        drop(snapshot);
 5009
 5010        self.transact(window, cx, |this, window, cx| {
 5011            if clear_linked_edit_ranges {
 5012                this.linked_edit_ranges.clear();
 5013            }
 5014            let initial_buffer_versions =
 5015                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 5016
 5017            this.buffer.update(cx, |buffer, cx| {
 5018                if has_adjacent_edits {
 5019                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 5020                } else {
 5021                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 5022                }
 5023            });
 5024            linked_edits.apply(cx);
 5025            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 5026            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 5027            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 5028            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 5029                new_anchor_selections,
 5030                &map,
 5031            )
 5032            .zip(new_selection_deltas)
 5033            .map(|(selection, delta)| Selection {
 5034                id: selection.id,
 5035                start: selection.start + delta,
 5036                end: selection.end + delta,
 5037                reversed: selection.reversed,
 5038                goal: SelectionGoal::None,
 5039            })
 5040            .collect::<Vec<_>>();
 5041
 5042            let mut i = 0;
 5043            for (position, delta, selection_id, pair) in new_autoclose_regions {
 5044                let position = position.to_offset(map.buffer_snapshot()) + delta;
 5045                let start = map.buffer_snapshot().anchor_before(position);
 5046                let end = map.buffer_snapshot().anchor_after(position);
 5047                while let Some(existing_state) = this.autoclose_regions.get(i) {
 5048                    match existing_state
 5049                        .range
 5050                        .start
 5051                        .cmp(&start, map.buffer_snapshot())
 5052                    {
 5053                        Ordering::Less => i += 1,
 5054                        Ordering::Greater => break,
 5055                        Ordering::Equal => {
 5056                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 5057                                Ordering::Less => i += 1,
 5058                                Ordering::Equal => break,
 5059                                Ordering::Greater => break,
 5060                            }
 5061                        }
 5062                    }
 5063                }
 5064                this.autoclose_regions.insert(
 5065                    i,
 5066                    AutocloseRegion {
 5067                        selection_id,
 5068                        range: start..end,
 5069                        pair,
 5070                    },
 5071                );
 5072            }
 5073
 5074            let had_active_edit_prediction = this.has_active_edit_prediction();
 5075            this.change_selections(
 5076                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 5077                window,
 5078                cx,
 5079                |s| s.select(new_selections),
 5080            );
 5081
 5082            if !bracket_inserted
 5083                && let Some(on_type_format_task) =
 5084                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5085            {
 5086                on_type_format_task.detach_and_log_err(cx);
 5087            }
 5088
 5089            let editor_settings = EditorSettings::get_global(cx);
 5090            if bracket_inserted
 5091                && (editor_settings.auto_signature_help
 5092                    || editor_settings.show_signature_help_after_edits)
 5093            {
 5094                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5095            }
 5096
 5097            let trigger_in_words =
 5098                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5099            if this.hard_wrap.is_some() {
 5100                let latest: Range<Point> = this.selections.newest(&map).range();
 5101                if latest.is_empty()
 5102                    && this
 5103                        .buffer()
 5104                        .read(cx)
 5105                        .snapshot(cx)
 5106                        .line_len(MultiBufferRow(latest.start.row))
 5107                        == latest.start.column
 5108                {
 5109                    this.rewrap_impl(
 5110                        RewrapOptions {
 5111                            override_language_settings: true,
 5112                            preserve_existing_whitespace: true,
 5113                        },
 5114                        cx,
 5115                    )
 5116                }
 5117            }
 5118            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5119            refresh_linked_ranges(this, window, cx);
 5120            this.refresh_edit_prediction(true, false, window, cx);
 5121            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5122        });
 5123    }
 5124
 5125    fn find_possible_emoji_shortcode_at_position(
 5126        snapshot: &MultiBufferSnapshot,
 5127        position: Point,
 5128    ) -> Option<String> {
 5129        let mut chars = Vec::new();
 5130        let mut found_colon = false;
 5131        for char in snapshot.reversed_chars_at(position).take(100) {
 5132            // Found a possible emoji shortcode in the middle of the buffer
 5133            if found_colon {
 5134                if char.is_whitespace() {
 5135                    chars.reverse();
 5136                    return Some(chars.iter().collect());
 5137                }
 5138                // If the previous character is not a whitespace, we are in the middle of a word
 5139                // and we only want to complete the shortcode if the word is made up of other emojis
 5140                let mut containing_word = String::new();
 5141                for ch in snapshot
 5142                    .reversed_chars_at(position)
 5143                    .skip(chars.len() + 1)
 5144                    .take(100)
 5145                {
 5146                    if ch.is_whitespace() {
 5147                        break;
 5148                    }
 5149                    containing_word.push(ch);
 5150                }
 5151                let containing_word = containing_word.chars().rev().collect::<String>();
 5152                if util::word_consists_of_emojis(containing_word.as_str()) {
 5153                    chars.reverse();
 5154                    return Some(chars.iter().collect());
 5155                }
 5156            }
 5157
 5158            if char.is_whitespace() || !char.is_ascii() {
 5159                return None;
 5160            }
 5161            if char == ':' {
 5162                found_colon = true;
 5163            } else {
 5164                chars.push(char);
 5165            }
 5166        }
 5167        // Found a possible emoji shortcode at the beginning of the buffer
 5168        chars.reverse();
 5169        Some(chars.iter().collect())
 5170    }
 5171
 5172    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5173        if self.read_only(cx) {
 5174            return;
 5175        }
 5176
 5177        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5178        self.transact(window, cx, |this, window, cx| {
 5179            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5180                let selections = this
 5181                    .selections
 5182                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5183                let multi_buffer = this.buffer.read(cx);
 5184                let buffer = multi_buffer.snapshot(cx);
 5185                selections
 5186                    .iter()
 5187                    .map(|selection| {
 5188                        let start_point = selection.start.to_point(&buffer);
 5189                        let mut existing_indent =
 5190                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5191                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5192                        let start = selection.start;
 5193                        let end = selection.end;
 5194                        let selection_is_empty = start == end;
 5195                        let language_scope = buffer.language_scope_at(start);
 5196                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5197                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5198                                &buffer,
 5199                                start..end,
 5200                                language,
 5201                            )
 5202                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5203                                    &buffer,
 5204                                    start..end,
 5205                                );
 5206
 5207                            let mut newline_config = NewlineConfig::Newline {
 5208                                additional_indent: IndentSize::spaces(0),
 5209                                extra_line_additional_indent: if needs_extra_newline {
 5210                                    Some(IndentSize::spaces(0))
 5211                                } else {
 5212                                    None
 5213                                },
 5214                                prevent_auto_indent: false,
 5215                            };
 5216
 5217                            let comment_delimiter = maybe!({
 5218                                if !selection_is_empty {
 5219                                    return None;
 5220                                }
 5221
 5222                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5223                                    return None;
 5224                                }
 5225
 5226                                return comment_delimiter_for_newline(
 5227                                    &start_point,
 5228                                    &buffer,
 5229                                    language,
 5230                                );
 5231                            });
 5232
 5233                            let doc_delimiter = maybe!({
 5234                                if !selection_is_empty {
 5235                                    return None;
 5236                                }
 5237
 5238                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5239                                    return None;
 5240                                }
 5241
 5242                                return documentation_delimiter_for_newline(
 5243                                    &start_point,
 5244                                    &buffer,
 5245                                    language,
 5246                                    &mut newline_config,
 5247                                );
 5248                            });
 5249
 5250                            let list_delimiter = maybe!({
 5251                                if !selection_is_empty {
 5252                                    return None;
 5253                                }
 5254
 5255                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5256                                    return None;
 5257                                }
 5258
 5259                                return list_delimiter_for_newline(
 5260                                    &start_point,
 5261                                    &buffer,
 5262                                    language,
 5263                                    &mut newline_config,
 5264                                );
 5265                            });
 5266
 5267                            (
 5268                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5269                                newline_config,
 5270                            )
 5271                        } else {
 5272                            (
 5273                                None,
 5274                                NewlineConfig::Newline {
 5275                                    additional_indent: IndentSize::spaces(0),
 5276                                    extra_line_additional_indent: None,
 5277                                    prevent_auto_indent: false,
 5278                                },
 5279                            )
 5280                        };
 5281
 5282                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5283                            NewlineConfig::ClearCurrentLine => {
 5284                                let row_start =
 5285                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5286                                (row_start, String::new(), false)
 5287                            }
 5288                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5289                                let row_start =
 5290                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5291                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5292                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5293                                let reduced_indent =
 5294                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5295                                let mut new_text = String::new();
 5296                                new_text.extend(reduced_indent.chars());
 5297                                new_text.push_str(continuation);
 5298                                (row_start, new_text, true)
 5299                            }
 5300                            NewlineConfig::Newline {
 5301                                additional_indent,
 5302                                extra_line_additional_indent,
 5303                                prevent_auto_indent,
 5304                            } => {
 5305                                let auto_indent_mode =
 5306                                    buffer.language_settings_at(start, cx).auto_indent;
 5307                                let preserve_indent =
 5308                                    auto_indent_mode != language::AutoIndentMode::None;
 5309                                let apply_syntax_indent =
 5310                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5311                                let capacity_for_delimiter =
 5312                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5313                                let existing_indent_len = if preserve_indent {
 5314                                    existing_indent.len as usize
 5315                                } else {
 5316                                    0
 5317                                };
 5318                                let extra_line_len = extra_line_additional_indent
 5319                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5320                                    .unwrap_or(0);
 5321                                let mut new_text = String::with_capacity(
 5322                                    1 + capacity_for_delimiter
 5323                                        + existing_indent_len
 5324                                        + additional_indent.len as usize
 5325                                        + extra_line_len,
 5326                                );
 5327                                new_text.push('\n');
 5328                                if preserve_indent {
 5329                                    new_text.extend(existing_indent.chars());
 5330                                }
 5331                                new_text.extend(additional_indent.chars());
 5332                                if let Some(delimiter) = &delimiter {
 5333                                    new_text.push_str(delimiter);
 5334                                }
 5335                                if let Some(extra_indent) = extra_line_additional_indent {
 5336                                    new_text.push('\n');
 5337                                    if preserve_indent {
 5338                                        new_text.extend(existing_indent.chars());
 5339                                    }
 5340                                    new_text.extend(extra_indent.chars());
 5341                                }
 5342                                (
 5343                                    start,
 5344                                    new_text,
 5345                                    *prevent_auto_indent || !apply_syntax_indent,
 5346                                )
 5347                            }
 5348                        };
 5349
 5350                        let anchor = buffer.anchor_after(end);
 5351                        let new_selection = selection.map(|_| anchor);
 5352                        (
 5353                            ((edit_start..end, new_text), prevent_auto_indent),
 5354                            (newline_config.has_extra_line(), new_selection),
 5355                        )
 5356                    })
 5357                    .unzip()
 5358            };
 5359
 5360            let mut auto_indent_edits = Vec::new();
 5361            let mut edits = Vec::new();
 5362            for (edit, prevent_auto_indent) in edits_with_flags {
 5363                if prevent_auto_indent {
 5364                    edits.push(edit);
 5365                } else {
 5366                    auto_indent_edits.push(edit);
 5367                }
 5368            }
 5369            if !edits.is_empty() {
 5370                this.edit(edits, cx);
 5371            }
 5372            if !auto_indent_edits.is_empty() {
 5373                this.edit_with_autoindent(auto_indent_edits, cx);
 5374            }
 5375
 5376            let buffer = this.buffer.read(cx).snapshot(cx);
 5377            let new_selections = selection_info
 5378                .into_iter()
 5379                .map(|(extra_newline_inserted, new_selection)| {
 5380                    let mut cursor = new_selection.end.to_point(&buffer);
 5381                    if extra_newline_inserted {
 5382                        cursor.row -= 1;
 5383                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5384                    }
 5385                    new_selection.map(|_| cursor)
 5386                })
 5387                .collect();
 5388
 5389            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5390            this.refresh_edit_prediction(true, false, window, cx);
 5391            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5392                task.detach_and_log_err(cx);
 5393            }
 5394        });
 5395    }
 5396
 5397    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5398        if self.read_only(cx) {
 5399            return;
 5400        }
 5401
 5402        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5403
 5404        let buffer = self.buffer.read(cx);
 5405        let snapshot = buffer.snapshot(cx);
 5406
 5407        let mut edits = Vec::new();
 5408        let mut rows = Vec::new();
 5409
 5410        for (rows_inserted, selection) in self
 5411            .selections
 5412            .all_adjusted(&self.display_snapshot(cx))
 5413            .into_iter()
 5414            .enumerate()
 5415        {
 5416            let cursor = selection.head();
 5417            let row = cursor.row;
 5418
 5419            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5420
 5421            let newline = "\n".to_string();
 5422            edits.push((start_of_line..start_of_line, newline));
 5423
 5424            rows.push(row + rows_inserted as u32);
 5425        }
 5426
 5427        self.transact(window, cx, |editor, window, cx| {
 5428            editor.edit(edits, cx);
 5429
 5430            editor.change_selections(Default::default(), window, cx, |s| {
 5431                let mut index = 0;
 5432                s.move_cursors_with(&mut |map, _, _| {
 5433                    let row = rows[index];
 5434                    index += 1;
 5435
 5436                    let point = Point::new(row, 0);
 5437                    let boundary = map.next_line_boundary(point).1;
 5438                    let clipped = map.clip_point(boundary, Bias::Left);
 5439
 5440                    (clipped, SelectionGoal::None)
 5441                });
 5442            });
 5443
 5444            let mut indent_edits = Vec::new();
 5445            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5446            for row in rows {
 5447                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5448                for (row, indent) in indents {
 5449                    if indent.len == 0 {
 5450                        continue;
 5451                    }
 5452
 5453                    let text = match indent.kind {
 5454                        IndentKind::Space => " ".repeat(indent.len as usize),
 5455                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5456                    };
 5457                    let point = Point::new(row.0, 0);
 5458                    indent_edits.push((point..point, text));
 5459                }
 5460            }
 5461            editor.edit(indent_edits, cx);
 5462            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5463                format.detach_and_log_err(cx);
 5464            }
 5465        });
 5466    }
 5467
 5468    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5469        if self.read_only(cx) {
 5470            return;
 5471        }
 5472
 5473        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5474
 5475        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5476        let mut rows = Vec::new();
 5477        let mut rows_inserted = 0;
 5478
 5479        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5480            let cursor = selection.head();
 5481            let row = cursor.row;
 5482
 5483            let point = Point::new(row, 0);
 5484            let Some((buffer_handle, buffer_point, _)) =
 5485                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5486            else {
 5487                continue;
 5488            };
 5489
 5490            buffer_edits
 5491                .entry(buffer_handle.entity_id())
 5492                .or_insert_with(|| (buffer_handle, Vec::new()))
 5493                .1
 5494                .push(buffer_point);
 5495
 5496            rows_inserted += 1;
 5497            rows.push(row + rows_inserted);
 5498        }
 5499
 5500        self.transact(window, cx, |editor, window, cx| {
 5501            for (_, (buffer_handle, points)) in &buffer_edits {
 5502                buffer_handle.update(cx, |buffer, cx| {
 5503                    let edits: Vec<_> = points
 5504                        .iter()
 5505                        .map(|point| {
 5506                            let target = Point::new(point.row + 1, 0);
 5507                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5508                            (start_of_line..start_of_line, "\n")
 5509                        })
 5510                        .collect();
 5511                    buffer.edit(edits, None, cx);
 5512                });
 5513            }
 5514
 5515            editor.change_selections(Default::default(), window, cx, |s| {
 5516                let mut index = 0;
 5517                s.move_cursors_with(&mut |map, _, _| {
 5518                    let row = rows[index];
 5519                    index += 1;
 5520
 5521                    let point = Point::new(row, 0);
 5522                    let boundary = map.next_line_boundary(point).1;
 5523                    let clipped = map.clip_point(boundary, Bias::Left);
 5524
 5525                    (clipped, SelectionGoal::None)
 5526                });
 5527            });
 5528
 5529            let mut indent_edits = Vec::new();
 5530            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5531            for row in rows {
 5532                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5533                for (row, indent) in indents {
 5534                    if indent.len == 0 {
 5535                        continue;
 5536                    }
 5537
 5538                    let text = match indent.kind {
 5539                        IndentKind::Space => " ".repeat(indent.len as usize),
 5540                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5541                    };
 5542                    let point = Point::new(row.0, 0);
 5543                    indent_edits.push((point..point, text));
 5544                }
 5545            }
 5546            editor.edit(indent_edits, cx);
 5547            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5548                format.detach_and_log_err(cx);
 5549            }
 5550        });
 5551    }
 5552
 5553    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5554        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5555            original_indent_columns: Vec::new(),
 5556        });
 5557        self.replace_selections(text, autoindent, window, cx, false);
 5558    }
 5559
 5560    /// Replaces the editor's selections with the provided `text`, applying the
 5561    /// given `autoindent_mode` (`None` will skip autoindentation).
 5562    ///
 5563    /// Early returns if the editor is in read-only mode, without applying any
 5564    /// edits.
 5565    fn replace_selections(
 5566        &mut self,
 5567        text: &str,
 5568        autoindent_mode: Option<AutoindentMode>,
 5569        window: &mut Window,
 5570        cx: &mut Context<Self>,
 5571        apply_linked_edits: bool,
 5572    ) {
 5573        if self.read_only(cx) {
 5574            return;
 5575        }
 5576
 5577        let text: Arc<str> = text.into();
 5578        self.transact(window, cx, |this, window, cx| {
 5579            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5580            let linked_edits = if apply_linked_edits {
 5581                this.linked_edits_for_selections(text.clone(), cx)
 5582            } else {
 5583                LinkedEdits::new()
 5584            };
 5585
 5586            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5587                let anchors = {
 5588                    let snapshot = buffer.read(cx);
 5589                    old_selections
 5590                        .iter()
 5591                        .map(|s| {
 5592                            let anchor = snapshot.anchor_after(s.head());
 5593                            s.map(|_| anchor)
 5594                        })
 5595                        .collect::<Vec<_>>()
 5596                };
 5597                buffer.edit(
 5598                    old_selections
 5599                        .iter()
 5600                        .map(|s| (s.start..s.end, text.clone())),
 5601                    autoindent_mode,
 5602                    cx,
 5603                );
 5604                anchors
 5605            });
 5606
 5607            linked_edits.apply(cx);
 5608
 5609            this.change_selections(Default::default(), window, cx, |s| {
 5610                s.select_anchors(selection_anchors);
 5611            });
 5612
 5613            if apply_linked_edits {
 5614                refresh_linked_ranges(this, window, cx);
 5615            }
 5616
 5617            cx.notify();
 5618        });
 5619    }
 5620
 5621    /// Collects linked edits for the current selections, pairing each linked
 5622    /// range with `text`.
 5623    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5624        let mut linked_edits = LinkedEdits::new();
 5625        if !self.linked_edit_ranges.is_empty() {
 5626            for selection in self.selections.disjoint_anchors() {
 5627                let start = selection.start.text_anchor;
 5628                let end = selection.end.text_anchor;
 5629                linked_edits.push(self, start..end, text.clone(), cx);
 5630            }
 5631        }
 5632        linked_edits
 5633    }
 5634
 5635    /// Deletes the content covered by the current selections and applies
 5636    /// linked edits.
 5637    pub fn delete_selections_with_linked_edits(
 5638        &mut self,
 5639        window: &mut Window,
 5640        cx: &mut Context<Self>,
 5641    ) {
 5642        self.replace_selections("", None, window, cx, true);
 5643    }
 5644
 5645    #[cfg(any(test, feature = "test-support"))]
 5646    pub fn set_linked_edit_ranges_for_testing(
 5647        &mut self,
 5648        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5649        cx: &mut Context<Self>,
 5650    ) -> Option<()> {
 5651        let Some((buffer, _)) = self
 5652            .buffer
 5653            .read(cx)
 5654            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5655        else {
 5656            return None;
 5657        };
 5658        let buffer = buffer.read(cx);
 5659        let buffer_id = buffer.remote_id();
 5660        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5661        for (base_range, linked_ranges_points) in ranges {
 5662            let base_anchor =
 5663                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5664            let linked_anchors = linked_ranges_points
 5665                .into_iter()
 5666                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5667                .collect();
 5668            linked_ranges.push((base_anchor, linked_anchors));
 5669        }
 5670        let mut map = HashMap::default();
 5671        map.insert(buffer_id, linked_ranges);
 5672        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5673        Some(())
 5674    }
 5675
 5676    fn trigger_completion_on_input(
 5677        &mut self,
 5678        text: &str,
 5679        trigger_in_words: bool,
 5680        window: &mut Window,
 5681        cx: &mut Context<Self>,
 5682    ) {
 5683        let completions_source = self
 5684            .context_menu
 5685            .borrow()
 5686            .as_ref()
 5687            .and_then(|menu| match menu {
 5688                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5689                CodeContextMenu::CodeActions(_) => None,
 5690            });
 5691
 5692        match completions_source {
 5693            Some(CompletionsMenuSource::Words { .. }) => {
 5694                self.open_or_update_completions_menu(
 5695                    Some(CompletionsMenuSource::Words {
 5696                        ignore_threshold: false,
 5697                    }),
 5698                    None,
 5699                    trigger_in_words,
 5700                    window,
 5701                    cx,
 5702                );
 5703            }
 5704            _ => self.open_or_update_completions_menu(
 5705                None,
 5706                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5707                true,
 5708                window,
 5709                cx,
 5710            ),
 5711        }
 5712    }
 5713
 5714    /// If any empty selections is touching the start of its innermost containing autoclose
 5715    /// region, expand it to select the brackets.
 5716    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5717        let selections = self
 5718            .selections
 5719            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5720        let buffer = self.buffer.read(cx).read(cx);
 5721        let new_selections = self
 5722            .selections_with_autoclose_regions(selections, &buffer)
 5723            .map(|(mut selection, region)| {
 5724                if !selection.is_empty() {
 5725                    return selection;
 5726                }
 5727
 5728                if let Some(region) = region {
 5729                    let mut range = region.range.to_offset(&buffer);
 5730                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5731                        range.start -= region.pair.start.len();
 5732                        if buffer.contains_str_at(range.start, &region.pair.start)
 5733                            && buffer.contains_str_at(range.end, &region.pair.end)
 5734                        {
 5735                            range.end += region.pair.end.len();
 5736                            selection.start = range.start;
 5737                            selection.end = range.end;
 5738
 5739                            return selection;
 5740                        }
 5741                    }
 5742                }
 5743
 5744                let always_treat_brackets_as_autoclosed = buffer
 5745                    .language_settings_at(selection.start, cx)
 5746                    .always_treat_brackets_as_autoclosed;
 5747
 5748                if !always_treat_brackets_as_autoclosed {
 5749                    return selection;
 5750                }
 5751
 5752                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5753                    for (pair, enabled) in scope.brackets() {
 5754                        if !enabled || !pair.close {
 5755                            continue;
 5756                        }
 5757
 5758                        if buffer.contains_str_at(selection.start, &pair.end) {
 5759                            let pair_start_len = pair.start.len();
 5760                            if buffer.contains_str_at(
 5761                                selection.start.saturating_sub_usize(pair_start_len),
 5762                                &pair.start,
 5763                            ) {
 5764                                selection.start -= pair_start_len;
 5765                                selection.end += pair.end.len();
 5766
 5767                                return selection;
 5768                            }
 5769                        }
 5770                    }
 5771                }
 5772
 5773                selection
 5774            })
 5775            .collect();
 5776
 5777        drop(buffer);
 5778        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5779            selections.select(new_selections)
 5780        });
 5781    }
 5782
 5783    /// Iterate the given selections, and for each one, find the smallest surrounding
 5784    /// autoclose region. This uses the ordering of the selections and the autoclose
 5785    /// regions to avoid repeated comparisons.
 5786    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5787        &'a self,
 5788        selections: impl IntoIterator<Item = Selection<D>>,
 5789        buffer: &'a MultiBufferSnapshot,
 5790    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5791        let mut i = 0;
 5792        let mut regions = self.autoclose_regions.as_slice();
 5793        selections.into_iter().map(move |selection| {
 5794            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5795
 5796            let mut enclosing = None;
 5797            while let Some(pair_state) = regions.get(i) {
 5798                if pair_state.range.end.to_offset(buffer) < range.start {
 5799                    regions = &regions[i + 1..];
 5800                    i = 0;
 5801                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5802                    break;
 5803                } else {
 5804                    if pair_state.selection_id == selection.id {
 5805                        enclosing = Some(pair_state);
 5806                    }
 5807                    i += 1;
 5808                }
 5809            }
 5810
 5811            (selection, enclosing)
 5812        })
 5813    }
 5814
 5815    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5816    fn invalidate_autoclose_regions(
 5817        &mut self,
 5818        mut selections: &[Selection<Anchor>],
 5819        buffer: &MultiBufferSnapshot,
 5820    ) {
 5821        self.autoclose_regions.retain(|state| {
 5822            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5823                return false;
 5824            }
 5825
 5826            let mut i = 0;
 5827            while let Some(selection) = selections.get(i) {
 5828                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5829                    selections = &selections[1..];
 5830                    continue;
 5831                }
 5832                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5833                    break;
 5834                }
 5835                if selection.id == state.selection_id {
 5836                    return true;
 5837                } else {
 5838                    i += 1;
 5839                }
 5840            }
 5841            false
 5842        });
 5843    }
 5844
 5845    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5846        let offset = position.to_offset(buffer);
 5847        let (word_range, kind) =
 5848            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5849        if offset > word_range.start && kind == Some(CharKind::Word) {
 5850            Some(
 5851                buffer
 5852                    .text_for_range(word_range.start..offset)
 5853                    .collect::<String>(),
 5854            )
 5855        } else {
 5856            None
 5857        }
 5858    }
 5859
 5860    pub fn visible_excerpts(
 5861        &self,
 5862        lsp_related_only: bool,
 5863        cx: &mut Context<Editor>,
 5864    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5865        let project = self.project().cloned();
 5866        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5867        let multi_buffer = self.buffer().read(cx);
 5868        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5869        multi_buffer_snapshot
 5870            .range_to_buffer_ranges(
 5871                self.multi_buffer_visible_range(&display_snapshot, cx)
 5872                    .to_inclusive(),
 5873            )
 5874            .into_iter()
 5875            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5876            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5877                if !lsp_related_only {
 5878                    return Some((
 5879                        excerpt_id,
 5880                        (
 5881                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5882                            buffer.version().clone(),
 5883                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5884                        ),
 5885                    ));
 5886                }
 5887
 5888                let project = project.as_ref()?.read(cx);
 5889                let buffer_file = project::File::from_dyn(buffer.file())?;
 5890                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5891                let worktree_entry = buffer_worktree
 5892                    .read(cx)
 5893                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5894                if worktree_entry.is_ignored {
 5895                    None
 5896                } else {
 5897                    Some((
 5898                        excerpt_id,
 5899                        (
 5900                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5901                            buffer.version().clone(),
 5902                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5903                        ),
 5904                    ))
 5905                }
 5906            })
 5907            .collect()
 5908    }
 5909
 5910    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5911        TextLayoutDetails {
 5912            text_system: window.text_system().clone(),
 5913            editor_style: self.style.clone().unwrap(),
 5914            rem_size: window.rem_size(),
 5915            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5916            visible_rows: self.visible_line_count(),
 5917            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5918        }
 5919    }
 5920
 5921    fn trigger_on_type_formatting(
 5922        &self,
 5923        input: String,
 5924        window: &mut Window,
 5925        cx: &mut Context<Self>,
 5926    ) -> Option<Task<Result<()>>> {
 5927        if input.chars().count() != 1 {
 5928            return None;
 5929        }
 5930
 5931        let project = self.project()?;
 5932        let position = self.selections.newest_anchor().head();
 5933        let (buffer, buffer_position) = self
 5934            .buffer
 5935            .read(cx)
 5936            .text_anchor_for_position(position, cx)?;
 5937
 5938        let settings = language_settings::language_settings(
 5939            buffer
 5940                .read(cx)
 5941                .language_at(buffer_position)
 5942                .map(|l| l.name()),
 5943            buffer.read(cx).file(),
 5944            cx,
 5945        );
 5946        if !settings.use_on_type_format {
 5947            return None;
 5948        }
 5949
 5950        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5951        // hence we do LSP request & edit on host side only — add formats to host's history.
 5952        let push_to_lsp_host_history = true;
 5953        // If this is not the host, append its history with new edits.
 5954        let push_to_client_history = project.read(cx).is_via_collab();
 5955
 5956        let on_type_formatting = project.update(cx, |project, cx| {
 5957            project.on_type_format(
 5958                buffer.clone(),
 5959                buffer_position,
 5960                input,
 5961                push_to_lsp_host_history,
 5962                cx,
 5963            )
 5964        });
 5965        Some(cx.spawn_in(window, async move |editor, cx| {
 5966            if let Some(transaction) = on_type_formatting.await? {
 5967                if push_to_client_history {
 5968                    buffer.update(cx, |buffer, _| {
 5969                        buffer.push_transaction(transaction, Instant::now());
 5970                        buffer.finalize_last_transaction();
 5971                    });
 5972                }
 5973                editor.update(cx, |editor, cx| {
 5974                    editor.refresh_document_highlights(cx);
 5975                })?;
 5976            }
 5977            Ok(())
 5978        }))
 5979    }
 5980
 5981    pub fn show_word_completions(
 5982        &mut self,
 5983        _: &ShowWordCompletions,
 5984        window: &mut Window,
 5985        cx: &mut Context<Self>,
 5986    ) {
 5987        self.open_or_update_completions_menu(
 5988            Some(CompletionsMenuSource::Words {
 5989                ignore_threshold: true,
 5990            }),
 5991            None,
 5992            false,
 5993            window,
 5994            cx,
 5995        );
 5996    }
 5997
 5998    pub fn show_completions(
 5999        &mut self,
 6000        _: &ShowCompletions,
 6001        window: &mut Window,
 6002        cx: &mut Context<Self>,
 6003    ) {
 6004        self.open_or_update_completions_menu(None, None, false, window, cx);
 6005    }
 6006
 6007    fn open_or_update_completions_menu(
 6008        &mut self,
 6009        requested_source: Option<CompletionsMenuSource>,
 6010        trigger: Option<String>,
 6011        trigger_in_words: bool,
 6012        window: &mut Window,
 6013        cx: &mut Context<Self>,
 6014    ) {
 6015        if self.pending_rename.is_some() {
 6016            return;
 6017        }
 6018
 6019        let completions_source = self
 6020            .context_menu
 6021            .borrow()
 6022            .as_ref()
 6023            .and_then(|menu| match menu {
 6024                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 6025                CodeContextMenu::CodeActions(_) => None,
 6026            });
 6027
 6028        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 6029
 6030        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 6031        // inserted and selected. To handle that case, the start of the selection is used so that
 6032        // the menu starts with all choices.
 6033        let position = self
 6034            .selections
 6035            .newest_anchor()
 6036            .start
 6037            .bias_right(&multibuffer_snapshot);
 6038        if position.diff_base_anchor.is_some() {
 6039            return;
 6040        }
 6041        let buffer_position = multibuffer_snapshot.anchor_before(position);
 6042        let Some(buffer) = buffer_position
 6043            .text_anchor
 6044            .buffer_id
 6045            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 6046        else {
 6047            return;
 6048        };
 6049        let buffer_snapshot = buffer.read(cx).snapshot();
 6050
 6051        let menu_is_open = matches!(
 6052            self.context_menu.borrow().as_ref(),
 6053            Some(CodeContextMenu::Completions(_))
 6054        );
 6055
 6056        let language = buffer_snapshot
 6057            .language_at(buffer_position.text_anchor)
 6058            .map(|language| language.name());
 6059
 6060        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 6061        let completion_settings = language_settings.completions.clone();
 6062
 6063        let show_completions_on_input = self
 6064            .show_completions_on_input_override
 6065            .unwrap_or(language_settings.show_completions_on_input);
 6066        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 6067            return;
 6068        }
 6069
 6070        let query: Option<Arc<String>> =
 6071            Self::completion_query(&multibuffer_snapshot, buffer_position)
 6072                .map(|query| query.into());
 6073
 6074        drop(multibuffer_snapshot);
 6075
 6076        // Hide the current completions menu when query is empty. Without this, cached
 6077        // completions from before the trigger char may be reused (#32774).
 6078        if query.is_none() && menu_is_open {
 6079            self.hide_context_menu(window, cx);
 6080        }
 6081
 6082        let mut ignore_word_threshold = false;
 6083        let provider = match requested_source {
 6084            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 6085            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 6086                ignore_word_threshold = ignore_threshold;
 6087                None
 6088            }
 6089            Some(CompletionsMenuSource::SnippetChoices)
 6090            | Some(CompletionsMenuSource::SnippetsOnly) => {
 6091                log::error!("bug: SnippetChoices requested_source is not handled");
 6092                None
 6093            }
 6094        };
 6095
 6096        let sort_completions = provider
 6097            .as_ref()
 6098            .is_some_and(|provider| provider.sort_completions());
 6099
 6100        let filter_completions = provider
 6101            .as_ref()
 6102            .is_none_or(|provider| provider.filter_completions());
 6103
 6104        let was_snippets_only = matches!(
 6105            completions_source,
 6106            Some(CompletionsMenuSource::SnippetsOnly)
 6107        );
 6108
 6109        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6110            if filter_completions {
 6111                menu.filter(
 6112                    query.clone().unwrap_or_default(),
 6113                    buffer_position.text_anchor,
 6114                    &buffer,
 6115                    provider.clone(),
 6116                    window,
 6117                    cx,
 6118                );
 6119            }
 6120            // When `is_incomplete` is false, no need to re-query completions when the current query
 6121            // is a suffix of the initial query.
 6122            let was_complete = !menu.is_incomplete;
 6123            if was_complete && !was_snippets_only {
 6124                // If the new query is a suffix of the old query (typing more characters) and
 6125                // the previous result was complete, the existing completions can be filtered.
 6126                //
 6127                // Note that snippet completions are always complete.
 6128                let query_matches = match (&menu.initial_query, &query) {
 6129                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6130                    (None, _) => true,
 6131                    _ => false,
 6132                };
 6133                if query_matches {
 6134                    let position_matches = if menu.initial_position == position {
 6135                        true
 6136                    } else {
 6137                        let snapshot = self.buffer.read(cx).read(cx);
 6138                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6139                    };
 6140                    if position_matches {
 6141                        return;
 6142                    }
 6143                }
 6144            }
 6145        };
 6146
 6147        let Anchor {
 6148            excerpt_id: buffer_excerpt_id,
 6149            text_anchor: buffer_position,
 6150            ..
 6151        } = buffer_position;
 6152
 6153        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6154            buffer_snapshot.surrounding_word(buffer_position, None)
 6155        {
 6156            let word_to_exclude = buffer_snapshot
 6157                .text_for_range(word_range.clone())
 6158                .collect::<String>();
 6159            (
 6160                buffer_snapshot.anchor_before(word_range.start)
 6161                    ..buffer_snapshot.anchor_after(buffer_position),
 6162                Some(word_to_exclude),
 6163            )
 6164        } else {
 6165            (buffer_position..buffer_position, None)
 6166        };
 6167
 6168        let show_completion_documentation = buffer_snapshot
 6169            .settings_at(buffer_position, cx)
 6170            .show_completion_documentation;
 6171
 6172        // The document can be large, so stay in reasonable bounds when searching for words,
 6173        // otherwise completion pop-up might be slow to appear.
 6174        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6175        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6176        let min_word_search = buffer_snapshot.clip_point(
 6177            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6178            Bias::Left,
 6179        );
 6180        let max_word_search = buffer_snapshot.clip_point(
 6181            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6182            Bias::Right,
 6183        );
 6184        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6185            ..buffer_snapshot.point_to_offset(max_word_search);
 6186
 6187        let skip_digits = query
 6188            .as_ref()
 6189            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6190
 6191        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6192            trigger.as_ref().is_none_or(|trigger| {
 6193                provider.is_completion_trigger(
 6194                    &buffer,
 6195                    position.text_anchor,
 6196                    trigger,
 6197                    trigger_in_words,
 6198                    cx,
 6199                )
 6200            })
 6201        });
 6202
 6203        let provider_responses = if let Some(provider) = &provider
 6204            && load_provider_completions
 6205        {
 6206            let trigger_character =
 6207                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6208            let completion_context = CompletionContext {
 6209                trigger_kind: match &trigger_character {
 6210                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6211                    None => CompletionTriggerKind::INVOKED,
 6212                },
 6213                trigger_character,
 6214            };
 6215
 6216            provider.completions(
 6217                buffer_excerpt_id,
 6218                &buffer,
 6219                buffer_position,
 6220                completion_context,
 6221                window,
 6222                cx,
 6223            )
 6224        } else {
 6225            Task::ready(Ok(Vec::new()))
 6226        };
 6227
 6228        let load_word_completions = if !self.word_completions_enabled {
 6229            false
 6230        } else if requested_source
 6231            == Some(CompletionsMenuSource::Words {
 6232                ignore_threshold: true,
 6233            })
 6234        {
 6235            true
 6236        } else {
 6237            load_provider_completions
 6238                && completion_settings.words != WordsCompletionMode::Disabled
 6239                && (ignore_word_threshold || {
 6240                    let words_min_length = completion_settings.words_min_length;
 6241                    // check whether word has at least `words_min_length` characters
 6242                    let query_chars = query.iter().flat_map(|q| q.chars());
 6243                    query_chars.take(words_min_length).count() == words_min_length
 6244                })
 6245        };
 6246
 6247        let mut words = if load_word_completions {
 6248            cx.background_spawn({
 6249                let buffer_snapshot = buffer_snapshot.clone();
 6250                async move {
 6251                    buffer_snapshot.words_in_range(WordsQuery {
 6252                        fuzzy_contents: None,
 6253                        range: word_search_range,
 6254                        skip_digits,
 6255                    })
 6256                }
 6257            })
 6258        } else {
 6259            Task::ready(BTreeMap::default())
 6260        };
 6261
 6262        let snippets = if let Some(provider) = &provider
 6263            && provider.show_snippets()
 6264            && let Some(project) = self.project()
 6265        {
 6266            let char_classifier = buffer_snapshot
 6267                .char_classifier_at(buffer_position)
 6268                .scope_context(Some(CharScopeContext::Completion));
 6269            project.update(cx, |project, cx| {
 6270                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6271            })
 6272        } else {
 6273            Task::ready(Ok(CompletionResponse {
 6274                completions: Vec::new(),
 6275                display_options: Default::default(),
 6276                is_incomplete: false,
 6277            }))
 6278        };
 6279
 6280        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6281
 6282        let id = post_inc(&mut self.next_completion_id);
 6283        let task = cx.spawn_in(window, async move |editor, cx| {
 6284            let Ok(()) = editor.update(cx, |this, _| {
 6285                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6286            }) else {
 6287                return;
 6288            };
 6289
 6290            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6291            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6292            let mut completions = Vec::new();
 6293            let mut is_incomplete = false;
 6294            let mut display_options: Option<CompletionDisplayOptions> = None;
 6295            if let Some(provider_responses) = provider_responses.await.log_err()
 6296                && !provider_responses.is_empty()
 6297            {
 6298                for response in provider_responses {
 6299                    completions.extend(response.completions);
 6300                    is_incomplete = is_incomplete || response.is_incomplete;
 6301                    match display_options.as_mut() {
 6302                        None => {
 6303                            display_options = Some(response.display_options);
 6304                        }
 6305                        Some(options) => options.merge(&response.display_options),
 6306                    }
 6307                }
 6308                if completion_settings.words == WordsCompletionMode::Fallback {
 6309                    words = Task::ready(BTreeMap::default());
 6310                }
 6311            }
 6312            let display_options = display_options.unwrap_or_default();
 6313
 6314            let mut words = words.await;
 6315            if let Some(word_to_exclude) = &word_to_exclude {
 6316                words.remove(word_to_exclude);
 6317            }
 6318            for lsp_completion in &completions {
 6319                words.remove(&lsp_completion.new_text);
 6320            }
 6321            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6322                replace_range: word_replace_range.clone(),
 6323                new_text: word.clone(),
 6324                label: CodeLabel::plain(word, None),
 6325                match_start: None,
 6326                snippet_deduplication_key: None,
 6327                icon_path: None,
 6328                documentation: None,
 6329                source: CompletionSource::BufferWord {
 6330                    word_range,
 6331                    resolved: false,
 6332                },
 6333                insert_text_mode: Some(InsertTextMode::AS_IS),
 6334                confirm: None,
 6335            }));
 6336
 6337            completions.extend(
 6338                snippets
 6339                    .await
 6340                    .into_iter()
 6341                    .flat_map(|response| response.completions),
 6342            );
 6343
 6344            let menu = if completions.is_empty() {
 6345                None
 6346            } else {
 6347                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6348                    let languages = editor
 6349                        .workspace
 6350                        .as_ref()
 6351                        .and_then(|(workspace, _)| workspace.upgrade())
 6352                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6353                    let menu = CompletionsMenu::new(
 6354                        id,
 6355                        requested_source.unwrap_or(if load_provider_completions {
 6356                            CompletionsMenuSource::Normal
 6357                        } else {
 6358                            CompletionsMenuSource::SnippetsOnly
 6359                        }),
 6360                        sort_completions,
 6361                        show_completion_documentation,
 6362                        position,
 6363                        query.clone(),
 6364                        is_incomplete,
 6365                        buffer.clone(),
 6366                        completions.into(),
 6367                        editor
 6368                            .context_menu()
 6369                            .borrow_mut()
 6370                            .as_ref()
 6371                            .map(|menu| menu.primary_scroll_handle()),
 6372                        display_options,
 6373                        snippet_sort_order,
 6374                        languages,
 6375                        language,
 6376                        cx,
 6377                    );
 6378
 6379                    let query = if filter_completions { query } else { None };
 6380                    let matches_task = menu.do_async_filtering(
 6381                        query.unwrap_or_default(),
 6382                        buffer_position,
 6383                        &buffer,
 6384                        cx,
 6385                    );
 6386                    (menu, matches_task)
 6387                }) else {
 6388                    return;
 6389                };
 6390
 6391                let matches = matches_task.await;
 6392
 6393                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6394                    // Newer menu already set, so exit.
 6395                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6396                        editor.context_menu.borrow().as_ref()
 6397                        && prev_menu.id > id
 6398                    {
 6399                        return;
 6400                    };
 6401
 6402                    // Only valid to take prev_menu because either the new menu is immediately set
 6403                    // below, or the menu is hidden.
 6404                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6405                        editor.context_menu.borrow_mut().take()
 6406                    {
 6407                        let position_matches =
 6408                            if prev_menu.initial_position == menu.initial_position {
 6409                                true
 6410                            } else {
 6411                                let snapshot = editor.buffer.read(cx).read(cx);
 6412                                prev_menu.initial_position.to_offset(&snapshot)
 6413                                    == menu.initial_position.to_offset(&snapshot)
 6414                            };
 6415                        if position_matches {
 6416                            // Preserve markdown cache before `set_filter_results` because it will
 6417                            // try to populate the documentation cache.
 6418                            menu.preserve_markdown_cache(prev_menu);
 6419                        }
 6420                    };
 6421
 6422                    menu.set_filter_results(matches, provider, window, cx);
 6423                }) else {
 6424                    return;
 6425                };
 6426
 6427                menu.visible().then_some(menu)
 6428            };
 6429
 6430            editor
 6431                .update_in(cx, |editor, window, cx| {
 6432                    if editor.focus_handle.is_focused(window)
 6433                        && let Some(menu) = menu
 6434                    {
 6435                        *editor.context_menu.borrow_mut() =
 6436                            Some(CodeContextMenu::Completions(menu));
 6437
 6438                        crate::hover_popover::hide_hover(editor, cx);
 6439                        if editor.show_edit_predictions_in_menu() {
 6440                            editor.update_visible_edit_prediction(window, cx);
 6441                        } else {
 6442                            editor
 6443                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6444                        }
 6445
 6446                        cx.notify();
 6447                        return;
 6448                    }
 6449
 6450                    if editor.completion_tasks.len() <= 1 {
 6451                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6452                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6453                        // If it was already hidden and we don't show edit predictions in the menu,
 6454                        // we should also show the edit prediction when available.
 6455                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6456                            editor.update_visible_edit_prediction(window, cx);
 6457                        }
 6458                    }
 6459                })
 6460                .ok();
 6461        });
 6462
 6463        self.completion_tasks.push((id, task));
 6464    }
 6465
 6466    #[cfg(any(test, feature = "test-support"))]
 6467    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6468        let menu = self.context_menu.borrow();
 6469        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6470            let completions = menu.completions.borrow();
 6471            Some(completions.to_vec())
 6472        } else {
 6473            None
 6474        }
 6475    }
 6476
 6477    pub fn with_completions_menu_matching_id<R>(
 6478        &self,
 6479        id: CompletionId,
 6480        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6481    ) -> R {
 6482        let mut context_menu = self.context_menu.borrow_mut();
 6483        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6484            return f(None);
 6485        };
 6486        if completions_menu.id != id {
 6487            return f(None);
 6488        }
 6489        f(Some(completions_menu))
 6490    }
 6491
 6492    pub fn confirm_completion(
 6493        &mut self,
 6494        action: &ConfirmCompletion,
 6495        window: &mut Window,
 6496        cx: &mut Context<Self>,
 6497    ) -> Option<Task<Result<()>>> {
 6498        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6499        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6500    }
 6501
 6502    pub fn confirm_completion_insert(
 6503        &mut self,
 6504        _: &ConfirmCompletionInsert,
 6505        window: &mut Window,
 6506        cx: &mut Context<Self>,
 6507    ) -> Option<Task<Result<()>>> {
 6508        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6509        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6510    }
 6511
 6512    pub fn confirm_completion_replace(
 6513        &mut self,
 6514        _: &ConfirmCompletionReplace,
 6515        window: &mut Window,
 6516        cx: &mut Context<Self>,
 6517    ) -> Option<Task<Result<()>>> {
 6518        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6519        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6520    }
 6521
 6522    pub fn compose_completion(
 6523        &mut self,
 6524        action: &ComposeCompletion,
 6525        window: &mut Window,
 6526        cx: &mut Context<Self>,
 6527    ) -> Option<Task<Result<()>>> {
 6528        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6529        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6530    }
 6531
 6532    fn do_completion(
 6533        &mut self,
 6534        item_ix: Option<usize>,
 6535        intent: CompletionIntent,
 6536        window: &mut Window,
 6537        cx: &mut Context<Editor>,
 6538    ) -> Option<Task<Result<()>>> {
 6539        use language::ToOffset as _;
 6540
 6541        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6542        else {
 6543            return None;
 6544        };
 6545
 6546        let candidate_id = {
 6547            let entries = completions_menu.entries.borrow();
 6548            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6549            if self.show_edit_predictions_in_menu() {
 6550                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6551            }
 6552            mat.candidate_id
 6553        };
 6554
 6555        let completion = completions_menu
 6556            .completions
 6557            .borrow()
 6558            .get(candidate_id)?
 6559            .clone();
 6560        cx.stop_propagation();
 6561
 6562        let buffer_handle = completions_menu.buffer.clone();
 6563
 6564        let CompletionEdit {
 6565            new_text,
 6566            snippet,
 6567            replace_range,
 6568        } = process_completion_for_edit(
 6569            &completion,
 6570            intent,
 6571            &buffer_handle,
 6572            &completions_menu.initial_position.text_anchor,
 6573            cx,
 6574        );
 6575
 6576        let buffer = buffer_handle.read(cx);
 6577        let snapshot = self.buffer.read(cx).snapshot(cx);
 6578        let newest_anchor = self.selections.newest_anchor();
 6579        let replace_range_multibuffer = {
 6580            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6581            excerpt.map_range_from_buffer(replace_range.clone())
 6582        };
 6583        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6584            return None;
 6585        }
 6586
 6587        let old_text = buffer
 6588            .text_for_range(replace_range.clone())
 6589            .collect::<String>();
 6590        let lookbehind = newest_anchor
 6591            .start
 6592            .text_anchor
 6593            .to_offset(buffer)
 6594            .saturating_sub(replace_range.start.0);
 6595        let lookahead = replace_range
 6596            .end
 6597            .0
 6598            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6599        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6600        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6601
 6602        let selections = self
 6603            .selections
 6604            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6605        let mut ranges = Vec::new();
 6606        let mut all_commit_ranges = Vec::new();
 6607        let mut linked_edits = LinkedEdits::new();
 6608
 6609        let text: Arc<str> = new_text.clone().into();
 6610        for selection in &selections {
 6611            let range = if selection.id == newest_anchor.id {
 6612                replace_range_multibuffer.clone()
 6613            } else {
 6614                let mut range = selection.range();
 6615
 6616                // if prefix is present, don't duplicate it
 6617                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6618                    range.start = range.start.saturating_sub_usize(lookbehind);
 6619
 6620                    // if suffix is also present, mimic the newest cursor and replace it
 6621                    if selection.id != newest_anchor.id
 6622                        && snapshot.contains_str_at(range.end, suffix)
 6623                    {
 6624                        range.end += lookahead;
 6625                    }
 6626                }
 6627                range
 6628            };
 6629
 6630            ranges.push(range.clone());
 6631
 6632            let start_anchor = snapshot.anchor_before(range.start);
 6633            let end_anchor = snapshot.anchor_after(range.end);
 6634            let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6635            all_commit_ranges.push(anchor_range.clone());
 6636
 6637            if !self.linked_edit_ranges.is_empty() {
 6638                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6639            }
 6640        }
 6641
 6642        let common_prefix_len = old_text
 6643            .chars()
 6644            .zip(new_text.chars())
 6645            .take_while(|(a, b)| a == b)
 6646            .map(|(a, _)| a.len_utf8())
 6647            .sum::<usize>();
 6648
 6649        cx.emit(EditorEvent::InputHandled {
 6650            utf16_range_to_replace: None,
 6651            text: new_text[common_prefix_len..].into(),
 6652        });
 6653
 6654        self.transact(window, cx, |editor, window, cx| {
 6655            if let Some(mut snippet) = snippet {
 6656                snippet.text = new_text.to_string();
 6657                editor
 6658                    .insert_snippet(&ranges, snippet, window, cx)
 6659                    .log_err();
 6660            } else {
 6661                editor.buffer.update(cx, |multi_buffer, cx| {
 6662                    let auto_indent = match completion.insert_text_mode {
 6663                        Some(InsertTextMode::AS_IS) => None,
 6664                        _ => editor.autoindent_mode.clone(),
 6665                    };
 6666                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6667                    multi_buffer.edit(edits, auto_indent, cx);
 6668                });
 6669            }
 6670            linked_edits.apply(cx);
 6671            editor.refresh_edit_prediction(true, false, window, cx);
 6672        });
 6673        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6674
 6675        let show_new_completions_on_confirm = completion
 6676            .confirm
 6677            .as_ref()
 6678            .is_some_and(|confirm| confirm(intent, window, cx));
 6679        if show_new_completions_on_confirm {
 6680            self.open_or_update_completions_menu(None, None, false, window, cx);
 6681        }
 6682
 6683        let provider = self.completion_provider.as_ref()?;
 6684
 6685        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6686        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6687            let CompletionSource::Lsp {
 6688                lsp_completion,
 6689                server_id,
 6690                ..
 6691            } = &completion.source
 6692            else {
 6693                return None;
 6694            };
 6695            let lsp_command = lsp_completion.command.as_ref()?;
 6696            let available_commands = lsp_store
 6697                .read(cx)
 6698                .lsp_server_capabilities
 6699                .get(server_id)
 6700                .and_then(|server_capabilities| {
 6701                    server_capabilities
 6702                        .execute_command_provider
 6703                        .as_ref()
 6704                        .map(|options| options.commands.as_slice())
 6705                })?;
 6706            if available_commands.contains(&lsp_command.command) {
 6707                Some(CodeAction {
 6708                    server_id: *server_id,
 6709                    range: language::Anchor::MIN..language::Anchor::MIN,
 6710                    lsp_action: LspAction::Command(lsp_command.clone()),
 6711                    resolved: false,
 6712                })
 6713            } else {
 6714                None
 6715            }
 6716        });
 6717
 6718        drop(completion);
 6719        let apply_edits = provider.apply_additional_edits_for_completion(
 6720            buffer_handle.clone(),
 6721            completions_menu.completions.clone(),
 6722            candidate_id,
 6723            true,
 6724            all_commit_ranges,
 6725            cx,
 6726        );
 6727
 6728        let editor_settings = EditorSettings::get_global(cx);
 6729        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6730            // After the code completion is finished, users often want to know what signatures are needed.
 6731            // so we should automatically call signature_help
 6732            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6733        }
 6734
 6735        Some(cx.spawn_in(window, async move |editor, cx| {
 6736            apply_edits.await?;
 6737
 6738            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6739                let title = command.lsp_action.title().to_owned();
 6740                let project_transaction = lsp_store
 6741                    .update(cx, |lsp_store, cx| {
 6742                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6743                    })
 6744                    .await
 6745                    .context("applying post-completion command")?;
 6746                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6747                    Self::open_project_transaction(
 6748                        &editor,
 6749                        workspace.downgrade(),
 6750                        project_transaction,
 6751                        title,
 6752                        cx,
 6753                    )
 6754                    .await?;
 6755                }
 6756            }
 6757
 6758            Ok(())
 6759        }))
 6760    }
 6761
 6762    pub fn toggle_code_actions(
 6763        &mut self,
 6764        action: &ToggleCodeActions,
 6765        window: &mut Window,
 6766        cx: &mut Context<Self>,
 6767    ) {
 6768        let quick_launch = action.quick_launch;
 6769        let mut context_menu = self.context_menu.borrow_mut();
 6770        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6771            if code_actions.deployed_from == action.deployed_from {
 6772                // Toggle if we're selecting the same one
 6773                *context_menu = None;
 6774                cx.notify();
 6775                return;
 6776            } else {
 6777                // Otherwise, clear it and start a new one
 6778                *context_menu = None;
 6779                cx.notify();
 6780            }
 6781        }
 6782        drop(context_menu);
 6783        let snapshot = self.snapshot(window, cx);
 6784        let deployed_from = action.deployed_from.clone();
 6785        let action = action.clone();
 6786        self.completion_tasks.clear();
 6787        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6788
 6789        let multibuffer_point = match &action.deployed_from {
 6790            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6791                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6792            }
 6793            _ => self
 6794                .selections
 6795                .newest::<Point>(&snapshot.display_snapshot)
 6796                .head(),
 6797        };
 6798        let Some((buffer, buffer_row)) = snapshot
 6799            .buffer_snapshot()
 6800            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6801            .and_then(|(buffer_snapshot, range)| {
 6802                self.buffer()
 6803                    .read(cx)
 6804                    .buffer(buffer_snapshot.remote_id())
 6805                    .map(|buffer| (buffer, range.start.row))
 6806            })
 6807        else {
 6808            return;
 6809        };
 6810        let buffer_id = buffer.read(cx).remote_id();
 6811        let tasks = self
 6812            .runnables
 6813            .runnables((buffer_id, buffer_row))
 6814            .map(|t| Arc::new(t.to_owned()));
 6815
 6816        if !self.focus_handle.is_focused(window) {
 6817            return;
 6818        }
 6819        let project = self.project.clone();
 6820
 6821        let code_actions_task = match deployed_from {
 6822            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6823            _ => self.code_actions(buffer_row, window, cx),
 6824        };
 6825
 6826        let runnable_task = match deployed_from {
 6827            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6828            _ => {
 6829                let mut task_context_task = Task::ready(None);
 6830                if let Some(tasks) = &tasks
 6831                    && let Some(project) = project
 6832                {
 6833                    task_context_task =
 6834                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6835                }
 6836
 6837                cx.spawn_in(window, {
 6838                    let buffer = buffer.clone();
 6839                    async move |editor, cx| {
 6840                        let task_context = task_context_task.await;
 6841
 6842                        let resolved_tasks =
 6843                            tasks
 6844                                .zip(task_context.clone())
 6845                                .map(|(tasks, task_context)| ResolvedTasks {
 6846                                    templates: tasks.resolve(&task_context).collect(),
 6847                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6848                                        multibuffer_point.row,
 6849                                        tasks.column,
 6850                                    )),
 6851                                });
 6852                        let debug_scenarios = editor
 6853                            .update(cx, |editor, cx| {
 6854                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6855                            })?
 6856                            .await;
 6857                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6858                    }
 6859                })
 6860            }
 6861        };
 6862
 6863        cx.spawn_in(window, async move |editor, cx| {
 6864            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6865            let code_actions = code_actions_task.await;
 6866            let spawn_straight_away = quick_launch
 6867                && resolved_tasks
 6868                    .as_ref()
 6869                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6870                && code_actions
 6871                    .as_ref()
 6872                    .is_none_or(|actions| actions.is_empty())
 6873                && debug_scenarios.is_empty();
 6874
 6875            editor.update_in(cx, |editor, window, cx| {
 6876                crate::hover_popover::hide_hover(editor, cx);
 6877                let actions = CodeActionContents::new(
 6878                    resolved_tasks,
 6879                    code_actions,
 6880                    debug_scenarios,
 6881                    task_context.unwrap_or_default(),
 6882                );
 6883
 6884                // Don't show the menu if there are no actions available
 6885                if actions.is_empty() {
 6886                    cx.notify();
 6887                    return Task::ready(Ok(()));
 6888                }
 6889
 6890                *editor.context_menu.borrow_mut() =
 6891                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6892                        buffer,
 6893                        actions,
 6894                        selected_item: Default::default(),
 6895                        scroll_handle: UniformListScrollHandle::default(),
 6896                        deployed_from,
 6897                    }));
 6898                cx.notify();
 6899                if spawn_straight_away
 6900                    && let Some(task) = editor.confirm_code_action(
 6901                        &ConfirmCodeAction { item_ix: Some(0) },
 6902                        window,
 6903                        cx,
 6904                    )
 6905                {
 6906                    return task;
 6907                }
 6908
 6909                Task::ready(Ok(()))
 6910            })
 6911        })
 6912        .detach_and_log_err(cx);
 6913    }
 6914
 6915    fn debug_scenarios(
 6916        &mut self,
 6917        resolved_tasks: &Option<ResolvedTasks>,
 6918        buffer: &Entity<Buffer>,
 6919        cx: &mut App,
 6920    ) -> Task<Vec<task::DebugScenario>> {
 6921        maybe!({
 6922            let project = self.project()?;
 6923            let dap_store = project.read(cx).dap_store();
 6924            let mut scenarios = vec![];
 6925            let resolved_tasks = resolved_tasks.as_ref()?;
 6926            let buffer = buffer.read(cx);
 6927            let language = buffer.language()?;
 6928            let file = buffer.file();
 6929            let debug_adapter = language_settings(language.name().into(), file, cx)
 6930                .debuggers
 6931                .first()
 6932                .map(SharedString::from)
 6933                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6934
 6935            dap_store.update(cx, |dap_store, cx| {
 6936                for (_, task) in &resolved_tasks.templates {
 6937                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6938                        task.original_task().clone(),
 6939                        debug_adapter.clone().into(),
 6940                        task.display_label().to_owned().into(),
 6941                        cx,
 6942                    );
 6943                    scenarios.push(maybe_scenario);
 6944                }
 6945            });
 6946            Some(cx.background_spawn(async move {
 6947                futures::future::join_all(scenarios)
 6948                    .await
 6949                    .into_iter()
 6950                    .flatten()
 6951                    .collect::<Vec<_>>()
 6952            }))
 6953        })
 6954        .unwrap_or_else(|| Task::ready(vec![]))
 6955    }
 6956
 6957    fn code_actions(
 6958        &mut self,
 6959        buffer_row: u32,
 6960        window: &mut Window,
 6961        cx: &mut Context<Self>,
 6962    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6963        let mut task = self.code_actions_task.take();
 6964        cx.spawn_in(window, async move |editor, cx| {
 6965            while let Some(prev_task) = task {
 6966                prev_task.await.log_err();
 6967                task = editor
 6968                    .update(cx, |this, _| this.code_actions_task.take())
 6969                    .ok()?;
 6970            }
 6971
 6972            editor
 6973                .update(cx, |editor, cx| {
 6974                    editor
 6975                        .available_code_actions
 6976                        .clone()
 6977                        .and_then(|(location, code_actions)| {
 6978                            let snapshot = location.buffer.read(cx).snapshot();
 6979                            let point_range = location.range.to_point(&snapshot);
 6980                            let point_range = point_range.start.row..=point_range.end.row;
 6981                            if point_range.contains(&buffer_row) {
 6982                                Some(code_actions)
 6983                            } else {
 6984                                None
 6985                            }
 6986                        })
 6987                })
 6988                .ok()
 6989                .flatten()
 6990        })
 6991    }
 6992
 6993    pub fn confirm_code_action(
 6994        &mut self,
 6995        action: &ConfirmCodeAction,
 6996        window: &mut Window,
 6997        cx: &mut Context<Self>,
 6998    ) -> Option<Task<Result<()>>> {
 6999        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 7000
 7001        let actions_menu =
 7002            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 7003                menu
 7004            } else {
 7005                return None;
 7006            };
 7007
 7008        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 7009        let action = actions_menu.actions.get(action_ix)?;
 7010        let title = action.label();
 7011        let buffer = actions_menu.buffer;
 7012        let workspace = self.workspace()?;
 7013
 7014        match action {
 7015            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 7016                workspace.update(cx, |workspace, cx| {
 7017                    workspace.schedule_resolved_task(
 7018                        task_source_kind,
 7019                        resolved_task,
 7020                        false,
 7021                        window,
 7022                        cx,
 7023                    );
 7024
 7025                    Some(Task::ready(Ok(())))
 7026                })
 7027            }
 7028            CodeActionsItem::CodeAction {
 7029                excerpt_id,
 7030                action,
 7031                provider,
 7032            } => {
 7033                let apply_code_action =
 7034                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 7035                let workspace = workspace.downgrade();
 7036                Some(cx.spawn_in(window, async move |editor, cx| {
 7037                    let project_transaction = apply_code_action.await?;
 7038                    Self::open_project_transaction(
 7039                        &editor,
 7040                        workspace,
 7041                        project_transaction,
 7042                        title,
 7043                        cx,
 7044                    )
 7045                    .await
 7046                }))
 7047            }
 7048            CodeActionsItem::DebugScenario(scenario) => {
 7049                let context = actions_menu.actions.context.into();
 7050
 7051                workspace.update(cx, |workspace, cx| {
 7052                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 7053                    workspace.start_debug_session(
 7054                        scenario,
 7055                        context,
 7056                        Some(buffer),
 7057                        None,
 7058                        window,
 7059                        cx,
 7060                    );
 7061                });
 7062                Some(Task::ready(Ok(())))
 7063            }
 7064        }
 7065    }
 7066
 7067    fn open_transaction_for_hidden_buffers(
 7068        workspace: Entity<Workspace>,
 7069        transaction: ProjectTransaction,
 7070        title: String,
 7071        window: &mut Window,
 7072        cx: &mut Context<Self>,
 7073    ) {
 7074        if transaction.0.is_empty() {
 7075            return;
 7076        }
 7077
 7078        let edited_buffers_already_open = {
 7079            let other_editors: Vec<Entity<Editor>> = workspace
 7080                .read(cx)
 7081                .panes()
 7082                .iter()
 7083                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7084                .filter(|editor| editor.entity_id() != cx.entity_id())
 7085                .collect();
 7086
 7087            transaction.0.keys().all(|buffer| {
 7088                other_editors.iter().any(|editor| {
 7089                    let multi_buffer = editor.read(cx).buffer();
 7090                    multi_buffer.read(cx).is_singleton()
 7091                        && multi_buffer
 7092                            .read(cx)
 7093                            .as_singleton()
 7094                            .map_or(false, |singleton| {
 7095                                singleton.entity_id() == buffer.entity_id()
 7096                            })
 7097                })
 7098            })
 7099        };
 7100        if !edited_buffers_already_open {
 7101            let workspace = workspace.downgrade();
 7102            cx.defer_in(window, move |_, window, cx| {
 7103                cx.spawn_in(window, async move |editor, cx| {
 7104                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7105                        .await
 7106                        .ok()
 7107                })
 7108                .detach();
 7109            });
 7110        }
 7111    }
 7112
 7113    pub async fn open_project_transaction(
 7114        editor: &WeakEntity<Editor>,
 7115        workspace: WeakEntity<Workspace>,
 7116        transaction: ProjectTransaction,
 7117        title: String,
 7118        cx: &mut AsyncWindowContext,
 7119    ) -> Result<()> {
 7120        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7121        cx.update(|_, cx| {
 7122            entries.sort_unstable_by_key(|(buffer, _)| {
 7123                buffer.read(cx).file().map(|f| f.path().clone())
 7124            });
 7125        })?;
 7126        if entries.is_empty() {
 7127            return Ok(());
 7128        }
 7129
 7130        // If the project transaction's edits are all contained within this editor, then
 7131        // avoid opening a new editor to display them.
 7132
 7133        if let [(buffer, transaction)] = &*entries {
 7134            let excerpt = editor.update(cx, |editor, cx| {
 7135                editor
 7136                    .buffer()
 7137                    .read(cx)
 7138                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7139            })?;
 7140            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7141                && excerpted_buffer == *buffer
 7142            {
 7143                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7144                    let excerpt_range = excerpt_range.to_offset(buffer);
 7145                    buffer
 7146                        .edited_ranges_for_transaction::<usize>(transaction)
 7147                        .all(|range| {
 7148                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7149                        })
 7150                });
 7151
 7152                if all_edits_within_excerpt {
 7153                    return Ok(());
 7154                }
 7155            }
 7156        }
 7157
 7158        let mut ranges_to_highlight = Vec::new();
 7159        let excerpt_buffer = cx.new(|cx| {
 7160            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7161            for (buffer_handle, transaction) in &entries {
 7162                let edited_ranges = buffer_handle
 7163                    .read(cx)
 7164                    .edited_ranges_for_transaction::<Point>(transaction)
 7165                    .collect::<Vec<_>>();
 7166                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7167                    PathKey::for_buffer(buffer_handle, cx),
 7168                    buffer_handle.clone(),
 7169                    edited_ranges,
 7170                    multibuffer_context_lines(cx),
 7171                    cx,
 7172                );
 7173
 7174                ranges_to_highlight.extend(ranges);
 7175            }
 7176            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7177            multibuffer
 7178        });
 7179
 7180        workspace.update_in(cx, |workspace, window, cx| {
 7181            let project = workspace.project().clone();
 7182            let editor =
 7183                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7184            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7185            editor.update(cx, |editor, cx| {
 7186                editor.highlight_background(
 7187                    HighlightKey::Editor,
 7188                    &ranges_to_highlight,
 7189                    |_, theme| theme.colors().editor_highlighted_line_background,
 7190                    cx,
 7191                );
 7192            });
 7193        })?;
 7194
 7195        Ok(())
 7196    }
 7197
 7198    pub fn clear_code_action_providers(&mut self) {
 7199        self.code_action_providers.clear();
 7200        self.available_code_actions.take();
 7201    }
 7202
 7203    pub fn add_code_action_provider(
 7204        &mut self,
 7205        provider: Rc<dyn CodeActionProvider>,
 7206        window: &mut Window,
 7207        cx: &mut Context<Self>,
 7208    ) {
 7209        if self
 7210            .code_action_providers
 7211            .iter()
 7212            .any(|existing_provider| existing_provider.id() == provider.id())
 7213        {
 7214            return;
 7215        }
 7216
 7217        self.code_action_providers.push(provider);
 7218        self.refresh_code_actions(window, cx);
 7219    }
 7220
 7221    pub fn remove_code_action_provider(
 7222        &mut self,
 7223        id: Arc<str>,
 7224        window: &mut Window,
 7225        cx: &mut Context<Self>,
 7226    ) {
 7227        self.code_action_providers
 7228            .retain(|provider| provider.id() != id);
 7229        self.refresh_code_actions(window, cx);
 7230    }
 7231
 7232    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7233        !self.code_action_providers.is_empty()
 7234            && EditorSettings::get_global(cx).toolbar.code_actions
 7235    }
 7236
 7237    pub fn has_available_code_actions(&self) -> bool {
 7238        self.available_code_actions
 7239            .as_ref()
 7240            .is_some_and(|(_, actions)| !actions.is_empty())
 7241    }
 7242
 7243    fn render_inline_code_actions(
 7244        &self,
 7245        icon_size: ui::IconSize,
 7246        display_row: DisplayRow,
 7247        is_active: bool,
 7248        cx: &mut Context<Self>,
 7249    ) -> AnyElement {
 7250        let show_tooltip = !self.context_menu_visible();
 7251        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7252            .icon_size(icon_size)
 7253            .shape(ui::IconButtonShape::Square)
 7254            .icon_color(ui::Color::Hidden)
 7255            .toggle_state(is_active)
 7256            .when(show_tooltip, |this| {
 7257                this.tooltip({
 7258                    let focus_handle = self.focus_handle.clone();
 7259                    move |_window, cx| {
 7260                        Tooltip::for_action_in(
 7261                            "Toggle Code Actions",
 7262                            &ToggleCodeActions {
 7263                                deployed_from: None,
 7264                                quick_launch: false,
 7265                            },
 7266                            &focus_handle,
 7267                            cx,
 7268                        )
 7269                    }
 7270                })
 7271            })
 7272            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7273                window.focus(&editor.focus_handle(cx), cx);
 7274                editor.toggle_code_actions(
 7275                    &crate::actions::ToggleCodeActions {
 7276                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7277                            display_row,
 7278                        )),
 7279                        quick_launch: false,
 7280                    },
 7281                    window,
 7282                    cx,
 7283                );
 7284            }))
 7285            .into_any_element()
 7286    }
 7287
 7288    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7289        &self.context_menu
 7290    }
 7291
 7292    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7293        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7294            cx.background_executor()
 7295                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7296                .await;
 7297
 7298            let (start_buffer, start, _, end, newest_selection) = this
 7299                .update(cx, |this, cx| {
 7300                    let newest_selection = this.selections.newest_anchor().clone();
 7301                    if newest_selection.head().diff_base_anchor.is_some() {
 7302                        return None;
 7303                    }
 7304                    let display_snapshot = this.display_snapshot(cx);
 7305                    let newest_selection_adjusted =
 7306                        this.selections.newest_adjusted(&display_snapshot);
 7307                    let buffer = this.buffer.read(cx);
 7308
 7309                    let (start_buffer, start) =
 7310                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7311                    let (end_buffer, end) =
 7312                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7313
 7314                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7315                })?
 7316                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7317                .context(
 7318                    "Expected selection to lie in a single buffer when refreshing code actions",
 7319                )?;
 7320            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7321                let providers = this.code_action_providers.clone();
 7322                let tasks = this
 7323                    .code_action_providers
 7324                    .iter()
 7325                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7326                    .collect::<Vec<_>>();
 7327                (providers, tasks)
 7328            })?;
 7329
 7330            let mut actions = Vec::new();
 7331            for (provider, provider_actions) in
 7332                providers.into_iter().zip(future::join_all(tasks).await)
 7333            {
 7334                if let Some(provider_actions) = provider_actions.log_err() {
 7335                    actions.extend(provider_actions.into_iter().map(|action| {
 7336                        AvailableCodeAction {
 7337                            excerpt_id: newest_selection.start.excerpt_id,
 7338                            action,
 7339                            provider: provider.clone(),
 7340                        }
 7341                    }));
 7342                }
 7343            }
 7344
 7345            this.update(cx, |this, cx| {
 7346                this.available_code_actions = if actions.is_empty() {
 7347                    None
 7348                } else {
 7349                    Some((
 7350                        Location {
 7351                            buffer: start_buffer,
 7352                            range: start..end,
 7353                        },
 7354                        actions.into(),
 7355                    ))
 7356                };
 7357                cx.notify();
 7358            })
 7359        }));
 7360    }
 7361
 7362    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7363        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7364            self.show_git_blame_inline = false;
 7365
 7366            self.show_git_blame_inline_delay_task =
 7367                Some(cx.spawn_in(window, async move |this, cx| {
 7368                    cx.background_executor().timer(delay).await;
 7369
 7370                    this.update(cx, |this, cx| {
 7371                        this.show_git_blame_inline = true;
 7372                        cx.notify();
 7373                    })
 7374                    .log_err();
 7375                }));
 7376        }
 7377    }
 7378
 7379    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7380        let snapshot = self.snapshot(window, cx);
 7381        let cursor = self
 7382            .selections
 7383            .newest::<Point>(&snapshot.display_snapshot)
 7384            .head();
 7385        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7386        else {
 7387            return;
 7388        };
 7389
 7390        if self.blame.is_none() {
 7391            self.start_git_blame(true, window, cx);
 7392        }
 7393        let Some(blame) = self.blame.as_ref() else {
 7394            return;
 7395        };
 7396
 7397        let row_info = RowInfo {
 7398            buffer_id: Some(buffer.remote_id()),
 7399            buffer_row: Some(point.row),
 7400            ..Default::default()
 7401        };
 7402        let Some((buffer, blame_entry)) = blame
 7403            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7404            .flatten()
 7405        else {
 7406            return;
 7407        };
 7408
 7409        let anchor = self.selections.newest_anchor().head();
 7410        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7411        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7412            self.show_blame_popover(
 7413                buffer,
 7414                &blame_entry,
 7415                position + last_bounds.origin,
 7416                true,
 7417                cx,
 7418            );
 7419        };
 7420    }
 7421
 7422    fn show_blame_popover(
 7423        &mut self,
 7424        buffer: BufferId,
 7425        blame_entry: &BlameEntry,
 7426        position: gpui::Point<Pixels>,
 7427        ignore_timeout: bool,
 7428        cx: &mut Context<Self>,
 7429    ) {
 7430        if let Some(state) = &mut self.inline_blame_popover {
 7431            state.hide_task.take();
 7432        } else {
 7433            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7434            let blame_entry = blame_entry.clone();
 7435            let show_task = cx.spawn(async move |editor, cx| {
 7436                if !ignore_timeout {
 7437                    cx.background_executor()
 7438                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7439                        .await;
 7440                }
 7441                editor
 7442                    .update(cx, |editor, cx| {
 7443                        editor.inline_blame_popover_show_task.take();
 7444                        let Some(blame) = editor.blame.as_ref() else {
 7445                            return;
 7446                        };
 7447                        let blame = blame.read(cx);
 7448                        let details = blame.details_for_entry(buffer, &blame_entry);
 7449                        let markdown = cx.new(|cx| {
 7450                            Markdown::new(
 7451                                details
 7452                                    .as_ref()
 7453                                    .map(|message| message.message.clone())
 7454                                    .unwrap_or_default(),
 7455                                None,
 7456                                None,
 7457                                cx,
 7458                            )
 7459                        });
 7460                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7461                            position,
 7462                            hide_task: None,
 7463                            popover_bounds: None,
 7464                            popover_state: InlineBlamePopoverState {
 7465                                scroll_handle: ScrollHandle::new(),
 7466                                commit_message: details,
 7467                                markdown,
 7468                            },
 7469                            keyboard_grace: ignore_timeout,
 7470                        });
 7471                        cx.notify();
 7472                    })
 7473                    .ok();
 7474            });
 7475            self.inline_blame_popover_show_task = Some(show_task);
 7476        }
 7477    }
 7478
 7479    pub fn has_mouse_context_menu(&self) -> bool {
 7480        self.mouse_context_menu.is_some()
 7481    }
 7482
 7483    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7484        self.inline_blame_popover_show_task.take();
 7485        if let Some(state) = &mut self.inline_blame_popover {
 7486            let hide_task = cx.spawn(async move |editor, cx| {
 7487                if !ignore_timeout {
 7488                    cx.background_executor()
 7489                        .timer(std::time::Duration::from_millis(100))
 7490                        .await;
 7491                }
 7492                editor
 7493                    .update(cx, |editor, cx| {
 7494                        editor.inline_blame_popover.take();
 7495                        cx.notify();
 7496                    })
 7497                    .ok();
 7498            });
 7499            state.hide_task = Some(hide_task);
 7500            true
 7501        } else {
 7502            false
 7503        }
 7504    }
 7505
 7506    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7507        if self.pending_rename.is_some() {
 7508            return None;
 7509        }
 7510
 7511        let provider = self.semantics_provider.clone()?;
 7512        let buffer = self.buffer.read(cx);
 7513        let newest_selection = self.selections.newest_anchor().clone();
 7514        let cursor_position = newest_selection.head();
 7515        let (cursor_buffer, cursor_buffer_position) =
 7516            buffer.text_anchor_for_position(cursor_position, cx)?;
 7517        let (tail_buffer, tail_buffer_position) =
 7518            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7519        if cursor_buffer != tail_buffer {
 7520            return None;
 7521        }
 7522
 7523        let snapshot = cursor_buffer.read(cx).snapshot();
 7524        let word_ranges = cx.background_spawn(async move {
 7525            // this might look odd to put on the background thread, but
 7526            // `surrounding_word` can be quite expensive as it calls into
 7527            // tree-sitter language scopes
 7528            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7529            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7530            (start_word_range, end_word_range)
 7531        });
 7532
 7533        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7534        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7535            let (start_word_range, end_word_range) = word_ranges.await;
 7536            if start_word_range != end_word_range {
 7537                this.update(cx, |this, cx| {
 7538                    this.document_highlights_task.take();
 7539                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7540                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7541                })
 7542                .ok();
 7543                return;
 7544            }
 7545            cx.background_executor()
 7546                .timer(Duration::from_millis(debounce))
 7547                .await;
 7548
 7549            let highlights = if let Some(highlights) = cx.update(|cx| {
 7550                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7551            }) {
 7552                highlights.await.log_err()
 7553            } else {
 7554                None
 7555            };
 7556
 7557            if let Some(highlights) = highlights {
 7558                this.update(cx, |this, cx| {
 7559                    if this.pending_rename.is_some() {
 7560                        return;
 7561                    }
 7562
 7563                    let buffer = this.buffer.read(cx);
 7564                    if buffer
 7565                        .text_anchor_for_position(cursor_position, cx)
 7566                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7567                    {
 7568                        return;
 7569                    }
 7570
 7571                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7572                    let mut write_ranges = Vec::new();
 7573                    let mut read_ranges = Vec::new();
 7574                    for highlight in highlights {
 7575                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7576                        for (excerpt_id, _, excerpt_range) in
 7577                            buffer.excerpts_for_buffer(buffer_id, cx)
 7578                        {
 7579                            let start = highlight
 7580                                .range
 7581                                .start
 7582                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7583                            let end = highlight
 7584                                .range
 7585                                .end
 7586                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7587                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7588                                continue;
 7589                            }
 7590
 7591                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7592                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7593                                write_ranges.push(range);
 7594                            } else {
 7595                                read_ranges.push(range);
 7596                            }
 7597                        }
 7598                    }
 7599
 7600                    this.highlight_background(
 7601                        HighlightKey::DocumentHighlightRead,
 7602                        &read_ranges,
 7603                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7604                        cx,
 7605                    );
 7606                    this.highlight_background(
 7607                        HighlightKey::DocumentHighlightWrite,
 7608                        &write_ranges,
 7609                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7610                        cx,
 7611                    );
 7612                    cx.notify();
 7613                })
 7614                .log_err();
 7615            }
 7616        }));
 7617        None
 7618    }
 7619
 7620    fn prepare_highlight_query_from_selection(
 7621        &mut self,
 7622        snapshot: &DisplaySnapshot,
 7623        cx: &mut Context<Editor>,
 7624    ) -> Option<(String, Range<Anchor>)> {
 7625        if matches!(self.mode, EditorMode::SingleLine) {
 7626            return None;
 7627        }
 7628        if !EditorSettings::get_global(cx).selection_highlight {
 7629            return None;
 7630        }
 7631        if self.selections.count() != 1 || self.selections.line_mode() {
 7632            return None;
 7633        }
 7634        let selection = self.selections.newest::<Point>(&snapshot);
 7635        // If the selection spans multiple rows OR it is empty
 7636        if selection.start.row != selection.end.row
 7637            || selection.start.column == selection.end.column
 7638        {
 7639            return None;
 7640        }
 7641        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7642        let query = snapshot
 7643            .buffer_snapshot()
 7644            .text_for_range(selection_anchor_range.clone())
 7645            .collect::<String>();
 7646        if query.trim().is_empty() {
 7647            return None;
 7648        }
 7649        Some((query, selection_anchor_range))
 7650    }
 7651
 7652    #[ztracing::instrument(skip_all)]
 7653    fn update_selection_occurrence_highlights(
 7654        &mut self,
 7655        multi_buffer_snapshot: MultiBufferSnapshot,
 7656        query_text: String,
 7657        query_range: Range<Anchor>,
 7658        multi_buffer_range_to_query: Range<Point>,
 7659        use_debounce: bool,
 7660        window: &mut Window,
 7661        cx: &mut Context<Editor>,
 7662    ) -> Task<()> {
 7663        cx.spawn_in(window, async move |editor, cx| {
 7664            if use_debounce {
 7665                cx.background_executor()
 7666                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7667                    .await;
 7668            }
 7669            let match_task = cx.background_spawn(async move {
 7670                let buffer_ranges = multi_buffer_snapshot
 7671                    .range_to_buffer_ranges(
 7672                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7673                    )
 7674                    .into_iter()
 7675                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7676                let mut match_ranges = Vec::new();
 7677                let Ok(regex) = project::search::SearchQuery::text(
 7678                    query_text,
 7679                    false,
 7680                    false,
 7681                    false,
 7682                    Default::default(),
 7683                    Default::default(),
 7684                    false,
 7685                    None,
 7686                ) else {
 7687                    return Vec::default();
 7688                };
 7689                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7690                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7691                    match_ranges.extend(
 7692                        regex
 7693                            .search(
 7694                                buffer_snapshot,
 7695                                Some(search_range.start.0..search_range.end.0),
 7696                            )
 7697                            .await
 7698                            .into_iter()
 7699                            .filter_map(|match_range| {
 7700                                let match_start = buffer_snapshot
 7701                                    .anchor_after(search_range.start + match_range.start);
 7702                                let match_end = buffer_snapshot
 7703                                    .anchor_before(search_range.start + match_range.end);
 7704                                let match_anchor_range =
 7705                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7706                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7707                            }),
 7708                    );
 7709                }
 7710                match_ranges
 7711            });
 7712            let match_ranges = match_task.await;
 7713            editor
 7714                .update_in(cx, |editor, _, cx| {
 7715                    if use_debounce {
 7716                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7717                        editor.debounced_selection_highlight_complete = true;
 7718                    } else if editor.debounced_selection_highlight_complete {
 7719                        return;
 7720                    }
 7721                    if !match_ranges.is_empty() {
 7722                        editor.highlight_background(
 7723                            HighlightKey::SelectedTextHighlight,
 7724                            &match_ranges,
 7725                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7726                            cx,
 7727                        )
 7728                    }
 7729                })
 7730                .log_err();
 7731        })
 7732    }
 7733
 7734    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7735        struct NewlineFold;
 7736        let type_id = std::any::TypeId::of::<NewlineFold>();
 7737        if !self.mode.is_single_line() {
 7738            return;
 7739        }
 7740        let snapshot = self.snapshot(window, cx);
 7741        if snapshot.buffer_snapshot().max_point().row == 0 {
 7742            return;
 7743        }
 7744        let task = cx.background_spawn(async move {
 7745            let new_newlines = snapshot
 7746                .buffer_chars_at(MultiBufferOffset(0))
 7747                .filter_map(|(c, i)| {
 7748                    if c == '\n' {
 7749                        Some(
 7750                            snapshot.buffer_snapshot().anchor_after(i)
 7751                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7752                        )
 7753                    } else {
 7754                        None
 7755                    }
 7756                })
 7757                .collect::<Vec<_>>();
 7758            let existing_newlines = snapshot
 7759                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7760                .filter_map(|fold| {
 7761                    if fold.placeholder.type_tag == Some(type_id) {
 7762                        Some(fold.range.start..fold.range.end)
 7763                    } else {
 7764                        None
 7765                    }
 7766                })
 7767                .collect::<Vec<_>>();
 7768
 7769            (new_newlines, existing_newlines)
 7770        });
 7771        self.folding_newlines = cx.spawn(async move |this, cx| {
 7772            let (new_newlines, existing_newlines) = task.await;
 7773            if new_newlines == existing_newlines {
 7774                return;
 7775            }
 7776            let placeholder = FoldPlaceholder {
 7777                render: Arc::new(move |_, _, cx| {
 7778                    div()
 7779                        .bg(cx.theme().status().hint_background)
 7780                        .border_b_1()
 7781                        .size_full()
 7782                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7783                        .border_color(cx.theme().status().hint)
 7784                        .child("\\n")
 7785                        .into_any()
 7786                }),
 7787                constrain_width: false,
 7788                merge_adjacent: false,
 7789                type_tag: Some(type_id),
 7790                collapsed_text: None,
 7791            };
 7792            let creases = new_newlines
 7793                .into_iter()
 7794                .map(|range| Crease::simple(range, placeholder.clone()))
 7795                .collect();
 7796            this.update(cx, |this, cx| {
 7797                this.display_map.update(cx, |display_map, cx| {
 7798                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7799                    display_map.fold(creases, cx);
 7800                });
 7801            })
 7802            .ok();
 7803        });
 7804    }
 7805
 7806    #[ztracing::instrument(skip_all)]
 7807    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7808        if !self.lsp_data_enabled() {
 7809            return;
 7810        }
 7811        let cursor = self.selections.newest_anchor().head();
 7812        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7813
 7814        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7815            self.outline_symbols_at_cursor =
 7816                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7817            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7818            cx.notify();
 7819        } else {
 7820            let syntax = cx.theme().syntax().clone();
 7821            let background_task = cx.background_spawn(async move {
 7822                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7823            });
 7824            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7825                cx.spawn(async move |this, cx| {
 7826                    let symbols = background_task.await;
 7827                    this.update(cx, |this, cx| {
 7828                        this.outline_symbols_at_cursor = symbols;
 7829                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7830                        cx.notify();
 7831                    })
 7832                    .ok();
 7833                });
 7834        }
 7835    }
 7836
 7837    #[ztracing::instrument(skip_all)]
 7838    fn refresh_selected_text_highlights(
 7839        &mut self,
 7840        snapshot: &DisplaySnapshot,
 7841        on_buffer_edit: bool,
 7842        window: &mut Window,
 7843        cx: &mut Context<Editor>,
 7844    ) {
 7845        let Some((query_text, query_range)) =
 7846            self.prepare_highlight_query_from_selection(snapshot, cx)
 7847        else {
 7848            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7849            self.quick_selection_highlight_task.take();
 7850            self.debounced_selection_highlight_task.take();
 7851            self.debounced_selection_highlight_complete = false;
 7852            return;
 7853        };
 7854        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7855        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7856        let query_changed = self
 7857            .quick_selection_highlight_task
 7858            .as_ref()
 7859            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7860        if query_changed {
 7861            self.debounced_selection_highlight_complete = false;
 7862        }
 7863        if on_buffer_edit || query_changed {
 7864            self.quick_selection_highlight_task = Some((
 7865                query_range.clone(),
 7866                self.update_selection_occurrence_highlights(
 7867                    snapshot.buffer.clone(),
 7868                    query_text.clone(),
 7869                    query_range.clone(),
 7870                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7871                    false,
 7872                    window,
 7873                    cx,
 7874                ),
 7875            ));
 7876        }
 7877        if on_buffer_edit
 7878            || self
 7879                .debounced_selection_highlight_task
 7880                .as_ref()
 7881                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7882        {
 7883            let multi_buffer_start = multi_buffer_snapshot
 7884                .anchor_before(MultiBufferOffset(0))
 7885                .to_point(&multi_buffer_snapshot);
 7886            let multi_buffer_end = multi_buffer_snapshot
 7887                .anchor_after(multi_buffer_snapshot.len())
 7888                .to_point(&multi_buffer_snapshot);
 7889            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7890            self.debounced_selection_highlight_task = Some((
 7891                query_range.clone(),
 7892                self.update_selection_occurrence_highlights(
 7893                    snapshot.buffer.clone(),
 7894                    query_text,
 7895                    query_range,
 7896                    multi_buffer_full_range,
 7897                    true,
 7898                    window,
 7899                    cx,
 7900                ),
 7901            ));
 7902        }
 7903    }
 7904
 7905    pub fn multi_buffer_visible_range(
 7906        &self,
 7907        display_snapshot: &DisplaySnapshot,
 7908        cx: &App,
 7909    ) -> Range<Point> {
 7910        let visible_start = self
 7911            .scroll_manager
 7912            .native_anchor(display_snapshot, cx)
 7913            .anchor
 7914            .to_point(display_snapshot.buffer_snapshot())
 7915            .to_display_point(display_snapshot);
 7916
 7917        let mut target_end = visible_start;
 7918        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7919
 7920        visible_start.to_point(display_snapshot)
 7921            ..display_snapshot
 7922                .clip_point(target_end, Bias::Right)
 7923                .to_point(display_snapshot)
 7924    }
 7925
 7926    pub fn refresh_edit_prediction(
 7927        &mut self,
 7928        debounce: bool,
 7929        user_requested: bool,
 7930        window: &mut Window,
 7931        cx: &mut Context<Self>,
 7932    ) -> Option<()> {
 7933        if self.leader_id.is_some() {
 7934            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7935            return None;
 7936        }
 7937
 7938        let cursor = self.selections.newest_anchor().head();
 7939        let (buffer, cursor_buffer_position) =
 7940            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7941
 7942        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7943            return None;
 7944        }
 7945
 7946        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7947            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7948            return None;
 7949        }
 7950
 7951        self.update_visible_edit_prediction(window, cx);
 7952
 7953        if !user_requested
 7954            && (!self.should_show_edit_predictions()
 7955                || !self.is_focused(window)
 7956                || buffer.read(cx).is_empty())
 7957        {
 7958            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7959            return None;
 7960        }
 7961
 7962        self.edit_prediction_provider()?
 7963            .refresh(buffer, cursor_buffer_position, debounce, cx);
 7964        Some(())
 7965    }
 7966
 7967    fn show_edit_predictions_in_menu(&self) -> bool {
 7968        match self.edit_prediction_settings {
 7969            EditPredictionSettings::Disabled => false,
 7970            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7971        }
 7972    }
 7973
 7974    pub fn edit_predictions_enabled(&self) -> bool {
 7975        match self.edit_prediction_settings {
 7976            EditPredictionSettings::Disabled => false,
 7977            EditPredictionSettings::Enabled { .. } => true,
 7978        }
 7979    }
 7980
 7981    fn edit_prediction_requires_modifier(&self) -> bool {
 7982        match self.edit_prediction_settings {
 7983            EditPredictionSettings::Disabled => false,
 7984            EditPredictionSettings::Enabled {
 7985                preview_requires_modifier,
 7986                ..
 7987            } => preview_requires_modifier,
 7988        }
 7989    }
 7990
 7991    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7992        if self.edit_prediction_provider.is_none() {
 7993            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7994            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7995            return;
 7996        }
 7997
 7998        let selection = self.selections.newest_anchor();
 7999        let cursor = selection.head();
 8000
 8001        if let Some((buffer, cursor_buffer_position)) =
 8002            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8003        {
 8004            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8005                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8006                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8007                return;
 8008            }
 8009            self.edit_prediction_settings =
 8010                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8011        }
 8012    }
 8013
 8014    fn edit_prediction_settings_at_position(
 8015        &self,
 8016        buffer: &Entity<Buffer>,
 8017        buffer_position: language::Anchor,
 8018        cx: &App,
 8019    ) -> EditPredictionSettings {
 8020        if !self.mode.is_full()
 8021            || !self.show_edit_predictions_override.unwrap_or(true)
 8022            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 8023        {
 8024            return EditPredictionSettings::Disabled;
 8025        }
 8026
 8027        let buffer = buffer.read(cx);
 8028
 8029        let file = buffer.file();
 8030
 8031        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 8032            return EditPredictionSettings::Disabled;
 8033        };
 8034
 8035        let by_provider = matches!(
 8036            self.menu_edit_predictions_policy,
 8037            MenuEditPredictionsPolicy::ByProvider
 8038        );
 8039
 8040        let show_in_menu = by_provider
 8041            && self
 8042                .edit_prediction_provider
 8043                .as_ref()
 8044                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 8045
 8046        let preview_requires_modifier =
 8047            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 8048
 8049        EditPredictionSettings::Enabled {
 8050            show_in_menu,
 8051            preview_requires_modifier,
 8052        }
 8053    }
 8054
 8055    fn should_show_edit_predictions(&self) -> bool {
 8056        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 8057    }
 8058
 8059    pub fn edit_prediction_preview_is_active(&self) -> bool {
 8060        matches!(
 8061            self.edit_prediction_preview,
 8062            EditPredictionPreview::Active { .. }
 8063        )
 8064    }
 8065
 8066    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 8067        let cursor = self.selections.newest_anchor().head();
 8068        if let Some((buffer, cursor_position)) =
 8069            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8070        {
 8071            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 8072        } else {
 8073            false
 8074        }
 8075    }
 8076
 8077    pub fn supports_minimap(&self, cx: &App) -> bool {
 8078        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 8079    }
 8080
 8081    fn edit_predictions_enabled_in_buffer(
 8082        &self,
 8083        buffer: &Entity<Buffer>,
 8084        buffer_position: language::Anchor,
 8085        cx: &App,
 8086    ) -> bool {
 8087        maybe!({
 8088            if self.read_only(cx) || self.leader_id.is_some() {
 8089                return Some(false);
 8090            }
 8091            let provider = self.edit_prediction_provider()?;
 8092            if !provider.is_enabled(buffer, buffer_position, cx) {
 8093                return Some(false);
 8094            }
 8095            let buffer = buffer.read(cx);
 8096            let Some(file) = buffer.file() else {
 8097                return Some(true);
 8098            };
 8099            let settings = all_language_settings(Some(file), cx);
 8100            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8101        })
 8102        .unwrap_or(false)
 8103    }
 8104
 8105    pub fn show_edit_prediction(
 8106        &mut self,
 8107        _: &ShowEditPrediction,
 8108        window: &mut Window,
 8109        cx: &mut Context<Self>,
 8110    ) {
 8111        if !self.has_active_edit_prediction() {
 8112            self.refresh_edit_prediction(false, true, window, cx);
 8113            return;
 8114        }
 8115
 8116        self.update_visible_edit_prediction(window, cx);
 8117    }
 8118
 8119    pub fn display_cursor_names(
 8120        &mut self,
 8121        _: &DisplayCursorNames,
 8122        window: &mut Window,
 8123        cx: &mut Context<Self>,
 8124    ) {
 8125        self.show_cursor_names(window, cx);
 8126    }
 8127
 8128    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8129        self.show_cursor_names = true;
 8130        cx.notify();
 8131        cx.spawn_in(window, async move |this, cx| {
 8132            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8133            this.update(cx, |this, cx| {
 8134                this.show_cursor_names = false;
 8135                cx.notify()
 8136            })
 8137            .ok()
 8138        })
 8139        .detach();
 8140    }
 8141
 8142    pub fn accept_partial_edit_prediction(
 8143        &mut self,
 8144        granularity: EditPredictionGranularity,
 8145        window: &mut Window,
 8146        cx: &mut Context<Self>,
 8147    ) {
 8148        if self.show_edit_predictions_in_menu() {
 8149            self.hide_context_menu(window, cx);
 8150        }
 8151
 8152        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8153            return;
 8154        };
 8155
 8156        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8157            return;
 8158        }
 8159
 8160        match &active_edit_prediction.completion {
 8161            EditPrediction::MoveWithin { target, .. } => {
 8162                let target = *target;
 8163
 8164                if matches!(granularity, EditPredictionGranularity::Full) {
 8165                    if let Some(position_map) = &self.last_position_map {
 8166                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8167                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8168
 8169                        if is_visible || !self.edit_prediction_requires_modifier() {
 8170                            self.unfold_ranges(&[target..target], true, false, cx);
 8171                            self.change_selections(
 8172                                SelectionEffects::scroll(Autoscroll::newest()),
 8173                                window,
 8174                                cx,
 8175                                |selections| {
 8176                                    selections.select_anchor_ranges([target..target]);
 8177                                },
 8178                            );
 8179                            self.clear_row_highlights::<EditPredictionPreview>();
 8180                            self.edit_prediction_preview
 8181                                .set_previous_scroll_position(None);
 8182                        } else {
 8183                            // Highlight and request scroll
 8184                            self.edit_prediction_preview
 8185                                .set_previous_scroll_position(Some(
 8186                                    position_map.snapshot.scroll_anchor,
 8187                                ));
 8188                            self.highlight_rows::<EditPredictionPreview>(
 8189                                target..target,
 8190                                cx.theme().colors().editor_highlighted_line_background,
 8191                                RowHighlightOptions {
 8192                                    autoscroll: true,
 8193                                    ..Default::default()
 8194                                },
 8195                                cx,
 8196                            );
 8197                            self.request_autoscroll(Autoscroll::fit(), cx);
 8198                        }
 8199                    }
 8200                } else {
 8201                    self.change_selections(
 8202                        SelectionEffects::scroll(Autoscroll::newest()),
 8203                        window,
 8204                        cx,
 8205                        |selections| {
 8206                            selections.select_anchor_ranges([target..target]);
 8207                        },
 8208                    );
 8209                }
 8210            }
 8211            EditPrediction::MoveOutside { snapshot, target } => {
 8212                if let Some(workspace) = self.workspace() {
 8213                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8214                        .detach_and_log_err(cx);
 8215                }
 8216            }
 8217            EditPrediction::Edit {
 8218                edits,
 8219                cursor_position,
 8220                ..
 8221            } => {
 8222                self.report_edit_prediction_event(
 8223                    active_edit_prediction.completion_id.clone(),
 8224                    true,
 8225                    cx,
 8226                );
 8227
 8228                match granularity {
 8229                    EditPredictionGranularity::Full => {
 8230                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8231
 8232                        // Compute fallback cursor position BEFORE applying the edit,
 8233                        // so the anchor tracks through the edit correctly
 8234                        let fallback_cursor_target = {
 8235                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8236                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8237                        };
 8238
 8239                        self.buffer.update(cx, |buffer, cx| {
 8240                            buffer.edit(edits.iter().cloned(), None, cx)
 8241                        });
 8242
 8243                        if let Some(provider) = self.edit_prediction_provider() {
 8244                            provider.accept(cx);
 8245                        }
 8246
 8247                        // Resolve cursor position after the edit is applied
 8248                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8249                            // The anchor tracks through the edit, then we add the offset
 8250                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8251                            let base_offset = anchor.to_offset(&snapshot).0;
 8252                            let target_offset =
 8253                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8254                            snapshot.anchor_after(target_offset)
 8255                        } else {
 8256                            fallback_cursor_target
 8257                        };
 8258
 8259                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8260                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8261                        });
 8262
 8263                        let selections = self.selections.disjoint_anchors_arc();
 8264                        if let Some(transaction_id_now) =
 8265                            self.buffer.read(cx).last_transaction_id(cx)
 8266                        {
 8267                            if transaction_id_prev != Some(transaction_id_now) {
 8268                                self.selection_history
 8269                                    .insert_transaction(transaction_id_now, selections);
 8270                            }
 8271                        }
 8272
 8273                        self.update_visible_edit_prediction(window, cx);
 8274                        if self.active_edit_prediction.is_none() {
 8275                            self.refresh_edit_prediction(true, true, window, cx);
 8276                        }
 8277                        cx.notify();
 8278                    }
 8279                    _ => {
 8280                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8281                        let cursor_offset = self
 8282                            .selections
 8283                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8284                            .head();
 8285
 8286                        let insertion = edits.iter().find_map(|(range, text)| {
 8287                            let range = range.to_offset(&snapshot);
 8288                            if range.is_empty() && range.start == cursor_offset {
 8289                                Some(text)
 8290                            } else {
 8291                                None
 8292                            }
 8293                        });
 8294
 8295                        if let Some(text) = insertion {
 8296                            let text_to_insert = match granularity {
 8297                                EditPredictionGranularity::Word => {
 8298                                    let mut partial = text
 8299                                        .chars()
 8300                                        .by_ref()
 8301                                        .take_while(|c| c.is_alphabetic())
 8302                                        .collect::<String>();
 8303                                    if partial.is_empty() {
 8304                                        partial = text
 8305                                            .chars()
 8306                                            .by_ref()
 8307                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8308                                            .collect::<String>();
 8309                                    }
 8310                                    partial
 8311                                }
 8312                                EditPredictionGranularity::Line => {
 8313                                    if let Some(line) = text.split_inclusive('\n').next() {
 8314                                        line.to_string()
 8315                                    } else {
 8316                                        text.to_string()
 8317                                    }
 8318                                }
 8319                                EditPredictionGranularity::Full => unreachable!(),
 8320                            };
 8321
 8322                            cx.emit(EditorEvent::InputHandled {
 8323                                utf16_range_to_replace: None,
 8324                                text: text_to_insert.clone().into(),
 8325                            });
 8326
 8327                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8328                            self.refresh_edit_prediction(true, true, window, cx);
 8329                            cx.notify();
 8330                        } else {
 8331                            self.accept_partial_edit_prediction(
 8332                                EditPredictionGranularity::Full,
 8333                                window,
 8334                                cx,
 8335                            );
 8336                        }
 8337                    }
 8338                }
 8339            }
 8340        }
 8341    }
 8342
 8343    pub fn accept_next_word_edit_prediction(
 8344        &mut self,
 8345        _: &AcceptNextWordEditPrediction,
 8346        window: &mut Window,
 8347        cx: &mut Context<Self>,
 8348    ) {
 8349        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8350    }
 8351
 8352    pub fn accept_next_line_edit_prediction(
 8353        &mut self,
 8354        _: &AcceptNextLineEditPrediction,
 8355        window: &mut Window,
 8356        cx: &mut Context<Self>,
 8357    ) {
 8358        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8359    }
 8360
 8361    pub fn accept_edit_prediction(
 8362        &mut self,
 8363        _: &AcceptEditPrediction,
 8364        window: &mut Window,
 8365        cx: &mut Context<Self>,
 8366    ) {
 8367        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8368    }
 8369
 8370    fn discard_edit_prediction(
 8371        &mut self,
 8372        reason: EditPredictionDiscardReason,
 8373        cx: &mut Context<Self>,
 8374    ) -> bool {
 8375        if reason == EditPredictionDiscardReason::Rejected {
 8376            let completion_id = self
 8377                .active_edit_prediction
 8378                .as_ref()
 8379                .and_then(|active_completion| active_completion.completion_id.clone());
 8380
 8381            self.report_edit_prediction_event(completion_id, false, cx);
 8382        }
 8383
 8384        if let Some(provider) = self.edit_prediction_provider() {
 8385            provider.discard(reason, cx);
 8386        }
 8387
 8388        self.take_active_edit_prediction(cx)
 8389    }
 8390
 8391    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8392        let Some(provider) = self.edit_prediction_provider() else {
 8393            return;
 8394        };
 8395
 8396        let Some((_, buffer, _)) = self
 8397            .buffer
 8398            .read(cx)
 8399            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8400        else {
 8401            return;
 8402        };
 8403
 8404        let extension = buffer
 8405            .read(cx)
 8406            .file()
 8407            .and_then(|file| Some(file.path().extension()?.to_string()));
 8408
 8409        let event_type = match accepted {
 8410            true => "Edit Prediction Accepted",
 8411            false => "Edit Prediction Discarded",
 8412        };
 8413        telemetry::event!(
 8414            event_type,
 8415            provider = provider.name(),
 8416            prediction_id = id,
 8417            suggestion_accepted = accepted,
 8418            file_extension = extension,
 8419        );
 8420    }
 8421
 8422    fn open_editor_at_anchor(
 8423        snapshot: &language::BufferSnapshot,
 8424        target: language::Anchor,
 8425        workspace: &Entity<Workspace>,
 8426        window: &mut Window,
 8427        cx: &mut App,
 8428    ) -> Task<Result<()>> {
 8429        workspace.update(cx, |workspace, cx| {
 8430            let path = snapshot.file().map(|file| file.full_path(cx));
 8431            let Some(path) =
 8432                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8433            else {
 8434                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8435            };
 8436            let target = text::ToPoint::to_point(&target, snapshot);
 8437            let item = workspace.open_path(path, None, true, window, cx);
 8438            window.spawn(cx, async move |cx| {
 8439                let Some(editor) = item.await?.downcast::<Editor>() else {
 8440                    return Ok(());
 8441                };
 8442                editor
 8443                    .update_in(cx, |editor, window, cx| {
 8444                        editor.go_to_singleton_buffer_point(target, window, cx);
 8445                    })
 8446                    .ok();
 8447                anyhow::Ok(())
 8448            })
 8449        })
 8450    }
 8451
 8452    pub fn has_active_edit_prediction(&self) -> bool {
 8453        self.active_edit_prediction.is_some()
 8454    }
 8455
 8456    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8457        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8458            return false;
 8459        };
 8460
 8461        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8462        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8463        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8464        true
 8465    }
 8466
 8467    /// Returns true when we're displaying the edit prediction popover below the cursor
 8468    /// like we are not previewing and the LSP autocomplete menu is visible
 8469    /// or we are in `when_holding_modifier` mode.
 8470    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8471        if self.edit_prediction_preview_is_active()
 8472            || !self.show_edit_predictions_in_menu()
 8473            || !self.edit_predictions_enabled()
 8474        {
 8475            return false;
 8476        }
 8477
 8478        if self.has_visible_completions_menu() {
 8479            return true;
 8480        }
 8481
 8482        has_completion && self.edit_prediction_requires_modifier()
 8483    }
 8484
 8485    fn handle_modifiers_changed(
 8486        &mut self,
 8487        modifiers: Modifiers,
 8488        position_map: &PositionMap,
 8489        window: &mut Window,
 8490        cx: &mut Context<Self>,
 8491    ) {
 8492        // Ensure that the edit prediction preview is updated, even when not
 8493        // enabled, if there's an active edit prediction preview.
 8494        if self.show_edit_predictions_in_menu()
 8495            || matches!(
 8496                self.edit_prediction_preview,
 8497                EditPredictionPreview::Active { .. }
 8498            )
 8499        {
 8500            self.update_edit_prediction_preview(&modifiers, window, cx);
 8501        }
 8502
 8503        self.update_selection_mode(&modifiers, position_map, window, cx);
 8504
 8505        let mouse_position = window.mouse_position();
 8506        if !position_map.text_hitbox.is_hovered(window) {
 8507            return;
 8508        }
 8509
 8510        self.update_hovered_link(
 8511            position_map.point_for_position(mouse_position),
 8512            Some(mouse_position),
 8513            &position_map.snapshot,
 8514            modifiers,
 8515            window,
 8516            cx,
 8517        )
 8518    }
 8519
 8520    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8521        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8522            MultiCursorModifier::Alt => modifiers.secondary(),
 8523            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8524        }
 8525    }
 8526
 8527    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8528        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8529            MultiCursorModifier::Alt => modifiers.alt,
 8530            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8531        }
 8532    }
 8533
 8534    fn columnar_selection_mode(
 8535        modifiers: &Modifiers,
 8536        cx: &mut Context<Self>,
 8537    ) -> Option<ColumnarMode> {
 8538        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8539            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8540                Some(ColumnarMode::FromMouse)
 8541            } else if Self::is_alt_pressed(modifiers, cx) {
 8542                Some(ColumnarMode::FromSelection)
 8543            } else {
 8544                None
 8545            }
 8546        } else {
 8547            None
 8548        }
 8549    }
 8550
 8551    fn update_selection_mode(
 8552        &mut self,
 8553        modifiers: &Modifiers,
 8554        position_map: &PositionMap,
 8555        window: &mut Window,
 8556        cx: &mut Context<Self>,
 8557    ) {
 8558        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8559            return;
 8560        };
 8561        if self.selections.pending_anchor().is_none() {
 8562            return;
 8563        }
 8564
 8565        let mouse_position = window.mouse_position();
 8566        let point_for_position = position_map.point_for_position(mouse_position);
 8567        let position = point_for_position.previous_valid;
 8568
 8569        self.select(
 8570            SelectPhase::BeginColumnar {
 8571                position,
 8572                reset: false,
 8573                mode,
 8574                goal_column: point_for_position.exact_unclipped.column(),
 8575            },
 8576            window,
 8577            cx,
 8578        );
 8579    }
 8580
 8581    fn update_edit_prediction_preview(
 8582        &mut self,
 8583        modifiers: &Modifiers,
 8584        window: &mut Window,
 8585        cx: &mut Context<Self>,
 8586    ) {
 8587        let mut modifiers_held = false;
 8588
 8589        let key_context = self.key_context_internal(self.has_active_edit_prediction(), window, cx);
 8590        let actions: [&dyn Action; 3] = [
 8591            &AcceptEditPrediction,
 8592            &AcceptNextWordEditPrediction,
 8593            &AcceptNextLineEditPrediction,
 8594        ];
 8595
 8596        for action in actions {
 8597            let bindings = window.bindings_for_action_in_context(action, key_context.clone());
 8598            for binding in bindings {
 8599                if let Some(keystroke) = binding.keystrokes().first() {
 8600                    modifiers_held = modifiers_held
 8601                        || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8602                }
 8603            }
 8604        }
 8605
 8606        if modifiers_held {
 8607            if matches!(
 8608                self.edit_prediction_preview,
 8609                EditPredictionPreview::Inactive { .. }
 8610            ) {
 8611                self.edit_prediction_preview = EditPredictionPreview::Active {
 8612                    previous_scroll_position: None,
 8613                    since: Instant::now(),
 8614                };
 8615
 8616                self.update_visible_edit_prediction(window, cx);
 8617                cx.notify();
 8618            }
 8619        } else if let EditPredictionPreview::Active {
 8620            previous_scroll_position,
 8621            since,
 8622        } = self.edit_prediction_preview
 8623        {
 8624            if let (Some(previous_scroll_position), Some(position_map)) =
 8625                (previous_scroll_position, self.last_position_map.as_ref())
 8626            {
 8627                self.set_scroll_position(
 8628                    previous_scroll_position
 8629                        .scroll_position(&position_map.snapshot.display_snapshot),
 8630                    window,
 8631                    cx,
 8632                );
 8633            }
 8634
 8635            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8636                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8637            };
 8638            self.clear_row_highlights::<EditPredictionPreview>();
 8639            self.update_visible_edit_prediction(window, cx);
 8640            cx.notify();
 8641        }
 8642    }
 8643
 8644    fn update_visible_edit_prediction(
 8645        &mut self,
 8646        _window: &mut Window,
 8647        cx: &mut Context<Self>,
 8648    ) -> Option<()> {
 8649        if self.ime_transaction.is_some() {
 8650            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8651            return None;
 8652        }
 8653
 8654        let selection = self.selections.newest_anchor();
 8655        let cursor = selection.head();
 8656        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8657
 8658        // Check project-level disable_ai setting for the current buffer
 8659        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8660            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8661                return None;
 8662            }
 8663        }
 8664        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8665        let excerpt_id = cursor.excerpt_id;
 8666
 8667        let show_in_menu = self.show_edit_predictions_in_menu();
 8668        let completions_menu_has_precedence = !show_in_menu
 8669            && (self.context_menu.borrow().is_some()
 8670                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8671
 8672        if completions_menu_has_precedence
 8673            || !offset_selection.is_empty()
 8674            || self
 8675                .active_edit_prediction
 8676                .as_ref()
 8677                .is_some_and(|completion| {
 8678                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8679                        return false;
 8680                    };
 8681                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8682                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8683                    !invalidation_range.contains(&offset_selection.head())
 8684                })
 8685        {
 8686            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8687            return None;
 8688        }
 8689
 8690        self.take_active_edit_prediction(cx);
 8691        let Some(provider) = self.edit_prediction_provider() else {
 8692            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8693            return None;
 8694        };
 8695
 8696        let (buffer, cursor_buffer_position) =
 8697            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8698
 8699        self.edit_prediction_settings =
 8700            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8701
 8702        self.in_leading_whitespace = multibuffer.is_line_whitespace_upto(cursor);
 8703
 8704        if self.in_leading_whitespace {
 8705            let cursor_point = cursor.to_point(&multibuffer);
 8706            let mut suggested_indent = None;
 8707            multibuffer.suggested_indents_callback(
 8708                cursor_point.row..cursor_point.row + 1,
 8709                &mut |_, indent| {
 8710                    suggested_indent = Some(indent);
 8711                    ControlFlow::Break(())
 8712                },
 8713                cx,
 8714            );
 8715
 8716            if let Some(indent) = suggested_indent
 8717                && indent.len == cursor_point.column
 8718            {
 8719                self.in_leading_whitespace = false;
 8720            }
 8721        }
 8722
 8723        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8724
 8725        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8726        {
 8727            edit_prediction_types::EditPrediction::Local {
 8728                id,
 8729                edits,
 8730                cursor_position,
 8731                edit_preview,
 8732            } => (id, edits, cursor_position, edit_preview),
 8733            edit_prediction_types::EditPrediction::Jump {
 8734                id,
 8735                snapshot,
 8736                target,
 8737            } => {
 8738                if let Some(provider) = &self.edit_prediction_provider {
 8739                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8740                }
 8741                self.stale_edit_prediction_in_menu = None;
 8742                self.active_edit_prediction = Some(EditPredictionState {
 8743                    inlay_ids: vec![],
 8744                    completion: EditPrediction::MoveOutside { snapshot, target },
 8745                    completion_id: id,
 8746                    invalidation_range: None,
 8747                });
 8748                cx.notify();
 8749                return Some(());
 8750            }
 8751        };
 8752
 8753        let edits = edits
 8754            .into_iter()
 8755            .flat_map(|(range, new_text)| {
 8756                Some((
 8757                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8758                    new_text,
 8759                ))
 8760            })
 8761            .collect::<Vec<_>>();
 8762        if edits.is_empty() {
 8763            return None;
 8764        }
 8765
 8766        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8767            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8768            Some((anchor, predicted.offset))
 8769        });
 8770
 8771        let first_edit_start = edits.first().unwrap().0.start;
 8772        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8773        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8774
 8775        let last_edit_end = edits.last().unwrap().0.end;
 8776        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8777        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8778
 8779        let cursor_row = cursor.to_point(&multibuffer).row;
 8780
 8781        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8782
 8783        let mut inlay_ids = Vec::new();
 8784        let invalidation_row_range;
 8785        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8786            Some(cursor_row..edit_end_row)
 8787        } else if cursor_row > edit_end_row {
 8788            Some(edit_start_row..cursor_row)
 8789        } else {
 8790            None
 8791        };
 8792        let supports_jump = self
 8793            .edit_prediction_provider
 8794            .as_ref()
 8795            .map(|provider| provider.provider.supports_jump_to_edit())
 8796            .unwrap_or(true);
 8797
 8798        let is_move = supports_jump
 8799            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8800        let completion = if is_move {
 8801            if let Some(provider) = &self.edit_prediction_provider {
 8802                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8803            }
 8804            invalidation_row_range =
 8805                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8806            let target = first_edit_start;
 8807            EditPrediction::MoveWithin { target, snapshot }
 8808        } else {
 8809            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8810                && !self.edit_predictions_hidden_for_vim_mode;
 8811
 8812            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8813                if provider.show_tab_accept_marker() {
 8814                    EditDisplayMode::TabAccept
 8815                } else {
 8816                    EditDisplayMode::Inline
 8817                }
 8818            } else {
 8819                EditDisplayMode::DiffPopover
 8820            };
 8821
 8822            if show_completions_in_buffer {
 8823                if let Some(provider) = &self.edit_prediction_provider {
 8824                    let suggestion_display_type = match display_mode {
 8825                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8826                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8827                            SuggestionDisplayType::GhostText
 8828                        }
 8829                    };
 8830                    provider.provider.did_show(suggestion_display_type, cx);
 8831                }
 8832                if edits
 8833                    .iter()
 8834                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8835                {
 8836                    let mut inlays = Vec::new();
 8837                    for (range, new_text) in &edits {
 8838                        let inlay = Inlay::edit_prediction(
 8839                            post_inc(&mut self.next_inlay_id),
 8840                            range.start,
 8841                            new_text.as_ref(),
 8842                        );
 8843                        inlay_ids.push(inlay.id);
 8844                        inlays.push(inlay);
 8845                    }
 8846
 8847                    self.splice_inlays(&[], inlays, cx);
 8848                } else {
 8849                    let background_color = cx.theme().status().deleted_background;
 8850                    self.highlight_text(
 8851                        HighlightKey::EditPredictionHighlight,
 8852                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8853                        HighlightStyle {
 8854                            background_color: Some(background_color),
 8855                            ..Default::default()
 8856                        },
 8857                        cx,
 8858                    );
 8859                }
 8860            }
 8861
 8862            invalidation_row_range = edit_start_row..edit_end_row;
 8863
 8864            EditPrediction::Edit {
 8865                edits,
 8866                cursor_position,
 8867                edit_preview,
 8868                display_mode,
 8869                snapshot,
 8870            }
 8871        };
 8872
 8873        let invalidation_range = multibuffer
 8874            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8875            ..multibuffer.anchor_after(Point::new(
 8876                invalidation_row_range.end,
 8877                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8878            ));
 8879
 8880        self.stale_edit_prediction_in_menu = None;
 8881        self.active_edit_prediction = Some(EditPredictionState {
 8882            inlay_ids,
 8883            completion,
 8884            completion_id,
 8885            invalidation_range: Some(invalidation_range),
 8886        });
 8887
 8888        cx.notify();
 8889
 8890        Some(())
 8891    }
 8892
 8893    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8894        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8895    }
 8896
 8897    /// Get all display points of breakpoints that will be rendered within editor
 8898    ///
 8899    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8900    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8901    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8902    fn active_breakpoints(
 8903        &self,
 8904        range: Range<DisplayRow>,
 8905        window: &mut Window,
 8906        cx: &mut Context<Self>,
 8907    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8908        let mut breakpoint_display_points = HashMap::default();
 8909
 8910        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8911            return breakpoint_display_points;
 8912        };
 8913
 8914        let snapshot = self.snapshot(window, cx);
 8915
 8916        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8917        let Some(project) = self.project() else {
 8918            return breakpoint_display_points;
 8919        };
 8920
 8921        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8922            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8923
 8924        for (buffer_snapshot, range, excerpt_id) in
 8925            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8926        {
 8927            let Some(buffer) = project
 8928                .read(cx)
 8929                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8930            else {
 8931                continue;
 8932            };
 8933            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8934                &buffer,
 8935                Some(
 8936                    buffer_snapshot.anchor_before(range.start)
 8937                        ..buffer_snapshot.anchor_after(range.end),
 8938                ),
 8939                buffer_snapshot,
 8940                cx,
 8941            );
 8942            for (breakpoint, state) in breakpoints {
 8943                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8944                let position = multi_buffer_anchor
 8945                    .to_point(&multi_buffer_snapshot)
 8946                    .to_display_point(&snapshot);
 8947
 8948                breakpoint_display_points.insert(
 8949                    position.row(),
 8950                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8951                );
 8952            }
 8953        }
 8954
 8955        breakpoint_display_points
 8956    }
 8957
 8958    fn breakpoint_context_menu(
 8959        &self,
 8960        anchor: Anchor,
 8961        window: &mut Window,
 8962        cx: &mut Context<Self>,
 8963    ) -> Entity<ui::ContextMenu> {
 8964        let weak_editor = cx.weak_entity();
 8965        let focus_handle = self.focus_handle(cx);
 8966
 8967        let row = self
 8968            .buffer
 8969            .read(cx)
 8970            .snapshot(cx)
 8971            .summary_for_anchor::<Point>(&anchor)
 8972            .row;
 8973
 8974        let breakpoint = self
 8975            .breakpoint_at_row(row, window, cx)
 8976            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8977
 8978        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8979            "Edit Log Breakpoint"
 8980        } else {
 8981            "Set Log Breakpoint"
 8982        };
 8983
 8984        let condition_breakpoint_msg = if breakpoint
 8985            .as_ref()
 8986            .is_some_and(|bp| bp.1.condition.is_some())
 8987        {
 8988            "Edit Condition Breakpoint"
 8989        } else {
 8990            "Set Condition Breakpoint"
 8991        };
 8992
 8993        let hit_condition_breakpoint_msg = if breakpoint
 8994            .as_ref()
 8995            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8996        {
 8997            "Edit Hit Condition Breakpoint"
 8998        } else {
 8999            "Set Hit Condition Breakpoint"
 9000        };
 9001
 9002        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 9003            "Unset Breakpoint"
 9004        } else {
 9005            "Set Breakpoint"
 9006        };
 9007
 9008        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 9009
 9010        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 9011            BreakpointState::Enabled => Some("Disable"),
 9012            BreakpointState::Disabled => Some("Enable"),
 9013        });
 9014
 9015        let (anchor, breakpoint) =
 9016            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 9017
 9018        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 9019            menu.on_blur_subscription(Subscription::new(|| {}))
 9020                .context(focus_handle)
 9021                .when(run_to_cursor, |this| {
 9022                    let weak_editor = weak_editor.clone();
 9023                    this.entry("Run to Cursor", None, move |window, cx| {
 9024                        weak_editor
 9025                            .update(cx, |editor, cx| {
 9026                                editor.change_selections(
 9027                                    SelectionEffects::no_scroll(),
 9028                                    window,
 9029                                    cx,
 9030                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 9031                                );
 9032                            })
 9033                            .ok();
 9034
 9035                        window.dispatch_action(Box::new(RunToCursor), cx);
 9036                    })
 9037                    .separator()
 9038                })
 9039                .when_some(toggle_state_msg, |this, msg| {
 9040                    this.entry(msg, None, {
 9041                        let weak_editor = weak_editor.clone();
 9042                        let breakpoint = breakpoint.clone();
 9043                        move |_window, cx| {
 9044                            weak_editor
 9045                                .update(cx, |this, cx| {
 9046                                    this.edit_breakpoint_at_anchor(
 9047                                        anchor,
 9048                                        breakpoint.as_ref().clone(),
 9049                                        BreakpointEditAction::InvertState,
 9050                                        cx,
 9051                                    );
 9052                                })
 9053                                .log_err();
 9054                        }
 9055                    })
 9056                })
 9057                .entry(set_breakpoint_msg, None, {
 9058                    let weak_editor = weak_editor.clone();
 9059                    let breakpoint = breakpoint.clone();
 9060                    move |_window, cx| {
 9061                        weak_editor
 9062                            .update(cx, |this, cx| {
 9063                                this.edit_breakpoint_at_anchor(
 9064                                    anchor,
 9065                                    breakpoint.as_ref().clone(),
 9066                                    BreakpointEditAction::Toggle,
 9067                                    cx,
 9068                                );
 9069                            })
 9070                            .log_err();
 9071                    }
 9072                })
 9073                .entry(log_breakpoint_msg, None, {
 9074                    let breakpoint = breakpoint.clone();
 9075                    let weak_editor = weak_editor.clone();
 9076                    move |window, cx| {
 9077                        weak_editor
 9078                            .update(cx, |this, cx| {
 9079                                this.add_edit_breakpoint_block(
 9080                                    anchor,
 9081                                    breakpoint.as_ref(),
 9082                                    BreakpointPromptEditAction::Log,
 9083                                    window,
 9084                                    cx,
 9085                                );
 9086                            })
 9087                            .log_err();
 9088                    }
 9089                })
 9090                .entry(condition_breakpoint_msg, None, {
 9091                    let breakpoint = breakpoint.clone();
 9092                    let weak_editor = weak_editor.clone();
 9093                    move |window, cx| {
 9094                        weak_editor
 9095                            .update(cx, |this, cx| {
 9096                                this.add_edit_breakpoint_block(
 9097                                    anchor,
 9098                                    breakpoint.as_ref(),
 9099                                    BreakpointPromptEditAction::Condition,
 9100                                    window,
 9101                                    cx,
 9102                                );
 9103                            })
 9104                            .log_err();
 9105                    }
 9106                })
 9107                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9108                    weak_editor
 9109                        .update(cx, |this, cx| {
 9110                            this.add_edit_breakpoint_block(
 9111                                anchor,
 9112                                breakpoint.as_ref(),
 9113                                BreakpointPromptEditAction::HitCondition,
 9114                                window,
 9115                                cx,
 9116                            );
 9117                        })
 9118                        .log_err();
 9119                })
 9120        })
 9121    }
 9122
 9123    fn render_breakpoint(
 9124        &self,
 9125        position: Anchor,
 9126        row: DisplayRow,
 9127        breakpoint: &Breakpoint,
 9128        state: Option<BreakpointSessionState>,
 9129        cx: &mut Context<Self>,
 9130    ) -> IconButton {
 9131        let is_rejected = state.is_some_and(|s| !s.verified);
 9132        // Is it a breakpoint that shows up when hovering over gutter?
 9133        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9134            (false, false),
 9135            |PhantomBreakpointIndicator {
 9136                 is_active,
 9137                 display_row,
 9138                 collides_with_existing_breakpoint,
 9139             }| {
 9140                (
 9141                    is_active && display_row == row,
 9142                    collides_with_existing_breakpoint,
 9143                )
 9144            },
 9145        );
 9146
 9147        let (color, icon) = {
 9148            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9149                (false, false) => ui::IconName::DebugBreakpoint,
 9150                (true, false) => ui::IconName::DebugLogBreakpoint,
 9151                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9152                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9153            };
 9154
 9155            let theme_colors = cx.theme().colors();
 9156
 9157            let color = if is_phantom {
 9158                if collides_with_existing {
 9159                    Color::Custom(
 9160                        theme_colors
 9161                            .debugger_accent
 9162                            .blend(theme_colors.text.opacity(0.6)),
 9163                    )
 9164                } else {
 9165                    Color::Hint
 9166                }
 9167            } else if is_rejected {
 9168                Color::Disabled
 9169            } else {
 9170                Color::Debugger
 9171            };
 9172
 9173            (color, icon)
 9174        };
 9175
 9176        let breakpoint = Arc::from(breakpoint.clone());
 9177
 9178        let alt_as_text = gpui::Keystroke {
 9179            modifiers: Modifiers::secondary_key(),
 9180            ..Default::default()
 9181        };
 9182        let primary_action_text = if breakpoint.is_disabled() {
 9183            "Enable breakpoint"
 9184        } else if is_phantom && !collides_with_existing {
 9185            "Set breakpoint"
 9186        } else {
 9187            "Unset breakpoint"
 9188        };
 9189        let focus_handle = self.focus_handle.clone();
 9190
 9191        let meta = if is_rejected {
 9192            SharedString::from("No executable code is associated with this line.")
 9193        } else if collides_with_existing && !breakpoint.is_disabled() {
 9194            SharedString::from(format!(
 9195                "{alt_as_text}-click to disable,\nright-click for more options."
 9196            ))
 9197        } else {
 9198            SharedString::from("Right-click for more options.")
 9199        };
 9200        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9201            .icon_size(IconSize::XSmall)
 9202            .size(ui::ButtonSize::None)
 9203            .when(is_rejected, |this| {
 9204                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9205            })
 9206            .icon_color(color)
 9207            .style(ButtonStyle::Transparent)
 9208            .on_click(cx.listener({
 9209                move |editor, event: &ClickEvent, window, cx| {
 9210                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9211                        BreakpointEditAction::InvertState
 9212                    } else {
 9213                        BreakpointEditAction::Toggle
 9214                    };
 9215
 9216                    window.focus(&editor.focus_handle(cx), cx);
 9217                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9218                    editor.edit_breakpoint_at_anchor(
 9219                        position,
 9220                        breakpoint.as_ref().clone(),
 9221                        edit_action,
 9222                        cx,
 9223                    );
 9224                }
 9225            }))
 9226            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9227                editor.set_breakpoint_context_menu(
 9228                    row,
 9229                    Some(position),
 9230                    event.position(),
 9231                    window,
 9232                    cx,
 9233                );
 9234            }))
 9235            .tooltip(move |_window, cx| {
 9236                Tooltip::with_meta_in(
 9237                    primary_action_text,
 9238                    Some(&ToggleBreakpoint),
 9239                    meta.clone(),
 9240                    &focus_handle,
 9241                    cx,
 9242                )
 9243            })
 9244    }
 9245
 9246    fn build_tasks_context(
 9247        project: &Entity<Project>,
 9248        buffer: &Entity<Buffer>,
 9249        buffer_row: u32,
 9250        tasks: &Arc<RunnableTasks>,
 9251        cx: &mut Context<Self>,
 9252    ) -> Task<Option<task::TaskContext>> {
 9253        let position = Point::new(buffer_row, tasks.column);
 9254        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9255        let location = Location {
 9256            buffer: buffer.clone(),
 9257            range: range_start..range_start,
 9258        };
 9259        // Fill in the environmental variables from the tree-sitter captures
 9260        let mut captured_task_variables = TaskVariables::default();
 9261        for (capture_name, value) in tasks.extra_variables.clone() {
 9262            captured_task_variables.insert(
 9263                task::VariableName::Custom(capture_name.into()),
 9264                value.clone(),
 9265            );
 9266        }
 9267        project.update(cx, |project, cx| {
 9268            project.task_store().update(cx, |task_store, cx| {
 9269                task_store.task_context_for_location(captured_task_variables, location, cx)
 9270            })
 9271        })
 9272    }
 9273
 9274    pub fn context_menu_visible(&self) -> bool {
 9275        !self.edit_prediction_preview_is_active()
 9276            && self
 9277                .context_menu
 9278                .borrow()
 9279                .as_ref()
 9280                .is_some_and(|menu| menu.visible())
 9281    }
 9282
 9283    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9284        self.context_menu
 9285            .borrow()
 9286            .as_ref()
 9287            .map(|menu| menu.origin())
 9288    }
 9289
 9290    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9291        self.context_menu_options = Some(options);
 9292    }
 9293
 9294    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9295    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9296
 9297    fn render_edit_prediction_popover(
 9298        &mut self,
 9299        text_bounds: &Bounds<Pixels>,
 9300        content_origin: gpui::Point<Pixels>,
 9301        right_margin: Pixels,
 9302        editor_snapshot: &EditorSnapshot,
 9303        visible_row_range: Range<DisplayRow>,
 9304        scroll_top: ScrollOffset,
 9305        scroll_bottom: ScrollOffset,
 9306        line_layouts: &[LineWithInvisibles],
 9307        line_height: Pixels,
 9308        scroll_position: gpui::Point<ScrollOffset>,
 9309        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9310        newest_selection_head: Option<DisplayPoint>,
 9311        editor_width: Pixels,
 9312        style: &EditorStyle,
 9313        window: &mut Window,
 9314        cx: &mut App,
 9315    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9316        if self.mode().is_minimap() {
 9317            return None;
 9318        }
 9319        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9320
 9321        if self.edit_prediction_visible_in_cursor_popover(true) {
 9322            return None;
 9323        }
 9324
 9325        match &active_edit_prediction.completion {
 9326            EditPrediction::MoveWithin { target, .. } => {
 9327                let target_display_point = target.to_display_point(editor_snapshot);
 9328
 9329                if self.edit_prediction_requires_modifier() {
 9330                    if !self.edit_prediction_preview_is_active() {
 9331                        return None;
 9332                    }
 9333
 9334                    self.render_edit_prediction_modifier_jump_popover(
 9335                        text_bounds,
 9336                        content_origin,
 9337                        visible_row_range,
 9338                        line_layouts,
 9339                        line_height,
 9340                        scroll_pixel_position,
 9341                        newest_selection_head,
 9342                        target_display_point,
 9343                        window,
 9344                        cx,
 9345                    )
 9346                } else {
 9347                    self.render_edit_prediction_eager_jump_popover(
 9348                        text_bounds,
 9349                        content_origin,
 9350                        editor_snapshot,
 9351                        visible_row_range,
 9352                        scroll_top,
 9353                        scroll_bottom,
 9354                        line_height,
 9355                        scroll_pixel_position,
 9356                        target_display_point,
 9357                        editor_width,
 9358                        window,
 9359                        cx,
 9360                    )
 9361                }
 9362            }
 9363            EditPrediction::Edit {
 9364                display_mode: EditDisplayMode::Inline,
 9365                ..
 9366            } => None,
 9367            EditPrediction::Edit {
 9368                display_mode: EditDisplayMode::TabAccept,
 9369                edits,
 9370                ..
 9371            } => {
 9372                let range = &edits.first()?.0;
 9373                let target_display_point = range.end.to_display_point(editor_snapshot);
 9374
 9375                self.render_edit_prediction_end_of_line_popover(
 9376                    "Accept",
 9377                    editor_snapshot,
 9378                    visible_row_range,
 9379                    target_display_point,
 9380                    line_height,
 9381                    scroll_pixel_position,
 9382                    content_origin,
 9383                    editor_width,
 9384                    window,
 9385                    cx,
 9386                )
 9387            }
 9388            EditPrediction::Edit {
 9389                edits,
 9390                edit_preview,
 9391                display_mode: EditDisplayMode::DiffPopover,
 9392                snapshot,
 9393                ..
 9394            } => self.render_edit_prediction_diff_popover(
 9395                text_bounds,
 9396                content_origin,
 9397                right_margin,
 9398                editor_snapshot,
 9399                visible_row_range,
 9400                line_layouts,
 9401                line_height,
 9402                scroll_position,
 9403                scroll_pixel_position,
 9404                newest_selection_head,
 9405                editor_width,
 9406                style,
 9407                edits,
 9408                edit_preview,
 9409                snapshot,
 9410                window,
 9411                cx,
 9412            ),
 9413            EditPrediction::MoveOutside { snapshot, .. } => {
 9414                let mut element = self
 9415                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9416                    .into_any();
 9417
 9418                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9419                let origin_x = text_bounds.size.width - size.width - px(30.);
 9420                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9421                element.prepaint_at(origin, window, cx);
 9422
 9423                Some((element, origin))
 9424            }
 9425        }
 9426    }
 9427
 9428    fn render_edit_prediction_modifier_jump_popover(
 9429        &mut self,
 9430        text_bounds: &Bounds<Pixels>,
 9431        content_origin: gpui::Point<Pixels>,
 9432        visible_row_range: Range<DisplayRow>,
 9433        line_layouts: &[LineWithInvisibles],
 9434        line_height: Pixels,
 9435        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9436        newest_selection_head: Option<DisplayPoint>,
 9437        target_display_point: DisplayPoint,
 9438        window: &mut Window,
 9439        cx: &mut App,
 9440    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9441        let scrolled_content_origin =
 9442            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9443
 9444        const SCROLL_PADDING_Y: Pixels = px(12.);
 9445
 9446        if target_display_point.row() < visible_row_range.start {
 9447            return self.render_edit_prediction_scroll_popover(
 9448                &|_| SCROLL_PADDING_Y,
 9449                IconName::ArrowUp,
 9450                visible_row_range,
 9451                line_layouts,
 9452                newest_selection_head,
 9453                scrolled_content_origin,
 9454                window,
 9455                cx,
 9456            );
 9457        } else if target_display_point.row() >= visible_row_range.end {
 9458            return self.render_edit_prediction_scroll_popover(
 9459                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9460                IconName::ArrowDown,
 9461                visible_row_range,
 9462                line_layouts,
 9463                newest_selection_head,
 9464                scrolled_content_origin,
 9465                window,
 9466                cx,
 9467            );
 9468        }
 9469
 9470        const POLE_WIDTH: Pixels = px(2.);
 9471
 9472        let line_layout =
 9473            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9474        let target_column = target_display_point.column() as usize;
 9475
 9476        let target_x = line_layout.x_for_index(target_column);
 9477        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9478            - scroll_pixel_position.y;
 9479
 9480        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9481
 9482        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9483        border_color.l += 0.001;
 9484
 9485        let mut element = v_flex()
 9486            .items_end()
 9487            .when(flag_on_right, |el| el.items_start())
 9488            .child(if flag_on_right {
 9489                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9490                    .rounded_bl(px(0.))
 9491                    .rounded_tl(px(0.))
 9492                    .border_l_2()
 9493                    .border_color(border_color)
 9494            } else {
 9495                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9496                    .rounded_br(px(0.))
 9497                    .rounded_tr(px(0.))
 9498                    .border_r_2()
 9499                    .border_color(border_color)
 9500            })
 9501            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9502            .into_any();
 9503
 9504        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9505
 9506        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9507            - point(
 9508                if flag_on_right {
 9509                    POLE_WIDTH
 9510                } else {
 9511                    size.width - POLE_WIDTH
 9512                },
 9513                size.height - line_height,
 9514            );
 9515
 9516        origin.x = origin.x.max(content_origin.x);
 9517
 9518        element.prepaint_at(origin, window, cx);
 9519
 9520        Some((element, origin))
 9521    }
 9522
 9523    fn render_edit_prediction_scroll_popover(
 9524        &mut self,
 9525        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9526        scroll_icon: IconName,
 9527        visible_row_range: Range<DisplayRow>,
 9528        line_layouts: &[LineWithInvisibles],
 9529        newest_selection_head: Option<DisplayPoint>,
 9530        scrolled_content_origin: gpui::Point<Pixels>,
 9531        window: &mut Window,
 9532        cx: &mut App,
 9533    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9534        let mut element = self
 9535            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9536            .into_any();
 9537
 9538        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9539
 9540        let cursor = newest_selection_head?;
 9541        let cursor_row_layout =
 9542            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9543        let cursor_column = cursor.column() as usize;
 9544
 9545        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9546
 9547        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9548
 9549        element.prepaint_at(origin, window, cx);
 9550        Some((element, origin))
 9551    }
 9552
 9553    fn render_edit_prediction_eager_jump_popover(
 9554        &mut self,
 9555        text_bounds: &Bounds<Pixels>,
 9556        content_origin: gpui::Point<Pixels>,
 9557        editor_snapshot: &EditorSnapshot,
 9558        visible_row_range: Range<DisplayRow>,
 9559        scroll_top: ScrollOffset,
 9560        scroll_bottom: ScrollOffset,
 9561        line_height: Pixels,
 9562        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9563        target_display_point: DisplayPoint,
 9564        editor_width: Pixels,
 9565        window: &mut Window,
 9566        cx: &mut App,
 9567    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9568        if target_display_point.row().as_f64() < scroll_top {
 9569            let mut element = self
 9570                .render_edit_prediction_line_popover(
 9571                    "Jump to Edit",
 9572                    Some(IconName::ArrowUp),
 9573                    window,
 9574                    cx,
 9575                )
 9576                .into_any();
 9577
 9578            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9579            let offset = point(
 9580                (text_bounds.size.width - size.width) / 2.,
 9581                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9582            );
 9583
 9584            let origin = text_bounds.origin + offset;
 9585            element.prepaint_at(origin, window, cx);
 9586            Some((element, origin))
 9587        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9588            let mut element = self
 9589                .render_edit_prediction_line_popover(
 9590                    "Jump to Edit",
 9591                    Some(IconName::ArrowDown),
 9592                    window,
 9593                    cx,
 9594                )
 9595                .into_any();
 9596
 9597            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9598            let offset = point(
 9599                (text_bounds.size.width - size.width) / 2.,
 9600                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9601            );
 9602
 9603            let origin = text_bounds.origin + offset;
 9604            element.prepaint_at(origin, window, cx);
 9605            Some((element, origin))
 9606        } else {
 9607            self.render_edit_prediction_end_of_line_popover(
 9608                "Jump to Edit",
 9609                editor_snapshot,
 9610                visible_row_range,
 9611                target_display_point,
 9612                line_height,
 9613                scroll_pixel_position,
 9614                content_origin,
 9615                editor_width,
 9616                window,
 9617                cx,
 9618            )
 9619        }
 9620    }
 9621
 9622    fn render_edit_prediction_end_of_line_popover(
 9623        self: &mut Editor,
 9624        label: &'static str,
 9625        editor_snapshot: &EditorSnapshot,
 9626        visible_row_range: Range<DisplayRow>,
 9627        target_display_point: DisplayPoint,
 9628        line_height: Pixels,
 9629        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9630        content_origin: gpui::Point<Pixels>,
 9631        editor_width: Pixels,
 9632        window: &mut Window,
 9633        cx: &mut App,
 9634    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9635        let target_line_end = DisplayPoint::new(
 9636            target_display_point.row(),
 9637            editor_snapshot.line_len(target_display_point.row()),
 9638        );
 9639
 9640        let mut element = self
 9641            .render_edit_prediction_line_popover(label, None, window, cx)
 9642            .into_any();
 9643
 9644        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9645
 9646        let line_origin =
 9647            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9648
 9649        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9650        let mut origin = start_point
 9651            + line_origin
 9652            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9653        origin.x = origin.x.max(content_origin.x);
 9654
 9655        let max_x = content_origin.x + editor_width - size.width;
 9656
 9657        if origin.x > max_x {
 9658            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9659
 9660            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9661                origin.y += offset;
 9662                IconName::ArrowUp
 9663            } else {
 9664                origin.y -= offset;
 9665                IconName::ArrowDown
 9666            };
 9667
 9668            element = self
 9669                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9670                .into_any();
 9671
 9672            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9673
 9674            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9675        }
 9676
 9677        element.prepaint_at(origin, window, cx);
 9678        Some((element, origin))
 9679    }
 9680
 9681    fn render_edit_prediction_diff_popover(
 9682        self: &Editor,
 9683        text_bounds: &Bounds<Pixels>,
 9684        content_origin: gpui::Point<Pixels>,
 9685        right_margin: Pixels,
 9686        editor_snapshot: &EditorSnapshot,
 9687        visible_row_range: Range<DisplayRow>,
 9688        line_layouts: &[LineWithInvisibles],
 9689        line_height: Pixels,
 9690        scroll_position: gpui::Point<ScrollOffset>,
 9691        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9692        newest_selection_head: Option<DisplayPoint>,
 9693        editor_width: Pixels,
 9694        style: &EditorStyle,
 9695        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9696        edit_preview: &Option<language::EditPreview>,
 9697        snapshot: &language::BufferSnapshot,
 9698        window: &mut Window,
 9699        cx: &mut App,
 9700    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9701        let edit_start = edits
 9702            .first()
 9703            .unwrap()
 9704            .0
 9705            .start
 9706            .to_display_point(editor_snapshot);
 9707        let edit_end = edits
 9708            .last()
 9709            .unwrap()
 9710            .0
 9711            .end
 9712            .to_display_point(editor_snapshot);
 9713
 9714        let is_visible = visible_row_range.contains(&edit_start.row())
 9715            || visible_row_range.contains(&edit_end.row());
 9716        if !is_visible {
 9717            return None;
 9718        }
 9719
 9720        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9721            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9722        } else {
 9723            // Fallback for providers without edit_preview
 9724            crate::edit_prediction_fallback_text(edits, cx)
 9725        };
 9726
 9727        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9728        let line_count = highlighted_edits.text.lines().count();
 9729
 9730        const BORDER_WIDTH: Pixels = px(1.);
 9731
 9732        let keybind = self.render_edit_prediction_keybind(window, cx);
 9733        let has_keybind = keybind.is_some();
 9734
 9735        let mut element = h_flex()
 9736            .items_start()
 9737            .child(
 9738                h_flex()
 9739                    .bg(cx.theme().colors().editor_background)
 9740                    .border(BORDER_WIDTH)
 9741                    .shadow_xs()
 9742                    .border_color(cx.theme().colors().border)
 9743                    .rounded_l_lg()
 9744                    .when(line_count > 1, |el| el.rounded_br_lg())
 9745                    .pr_1()
 9746                    .child(styled_text),
 9747            )
 9748            .child(
 9749                h_flex()
 9750                    .h(line_height + BORDER_WIDTH * 2.)
 9751                    .px_1p5()
 9752                    .gap_1()
 9753                    // Workaround: For some reason, there's a gap if we don't do this
 9754                    .ml(-BORDER_WIDTH)
 9755                    .shadow(vec![gpui::BoxShadow {
 9756                        color: gpui::black().opacity(0.05),
 9757                        offset: point(px(1.), px(1.)),
 9758                        blur_radius: px(2.),
 9759                        spread_radius: px(0.),
 9760                    }])
 9761                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9762                    .border(BORDER_WIDTH)
 9763                    .border_color(cx.theme().colors().border)
 9764                    .rounded_r_lg()
 9765                    .id("edit_prediction_diff_popover_keybind")
 9766                    .when(!has_keybind, |el| {
 9767                        let status_colors = cx.theme().status();
 9768
 9769                        el.bg(status_colors.error_background)
 9770                            .border_color(status_colors.error.opacity(0.6))
 9771                            .child(Icon::new(IconName::Info).color(Color::Error))
 9772                            .cursor_default()
 9773                            .hoverable_tooltip(move |_window, cx| {
 9774                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9775                            })
 9776                    })
 9777                    .children(keybind),
 9778            )
 9779            .into_any();
 9780
 9781        let longest_row =
 9782            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9783        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9784            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9785        } else {
 9786            layout_line(
 9787                longest_row,
 9788                editor_snapshot,
 9789                style,
 9790                editor_width,
 9791                |_| false,
 9792                window,
 9793                cx,
 9794            )
 9795            .width
 9796        };
 9797
 9798        let viewport_bounds =
 9799            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9800                right: -right_margin,
 9801                ..Default::default()
 9802            });
 9803
 9804        let x_after_longest = Pixels::from(
 9805            ScrollPixelOffset::from(
 9806                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9807            ) - scroll_pixel_position.x,
 9808        );
 9809
 9810        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9811
 9812        // Fully visible if it can be displayed within the window (allow overlapping other
 9813        // panes). However, this is only allowed if the popover starts within text_bounds.
 9814        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9815            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9816
 9817        let mut origin = if can_position_to_the_right {
 9818            point(
 9819                x_after_longest,
 9820                text_bounds.origin.y
 9821                    + Pixels::from(
 9822                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9823                            - scroll_pixel_position.y,
 9824                    ),
 9825            )
 9826        } else {
 9827            let cursor_row = newest_selection_head.map(|head| head.row());
 9828            let above_edit = edit_start
 9829                .row()
 9830                .0
 9831                .checked_sub(line_count as u32)
 9832                .map(DisplayRow);
 9833            let below_edit = Some(edit_end.row() + 1);
 9834            let above_cursor =
 9835                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9836            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9837
 9838            // Place the edit popover adjacent to the edit if there is a location
 9839            // available that is onscreen and does not obscure the cursor. Otherwise,
 9840            // place it adjacent to the cursor.
 9841            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9842                .into_iter()
 9843                .flatten()
 9844                .find(|&start_row| {
 9845                    let end_row = start_row + line_count as u32;
 9846                    visible_row_range.contains(&start_row)
 9847                        && visible_row_range.contains(&end_row)
 9848                        && cursor_row
 9849                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9850                })?;
 9851
 9852            content_origin
 9853                + point(
 9854                    Pixels::from(-scroll_pixel_position.x),
 9855                    Pixels::from(
 9856                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9857                    ),
 9858                )
 9859        };
 9860
 9861        origin.x -= BORDER_WIDTH;
 9862
 9863        window.with_content_mask(
 9864            Some(gpui::ContentMask {
 9865                bounds: *text_bounds,
 9866            }),
 9867            |window| {
 9868                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9869            },
 9870        );
 9871
 9872        // Do not return an element, since it will already be drawn due to defer_draw.
 9873        None
 9874    }
 9875
 9876    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9877        px(30.)
 9878    }
 9879
 9880    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9881        if self.read_only(cx) {
 9882            cx.theme().players().read_only()
 9883        } else {
 9884            self.style.as_ref().unwrap().local_player
 9885        }
 9886    }
 9887
 9888    fn render_edit_prediction_inline_keystroke(
 9889        &self,
 9890        keystroke: &gpui::KeybindingKeystroke,
 9891        modifiers_color: Color,
 9892        cx: &App,
 9893    ) -> AnyElement {
 9894        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9895
 9896        h_flex()
 9897            .px_0p5()
 9898            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9899            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9900            .text_size(TextSize::XSmall.rems(cx))
 9901            .child(h_flex().children(ui::render_modifiers(
 9902                keystroke.modifiers(),
 9903                PlatformStyle::platform(),
 9904                Some(modifiers_color),
 9905                Some(IconSize::XSmall.rems().into()),
 9906                true,
 9907            )))
 9908            .when(is_platform_style_mac, |parent| {
 9909                parent.child(keystroke.key().to_string())
 9910            })
 9911            .when(!is_platform_style_mac, |parent| {
 9912                parent.child(
 9913                    Key::new(util::capitalize(keystroke.key()), Some(Color::Default))
 9914                        .size(Some(IconSize::XSmall.rems().into())),
 9915                )
 9916            })
 9917            .into_any()
 9918    }
 9919
 9920    fn render_edit_prediction_popover_keystroke(
 9921        &self,
 9922        keystroke: &gpui::KeybindingKeystroke,
 9923        color: Color,
 9924        cx: &App,
 9925    ) -> AnyElement {
 9926        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9927
 9928        if keystroke.modifiers().modified() {
 9929            h_flex()
 9930                .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9931                .when(is_platform_style_mac, |parent| parent.gap_1())
 9932                .child(h_flex().children(ui::render_modifiers(
 9933                    keystroke.modifiers(),
 9934                    PlatformStyle::platform(),
 9935                    Some(color),
 9936                    None,
 9937                    false,
 9938                )))
 9939                .into_any()
 9940        } else {
 9941            Key::new(util::capitalize(keystroke.key()), Some(color))
 9942                .size(Some(IconSize::XSmall.rems().into()))
 9943                .into_any_element()
 9944        }
 9945    }
 9946
 9947    fn render_edit_prediction_keybind(
 9948        &self,
 9949        window: &mut Window,
 9950        cx: &mut App,
 9951    ) -> Option<AnyElement> {
 9952        let keybind_display =
 9953            self.edit_prediction_keybind_display(EditPredictionKeybindSurface::Inline, window, cx);
 9954        let keystroke = keybind_display.displayed_keystroke.as_ref()?;
 9955
 9956        let modifiers_color = if *keystroke.modifiers() == window.modifiers() {
 9957            Color::Accent
 9958        } else {
 9959            Color::Muted
 9960        };
 9961
 9962        Some(self.render_edit_prediction_inline_keystroke(keystroke, modifiers_color, cx))
 9963    }
 9964
 9965    fn render_edit_prediction_line_popover(
 9966        &self,
 9967        label: impl Into<SharedString>,
 9968        icon: Option<IconName>,
 9969        window: &mut Window,
 9970        cx: &mut App,
 9971    ) -> Stateful<Div> {
 9972        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9973
 9974        let keybind = self.render_edit_prediction_keybind(window, cx);
 9975        let has_keybind = keybind.is_some();
 9976        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9977
 9978        h_flex()
 9979            .id("ep-line-popover")
 9980            .py_0p5()
 9981            .pl_1()
 9982            .pr(padding_right)
 9983            .gap_1()
 9984            .rounded_md()
 9985            .border_1()
 9986            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9987            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9988            .shadow_xs()
 9989            .when(!has_keybind, |el| {
 9990                let status_colors = cx.theme().status();
 9991
 9992                el.bg(status_colors.error_background)
 9993                    .border_color(status_colors.error.opacity(0.6))
 9994                    .pl_2()
 9995                    .child(Icon::new(icons.error).color(Color::Error))
 9996                    .cursor_default()
 9997                    .hoverable_tooltip(move |_window, cx| {
 9998                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9999                    })
10000            })
10001            .children(keybind)
10002            .child(
10003                Label::new(label)
10004                    .size(LabelSize::Small)
10005                    .when(!has_keybind, |el| {
10006                        el.color(cx.theme().status().error.into()).strikethrough()
10007                    }),
10008            )
10009            .when(!has_keybind, |el| {
10010                el.child(
10011                    h_flex().ml_1().child(
10012                        Icon::new(IconName::Info)
10013                            .size(IconSize::Small)
10014                            .color(cx.theme().status().error.into()),
10015                    ),
10016                )
10017            })
10018            .when_some(icon, |element, icon| {
10019                element.child(
10020                    div()
10021                        .mt(px(1.5))
10022                        .child(Icon::new(icon).size(IconSize::Small)),
10023                )
10024            })
10025    }
10026
10027    fn render_edit_prediction_jump_outside_popover(
10028        &self,
10029        snapshot: &BufferSnapshot,
10030        window: &mut Window,
10031        cx: &mut App,
10032    ) -> Stateful<Div> {
10033        let keybind = self.render_edit_prediction_keybind(window, cx);
10034        let has_keybind = keybind.is_some();
10035        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10036
10037        let file_name = snapshot
10038            .file()
10039            .map(|file| SharedString::new(file.file_name(cx)))
10040            .unwrap_or(SharedString::new_static("untitled"));
10041
10042        h_flex()
10043            .id("ep-jump-outside-popover")
10044            .py_1()
10045            .px_2()
10046            .gap_1()
10047            .rounded_md()
10048            .border_1()
10049            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10050            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10051            .shadow_xs()
10052            .when(!has_keybind, |el| {
10053                let status_colors = cx.theme().status();
10054
10055                el.bg(status_colors.error_background)
10056                    .border_color(status_colors.error.opacity(0.6))
10057                    .pl_2()
10058                    .child(Icon::new(icons.error).color(Color::Error))
10059                    .cursor_default()
10060                    .hoverable_tooltip(move |_window, cx| {
10061                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10062                    })
10063            })
10064            .children(keybind)
10065            .child(
10066                Label::new(file_name)
10067                    .size(LabelSize::Small)
10068                    .buffer_font(cx)
10069                    .when(!has_keybind, |el| {
10070                        el.color(cx.theme().status().error.into()).strikethrough()
10071                    }),
10072            )
10073            .when(!has_keybind, |el| {
10074                el.child(
10075                    h_flex().ml_1().child(
10076                        Icon::new(IconName::Info)
10077                            .size(IconSize::Small)
10078                            .color(cx.theme().status().error.into()),
10079                    ),
10080                )
10081            })
10082            .child(
10083                div()
10084                    .mt(px(1.5))
10085                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10086            )
10087    }
10088
10089    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10090        let accent_color = cx.theme().colors().text_accent;
10091        let editor_bg_color = cx.theme().colors().editor_background;
10092        editor_bg_color.blend(accent_color.opacity(0.1))
10093    }
10094
10095    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10096        let accent_color = cx.theme().colors().text_accent;
10097        let editor_bg_color = cx.theme().colors().editor_background;
10098        editor_bg_color.blend(accent_color.opacity(0.6))
10099    }
10100    fn get_prediction_provider_icons(
10101        provider: &Option<RegisteredEditPredictionDelegate>,
10102        cx: &App,
10103    ) -> edit_prediction_types::EditPredictionIconSet {
10104        match provider {
10105            Some(provider) => provider.provider.icons(cx),
10106            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10107        }
10108    }
10109
10110    fn render_edit_prediction_cursor_popover(
10111        &self,
10112        min_width: Pixels,
10113        max_width: Pixels,
10114        cursor_point: Point,
10115        style: &EditorStyle,
10116        window: &mut Window,
10117        cx: &mut Context<Editor>,
10118    ) -> Option<AnyElement> {
10119        let provider = self.edit_prediction_provider.as_ref()?;
10120        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10121
10122        let is_refreshing = provider.provider.is_refreshing(cx);
10123
10124        fn pending_completion_container(icon: IconName) -> Div {
10125            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10126        }
10127
10128        let completion = match &self.active_edit_prediction {
10129            Some(prediction) => {
10130                if !self.has_visible_completions_menu() {
10131                    const RADIUS: Pixels = px(6.);
10132                    const BORDER_WIDTH: Pixels = px(1.);
10133                    let keybind_display = self.edit_prediction_keybind_display(
10134                        EditPredictionKeybindSurface::CursorPopoverCompact,
10135                        window,
10136                        cx,
10137                    );
10138
10139                    return Some(
10140                        h_flex()
10141                            .elevation_2(cx)
10142                            .border(BORDER_WIDTH)
10143                            .border_color(cx.theme().colors().border)
10144                            .when(keybind_display.missing_accept_keystroke, |el| {
10145                                el.border_color(cx.theme().status().error)
10146                            })
10147                            .rounded(RADIUS)
10148                            .rounded_tl(px(0.))
10149                            .overflow_hidden()
10150                            .child(div().px_1p5().child(match &prediction.completion {
10151                                EditPrediction::MoveWithin { target, snapshot } => {
10152                                    use text::ToPoint as _;
10153                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10154                                    {
10155                                        Icon::new(icons.down)
10156                                    } else {
10157                                        Icon::new(icons.up)
10158                                    }
10159                                }
10160                                EditPrediction::MoveOutside { .. } => {
10161                                    // TODO [zeta2] custom icon for external jump?
10162                                    Icon::new(icons.base)
10163                                }
10164                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10165                            }))
10166                            .child(
10167                                h_flex()
10168                                    .gap_1()
10169                                    .py_1()
10170                                    .px_2()
10171                                    .rounded_r(RADIUS - BORDER_WIDTH)
10172                                    .border_l_1()
10173                                    .border_color(cx.theme().colors().border)
10174                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10175                                    .when(keybind_display.show_hold_label, |el| {
10176                                        el.child(
10177                                            Label::new("Hold")
10178                                                .size(LabelSize::Small)
10179                                                .when(
10180                                                    keybind_display.missing_accept_keystroke,
10181                                                    |el| el.strikethrough(),
10182                                                )
10183                                                .line_height_style(LineHeightStyle::UiLabel),
10184                                        )
10185                                    })
10186                                    .id("edit_prediction_cursor_popover_keybind")
10187                                    .when(keybind_display.missing_accept_keystroke, |el| {
10188                                        let status_colors = cx.theme().status();
10189
10190                                        el.bg(status_colors.error_background)
10191                                            .border_color(status_colors.error.opacity(0.6))
10192                                            .child(Icon::new(IconName::Info).color(Color::Error))
10193                                            .cursor_default()
10194                                            .hoverable_tooltip(move |_window, cx| {
10195                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10196                                                    .into()
10197                                            })
10198                                    })
10199                                    .when_some(
10200                                        keybind_display.displayed_keystroke.as_ref(),
10201                                        |el, compact_keystroke| {
10202                                            el.child(self.render_edit_prediction_popover_keystroke(
10203                                                compact_keystroke,
10204                                                Color::Default,
10205                                                cx,
10206                                            ))
10207                                        },
10208                                    ),
10209                            )
10210                            .into_any(),
10211                    );
10212                }
10213
10214                self.render_edit_prediction_cursor_popover_preview(
10215                    prediction,
10216                    cursor_point,
10217                    style,
10218                    cx,
10219                )?
10220            }
10221
10222            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10223                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10224                    stale_completion,
10225                    cursor_point,
10226                    style,
10227                    cx,
10228                )?,
10229
10230                None => pending_completion_container(icons.base)
10231                    .child(Label::new("...").size(LabelSize::Small)),
10232            },
10233
10234            None => pending_completion_container(icons.base)
10235                .child(Label::new("...").size(LabelSize::Small)),
10236        };
10237
10238        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10239            completion
10240                .with_animation(
10241                    "loading-completion",
10242                    Animation::new(Duration::from_secs(2))
10243                        .repeat()
10244                        .with_easing(pulsating_between(0.4, 0.8)),
10245                    |label, delta| label.opacity(delta),
10246                )
10247                .into_any_element()
10248        } else {
10249            completion.into_any_element()
10250        };
10251
10252        let has_completion = self.active_edit_prediction.is_some();
10253        let keybind_display = self.edit_prediction_keybind_display(
10254            EditPredictionKeybindSurface::CursorPopoverExpanded,
10255            window,
10256            cx,
10257        );
10258
10259        Some(
10260            h_flex()
10261                .min_w(min_width)
10262                .max_w(max_width)
10263                .flex_1()
10264                .elevation_2(cx)
10265                .border_color(cx.theme().colors().border)
10266                .child(
10267                    div()
10268                        .flex_1()
10269                        .py_1()
10270                        .px_2()
10271                        .overflow_hidden()
10272                        .child(completion),
10273                )
10274                .when_some(
10275                    keybind_display.displayed_keystroke.as_ref(),
10276                    |el, keystroke| {
10277                        let key_color = if !has_completion {
10278                            Color::Muted
10279                        } else {
10280                            Color::Default
10281                        };
10282
10283                        if keybind_display.action == EditPredictionKeybindAction::Preview {
10284                            el.child(
10285                                h_flex()
10286                                    .h_full()
10287                                    .border_l_1()
10288                                    .rounded_r_lg()
10289                                    .border_color(cx.theme().colors().border)
10290                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10291                                    .gap_1()
10292                                    .py_1()
10293                                    .px_2()
10294                                    .child(self.render_edit_prediction_popover_keystroke(
10295                                        keystroke, key_color, cx,
10296                                    ))
10297                                    .child(Label::new("Preview").into_any_element())
10298                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10299                            )
10300                        } else {
10301                            el.child(
10302                                h_flex()
10303                                    .h_full()
10304                                    .border_l_1()
10305                                    .rounded_r_lg()
10306                                    .border_color(cx.theme().colors().border)
10307                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10308                                    .gap_1()
10309                                    .py_1()
10310                                    .px_2()
10311                                    .child(self.render_edit_prediction_popover_keystroke(
10312                                        keystroke, key_color, cx,
10313                                    ))
10314                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10315                            )
10316                        }
10317                    },
10318                )
10319                .into_any(),
10320        )
10321    }
10322
10323    fn render_edit_prediction_cursor_popover_preview(
10324        &self,
10325        completion: &EditPredictionState,
10326        cursor_point: Point,
10327        style: &EditorStyle,
10328        cx: &mut Context<Editor>,
10329    ) -> Option<Div> {
10330        use text::ToPoint as _;
10331
10332        fn render_relative_row_jump(
10333            prefix: impl Into<String>,
10334            current_row: u32,
10335            target_row: u32,
10336        ) -> Div {
10337            let (row_diff, arrow) = if target_row < current_row {
10338                (current_row - target_row, IconName::ArrowUp)
10339            } else {
10340                (target_row - current_row, IconName::ArrowDown)
10341            };
10342
10343            h_flex()
10344                .child(
10345                    Label::new(format!("{}{}", prefix.into(), row_diff))
10346                        .color(Color::Muted)
10347                        .size(LabelSize::Small),
10348                )
10349                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10350        }
10351
10352        let supports_jump = self
10353            .edit_prediction_provider
10354            .as_ref()
10355            .map(|provider| provider.provider.supports_jump_to_edit())
10356            .unwrap_or(true);
10357
10358        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10359
10360        match &completion.completion {
10361            EditPrediction::MoveWithin {
10362                target, snapshot, ..
10363            } => {
10364                if !supports_jump {
10365                    return None;
10366                }
10367
10368                Some(
10369                    h_flex()
10370                        .px_2()
10371                        .gap_2()
10372                        .flex_1()
10373                        .child(
10374                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10375                                Icon::new(icons.down)
10376                            } else {
10377                                Icon::new(icons.up)
10378                            },
10379                        )
10380                        .child(Label::new("Jump to Edit")),
10381                )
10382            }
10383            EditPrediction::MoveOutside { snapshot, .. } => {
10384                let file_name = snapshot
10385                    .file()
10386                    .map(|file| file.file_name(cx))
10387                    .unwrap_or("untitled");
10388                Some(
10389                    h_flex()
10390                        .px_2()
10391                        .gap_2()
10392                        .flex_1()
10393                        .child(Icon::new(icons.base))
10394                        .child(Label::new(format!("Jump to {file_name}"))),
10395                )
10396            }
10397            EditPrediction::Edit {
10398                edits,
10399                edit_preview,
10400                snapshot,
10401                ..
10402            } => {
10403                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10404
10405                let (highlighted_edits, has_more_lines) =
10406                    if let Some(edit_preview) = edit_preview.as_ref() {
10407                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10408                            .first_line_preview()
10409                    } else {
10410                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10411                    };
10412
10413                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10414                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10415
10416                let preview = h_flex()
10417                    .gap_1()
10418                    .min_w_16()
10419                    .child(styled_text)
10420                    .when(has_more_lines, |parent| parent.child(""));
10421
10422                let left = if supports_jump && first_edit_row != cursor_point.row {
10423                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10424                        .into_any_element()
10425                } else {
10426                    Icon::new(icons.base).into_any_element()
10427                };
10428
10429                Some(
10430                    h_flex()
10431                        .h_full()
10432                        .flex_1()
10433                        .gap_2()
10434                        .pr_1()
10435                        .overflow_x_hidden()
10436                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10437                        .child(left)
10438                        .child(preview),
10439                )
10440            }
10441        }
10442    }
10443
10444    pub fn render_context_menu(
10445        &mut self,
10446        max_height_in_lines: u32,
10447        window: &mut Window,
10448        cx: &mut Context<Editor>,
10449    ) -> Option<AnyElement> {
10450        let menu = self.context_menu.borrow();
10451        let menu = menu.as_ref()?;
10452        if !menu.visible() {
10453            return None;
10454        };
10455        self.style
10456            .as_ref()
10457            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10458    }
10459
10460    fn render_context_menu_aside(
10461        &mut self,
10462        max_size: Size<Pixels>,
10463        window: &mut Window,
10464        cx: &mut Context<Editor>,
10465    ) -> Option<AnyElement> {
10466        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10467            if menu.visible() {
10468                menu.render_aside(max_size, window, cx)
10469            } else {
10470                None
10471            }
10472        })
10473    }
10474
10475    fn hide_context_menu(
10476        &mut self,
10477        window: &mut Window,
10478        cx: &mut Context<Self>,
10479    ) -> Option<CodeContextMenu> {
10480        cx.notify();
10481        self.completion_tasks.clear();
10482        let context_menu = self.context_menu.borrow_mut().take();
10483        self.stale_edit_prediction_in_menu.take();
10484        self.update_visible_edit_prediction(window, cx);
10485        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10486            && let Some(completion_provider) = &self.completion_provider
10487        {
10488            completion_provider.selection_changed(None, window, cx);
10489        }
10490        context_menu
10491    }
10492
10493    fn show_snippet_choices(
10494        &mut self,
10495        choices: &Vec<String>,
10496        selection: Range<Anchor>,
10497        cx: &mut Context<Self>,
10498    ) {
10499        let Some((_, buffer, _)) = self
10500            .buffer()
10501            .read(cx)
10502            .excerpt_containing(selection.start, cx)
10503        else {
10504            return;
10505        };
10506        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10507        else {
10508            return;
10509        };
10510        if buffer != end_buffer {
10511            log::error!("expected anchor range to have matching buffer IDs");
10512            return;
10513        }
10514
10515        let id = post_inc(&mut self.next_completion_id);
10516        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10517        let mut context_menu = self.context_menu.borrow_mut();
10518        let old_menu = context_menu.take();
10519        *context_menu = Some(CodeContextMenu::Completions(
10520            CompletionsMenu::new_snippet_choices(
10521                id,
10522                true,
10523                choices,
10524                selection,
10525                buffer,
10526                old_menu.map(|menu| menu.primary_scroll_handle()),
10527                snippet_sort_order,
10528            ),
10529        ));
10530    }
10531
10532    pub fn insert_snippet(
10533        &mut self,
10534        insertion_ranges: &[Range<MultiBufferOffset>],
10535        snippet: Snippet,
10536        window: &mut Window,
10537        cx: &mut Context<Self>,
10538    ) -> Result<()> {
10539        struct Tabstop<T> {
10540            is_end_tabstop: bool,
10541            ranges: Vec<Range<T>>,
10542            choices: Option<Vec<String>>,
10543        }
10544
10545        let tabstops = self.buffer.update(cx, |buffer, cx| {
10546            let snippet_text: Arc<str> = snippet.text.clone().into();
10547            let edits = insertion_ranges
10548                .iter()
10549                .cloned()
10550                .map(|range| (range, snippet_text.clone()));
10551            let autoindent_mode = AutoindentMode::Block {
10552                original_indent_columns: Vec::new(),
10553            };
10554            buffer.edit(edits, Some(autoindent_mode), cx);
10555
10556            let snapshot = &*buffer.read(cx);
10557            let snippet = &snippet;
10558            snippet
10559                .tabstops
10560                .iter()
10561                .map(|tabstop| {
10562                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10563                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10564                    });
10565                    let mut tabstop_ranges = tabstop
10566                        .ranges
10567                        .iter()
10568                        .flat_map(|tabstop_range| {
10569                            let mut delta = 0_isize;
10570                            insertion_ranges.iter().map(move |insertion_range| {
10571                                let insertion_start = insertion_range.start + delta;
10572                                delta += snippet.text.len() as isize
10573                                    - (insertion_range.end - insertion_range.start) as isize;
10574
10575                                let start =
10576                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10577                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10578                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10579                            })
10580                        })
10581                        .collect::<Vec<_>>();
10582                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10583
10584                    Tabstop {
10585                        is_end_tabstop,
10586                        ranges: tabstop_ranges,
10587                        choices: tabstop.choices.clone(),
10588                    }
10589                })
10590                .collect::<Vec<_>>()
10591        });
10592        if let Some(tabstop) = tabstops.first() {
10593            self.change_selections(Default::default(), window, cx, |s| {
10594                // Reverse order so that the first range is the newest created selection.
10595                // Completions will use it and autoscroll will prioritize it.
10596                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10597            });
10598
10599            if let Some(choices) = &tabstop.choices
10600                && let Some(selection) = tabstop.ranges.first()
10601            {
10602                self.show_snippet_choices(choices, selection.clone(), cx)
10603            }
10604
10605            // If we're already at the last tabstop and it's at the end of the snippet,
10606            // we're done, we don't need to keep the state around.
10607            if !tabstop.is_end_tabstop {
10608                let choices = tabstops
10609                    .iter()
10610                    .map(|tabstop| tabstop.choices.clone())
10611                    .collect();
10612
10613                let ranges = tabstops
10614                    .into_iter()
10615                    .map(|tabstop| tabstop.ranges)
10616                    .collect::<Vec<_>>();
10617
10618                self.snippet_stack.push(SnippetState {
10619                    active_index: 0,
10620                    ranges,
10621                    choices,
10622                });
10623            }
10624
10625            // Check whether the just-entered snippet ends with an auto-closable bracket.
10626            if self.autoclose_regions.is_empty() {
10627                let snapshot = self.buffer.read(cx).snapshot(cx);
10628                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10629                    let selection_head = selection.head();
10630                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10631                        continue;
10632                    };
10633
10634                    let mut bracket_pair = None;
10635                    let max_lookup_length = scope
10636                        .brackets()
10637                        .map(|(pair, _)| {
10638                            pair.start
10639                                .as_str()
10640                                .chars()
10641                                .count()
10642                                .max(pair.end.as_str().chars().count())
10643                        })
10644                        .max();
10645                    if let Some(max_lookup_length) = max_lookup_length {
10646                        let next_text = snapshot
10647                            .chars_at(selection_head)
10648                            .take(max_lookup_length)
10649                            .collect::<String>();
10650                        let prev_text = snapshot
10651                            .reversed_chars_at(selection_head)
10652                            .take(max_lookup_length)
10653                            .collect::<String>();
10654
10655                        for (pair, enabled) in scope.brackets() {
10656                            if enabled
10657                                && pair.close
10658                                && prev_text.starts_with(pair.start.as_str())
10659                                && next_text.starts_with(pair.end.as_str())
10660                            {
10661                                bracket_pair = Some(pair.clone());
10662                                break;
10663                            }
10664                        }
10665                    }
10666
10667                    if let Some(pair) = bracket_pair {
10668                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10669                        let autoclose_enabled =
10670                            self.use_autoclose && snapshot_settings.use_autoclose;
10671                        if autoclose_enabled {
10672                            let start = snapshot.anchor_after(selection_head);
10673                            let end = snapshot.anchor_after(selection_head);
10674                            self.autoclose_regions.push(AutocloseRegion {
10675                                selection_id: selection.id,
10676                                range: start..end,
10677                                pair,
10678                            });
10679                        }
10680                    }
10681                }
10682            }
10683        }
10684        Ok(())
10685    }
10686
10687    pub fn move_to_next_snippet_tabstop(
10688        &mut self,
10689        window: &mut Window,
10690        cx: &mut Context<Self>,
10691    ) -> bool {
10692        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10693    }
10694
10695    pub fn move_to_prev_snippet_tabstop(
10696        &mut self,
10697        window: &mut Window,
10698        cx: &mut Context<Self>,
10699    ) -> bool {
10700        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10701    }
10702
10703    pub fn move_to_snippet_tabstop(
10704        &mut self,
10705        bias: Bias,
10706        window: &mut Window,
10707        cx: &mut Context<Self>,
10708    ) -> bool {
10709        if let Some(mut snippet) = self.snippet_stack.pop() {
10710            match bias {
10711                Bias::Left => {
10712                    if snippet.active_index > 0 {
10713                        snippet.active_index -= 1;
10714                    } else {
10715                        self.snippet_stack.push(snippet);
10716                        return false;
10717                    }
10718                }
10719                Bias::Right => {
10720                    if snippet.active_index + 1 < snippet.ranges.len() {
10721                        snippet.active_index += 1;
10722                    } else {
10723                        self.snippet_stack.push(snippet);
10724                        return false;
10725                    }
10726                }
10727            }
10728            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10729                self.change_selections(Default::default(), window, cx, |s| {
10730                    // Reverse order so that the first range is the newest created selection.
10731                    // Completions will use it and autoscroll will prioritize it.
10732                    s.select_ranges(current_ranges.iter().rev().cloned())
10733                });
10734
10735                if let Some(choices) = &snippet.choices[snippet.active_index]
10736                    && let Some(selection) = current_ranges.first()
10737                {
10738                    self.show_snippet_choices(choices, selection.clone(), cx);
10739                }
10740
10741                // If snippet state is not at the last tabstop, push it back on the stack
10742                if snippet.active_index + 1 < snippet.ranges.len() {
10743                    self.snippet_stack.push(snippet);
10744                }
10745                return true;
10746            }
10747        }
10748
10749        false
10750    }
10751
10752    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10753        self.transact(window, cx, |this, window, cx| {
10754            this.select_all(&SelectAll, window, cx);
10755            this.insert("", window, cx);
10756        });
10757    }
10758
10759    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10760        if self.read_only(cx) {
10761            return;
10762        }
10763        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10764        self.transact(window, cx, |this, window, cx| {
10765            this.select_autoclose_pair(window, cx);
10766
10767            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10768
10769            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10770            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10771            for selection in &mut selections {
10772                if selection.is_empty() {
10773                    let old_head = selection.head();
10774                    let mut new_head =
10775                        movement::left(&display_map, old_head.to_display_point(&display_map))
10776                            .to_point(&display_map);
10777                    if let Some((buffer, line_buffer_range)) = display_map
10778                        .buffer_snapshot()
10779                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10780                    {
10781                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10782                        let indent_len = match indent_size.kind {
10783                            IndentKind::Space => {
10784                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10785                            }
10786                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10787                        };
10788                        if old_head.column <= indent_size.len && old_head.column > 0 {
10789                            let indent_len = indent_len.get();
10790                            new_head = cmp::min(
10791                                new_head,
10792                                MultiBufferPoint::new(
10793                                    old_head.row,
10794                                    ((old_head.column - 1) / indent_len) * indent_len,
10795                                ),
10796                            );
10797                        }
10798                    }
10799
10800                    selection.set_head(new_head, SelectionGoal::None);
10801                }
10802            }
10803
10804            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10805            this.insert("", window, cx);
10806            linked_edits.apply_with_left_expansion(cx);
10807            this.refresh_edit_prediction(true, false, window, cx);
10808            refresh_linked_ranges(this, window, cx);
10809        });
10810    }
10811
10812    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10813        if self.read_only(cx) {
10814            return;
10815        }
10816        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10817        self.transact(window, cx, |this, window, cx| {
10818            this.change_selections(Default::default(), window, cx, |s| {
10819                s.move_with(&mut |map, selection| {
10820                    if selection.is_empty() {
10821                        let cursor = movement::right(map, selection.head());
10822                        selection.end = cursor;
10823                        selection.reversed = true;
10824                        selection.goal = SelectionGoal::None;
10825                    }
10826                })
10827            });
10828            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10829            this.insert("", window, cx);
10830            linked_edits.apply(cx);
10831            this.refresh_edit_prediction(true, false, window, cx);
10832            refresh_linked_ranges(this, window, cx);
10833        });
10834    }
10835
10836    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10837        if self.mode.is_single_line() {
10838            cx.propagate();
10839            return;
10840        }
10841
10842        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10843        if self.move_to_prev_snippet_tabstop(window, cx) {
10844            return;
10845        }
10846        self.outdent(&Outdent, window, cx);
10847    }
10848
10849    pub fn next_snippet_tabstop(
10850        &mut self,
10851        _: &NextSnippetTabstop,
10852        window: &mut Window,
10853        cx: &mut Context<Self>,
10854    ) {
10855        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10856            cx.propagate();
10857            return;
10858        }
10859
10860        if self.move_to_next_snippet_tabstop(window, cx) {
10861            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10862            return;
10863        }
10864        cx.propagate();
10865    }
10866
10867    pub fn previous_snippet_tabstop(
10868        &mut self,
10869        _: &PreviousSnippetTabstop,
10870        window: &mut Window,
10871        cx: &mut Context<Self>,
10872    ) {
10873        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10874            cx.propagate();
10875            return;
10876        }
10877
10878        if self.move_to_prev_snippet_tabstop(window, cx) {
10879            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10880            return;
10881        }
10882        cx.propagate();
10883    }
10884
10885    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10886        if self.mode.is_single_line() {
10887            cx.propagate();
10888            return;
10889        }
10890
10891        if self.move_to_next_snippet_tabstop(window, cx) {
10892            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10893            return;
10894        }
10895        if self.read_only(cx) {
10896            return;
10897        }
10898        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10899        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10900        let buffer = self.buffer.read(cx);
10901        let snapshot = buffer.snapshot(cx);
10902        let rows_iter = selections.iter().map(|s| s.head().row);
10903        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10904
10905        let has_some_cursor_in_whitespace = selections
10906            .iter()
10907            .filter(|selection| selection.is_empty())
10908            .any(|selection| {
10909                let cursor = selection.head();
10910                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10911                cursor.column < current_indent.len
10912            });
10913
10914        let mut edits = Vec::new();
10915        let mut prev_edited_row = 0;
10916        let mut row_delta = 0;
10917        for selection in &mut selections {
10918            if selection.start.row != prev_edited_row {
10919                row_delta = 0;
10920            }
10921            prev_edited_row = selection.end.row;
10922
10923            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10924            if selection.is_empty() {
10925                let cursor = selection.head();
10926                let settings = buffer.language_settings_at(cursor, cx);
10927                if settings.indent_list_on_tab {
10928                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10929                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10930                            row_delta = Self::indent_selection(
10931                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10932                            );
10933                            continue;
10934                        }
10935                    }
10936                }
10937            }
10938
10939            // If the selection is non-empty, then increase the indentation of the selected lines.
10940            if !selection.is_empty() {
10941                row_delta =
10942                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10943                continue;
10944            }
10945
10946            let cursor = selection.head();
10947            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10948            if let Some(suggested_indent) =
10949                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10950            {
10951                // Don't do anything if already at suggested indent
10952                // and there is any other cursor which is not
10953                if has_some_cursor_in_whitespace
10954                    && cursor.column == current_indent.len
10955                    && current_indent.len == suggested_indent.len
10956                {
10957                    continue;
10958                }
10959
10960                // Adjust line and move cursor to suggested indent
10961                // if cursor is not at suggested indent
10962                if cursor.column < suggested_indent.len
10963                    && cursor.column <= current_indent.len
10964                    && current_indent.len <= suggested_indent.len
10965                {
10966                    selection.start = Point::new(cursor.row, suggested_indent.len);
10967                    selection.end = selection.start;
10968                    if row_delta == 0 {
10969                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10970                            cursor.row,
10971                            current_indent,
10972                            suggested_indent,
10973                        ));
10974                        row_delta = suggested_indent.len - current_indent.len;
10975                    }
10976                    continue;
10977                }
10978
10979                // If current indent is more than suggested indent
10980                // only move cursor to current indent and skip indent
10981                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10982                    selection.start = Point::new(cursor.row, current_indent.len);
10983                    selection.end = selection.start;
10984                    continue;
10985                }
10986            }
10987
10988            // Otherwise, insert a hard or soft tab.
10989            let settings = buffer.language_settings_at(cursor, cx);
10990            let tab_size = if settings.hard_tabs {
10991                IndentSize::tab()
10992            } else {
10993                let tab_size = settings.tab_size.get();
10994                let indent_remainder = snapshot
10995                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10996                    .flat_map(str::chars)
10997                    .fold(row_delta % tab_size, |counter: u32, c| {
10998                        if c == '\t' {
10999                            0
11000                        } else {
11001                            (counter + 1) % tab_size
11002                        }
11003                    });
11004
11005                let chars_to_next_tab_stop = tab_size - indent_remainder;
11006                IndentSize::spaces(chars_to_next_tab_stop)
11007            };
11008            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11009            selection.end = selection.start;
11010            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11011            row_delta += tab_size.len;
11012        }
11013
11014        self.transact(window, cx, |this, window, cx| {
11015            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11016            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11017            this.refresh_edit_prediction(true, false, window, cx);
11018        });
11019    }
11020
11021    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11022        if self.read_only(cx) {
11023            return;
11024        }
11025        if self.mode.is_single_line() {
11026            cx.propagate();
11027            return;
11028        }
11029
11030        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11031        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11032        let mut prev_edited_row = 0;
11033        let mut row_delta = 0;
11034        let mut edits = Vec::new();
11035        let buffer = self.buffer.read(cx);
11036        let snapshot = buffer.snapshot(cx);
11037        for selection in &mut selections {
11038            if selection.start.row != prev_edited_row {
11039                row_delta = 0;
11040            }
11041            prev_edited_row = selection.end.row;
11042
11043            row_delta =
11044                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11045        }
11046
11047        self.transact(window, cx, |this, window, cx| {
11048            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11049            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11050        });
11051    }
11052
11053    fn indent_selection(
11054        buffer: &MultiBuffer,
11055        snapshot: &MultiBufferSnapshot,
11056        selection: &mut Selection<Point>,
11057        edits: &mut Vec<(Range<Point>, String)>,
11058        delta_for_start_row: u32,
11059        cx: &App,
11060    ) -> u32 {
11061        let settings = buffer.language_settings_at(selection.start, cx);
11062        let tab_size = settings.tab_size.get();
11063        let indent_kind = if settings.hard_tabs {
11064            IndentKind::Tab
11065        } else {
11066            IndentKind::Space
11067        };
11068        let mut start_row = selection.start.row;
11069        let mut end_row = selection.end.row + 1;
11070
11071        // If a selection ends at the beginning of a line, don't indent
11072        // that last line.
11073        if selection.end.column == 0 && selection.end.row > selection.start.row {
11074            end_row -= 1;
11075        }
11076
11077        // Avoid re-indenting a row that has already been indented by a
11078        // previous selection, but still update this selection's column
11079        // to reflect that indentation.
11080        if delta_for_start_row > 0 {
11081            start_row += 1;
11082            selection.start.column += delta_for_start_row;
11083            if selection.end.row == selection.start.row {
11084                selection.end.column += delta_for_start_row;
11085            }
11086        }
11087
11088        let mut delta_for_end_row = 0;
11089        let has_multiple_rows = start_row + 1 != end_row;
11090        for row in start_row..end_row {
11091            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11092            let indent_delta = match (current_indent.kind, indent_kind) {
11093                (IndentKind::Space, IndentKind::Space) => {
11094                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11095                    IndentSize::spaces(columns_to_next_tab_stop)
11096                }
11097                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11098                (_, IndentKind::Tab) => IndentSize::tab(),
11099            };
11100
11101            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11102                0
11103            } else {
11104                selection.start.column
11105            };
11106            let row_start = Point::new(row, start);
11107            edits.push((
11108                row_start..row_start,
11109                indent_delta.chars().collect::<String>(),
11110            ));
11111
11112            // Update this selection's endpoints to reflect the indentation.
11113            if row == selection.start.row {
11114                selection.start.column += indent_delta.len;
11115            }
11116            if row == selection.end.row {
11117                selection.end.column += indent_delta.len;
11118                delta_for_end_row = indent_delta.len;
11119            }
11120        }
11121
11122        if selection.start.row == selection.end.row {
11123            delta_for_start_row + delta_for_end_row
11124        } else {
11125            delta_for_end_row
11126        }
11127    }
11128
11129    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11130        if self.read_only(cx) {
11131            return;
11132        }
11133        if self.mode.is_single_line() {
11134            cx.propagate();
11135            return;
11136        }
11137
11138        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11139        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11140        let selections = self.selections.all::<Point>(&display_map);
11141        let mut deletion_ranges = Vec::new();
11142        let mut last_outdent = None;
11143        {
11144            let buffer = self.buffer.read(cx);
11145            let snapshot = buffer.snapshot(cx);
11146            for selection in &selections {
11147                let settings = buffer.language_settings_at(selection.start, cx);
11148                let tab_size = settings.tab_size.get();
11149                let mut rows = selection.spanned_rows(false, &display_map);
11150
11151                // Avoid re-outdenting a row that has already been outdented by a
11152                // previous selection.
11153                if let Some(last_row) = last_outdent
11154                    && last_row == rows.start
11155                {
11156                    rows.start = rows.start.next_row();
11157                }
11158                let has_multiple_rows = rows.len() > 1;
11159                for row in rows.iter_rows() {
11160                    let indent_size = snapshot.indent_size_for_line(row);
11161                    if indent_size.len > 0 {
11162                        let deletion_len = match indent_size.kind {
11163                            IndentKind::Space => {
11164                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11165                                if columns_to_prev_tab_stop == 0 {
11166                                    tab_size
11167                                } else {
11168                                    columns_to_prev_tab_stop
11169                                }
11170                            }
11171                            IndentKind::Tab => 1,
11172                        };
11173                        let start = if has_multiple_rows
11174                            || deletion_len > selection.start.column
11175                            || indent_size.len < selection.start.column
11176                        {
11177                            0
11178                        } else {
11179                            selection.start.column - deletion_len
11180                        };
11181                        deletion_ranges.push(
11182                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11183                        );
11184                        last_outdent = Some(row);
11185                    }
11186                }
11187            }
11188        }
11189
11190        self.transact(window, cx, |this, window, cx| {
11191            this.buffer.update(cx, |buffer, cx| {
11192                let empty_str: Arc<str> = Arc::default();
11193                buffer.edit(
11194                    deletion_ranges
11195                        .into_iter()
11196                        .map(|range| (range, empty_str.clone())),
11197                    None,
11198                    cx,
11199                );
11200            });
11201            let selections = this
11202                .selections
11203                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11204            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11205        });
11206    }
11207
11208    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11209        if self.read_only(cx) {
11210            return;
11211        }
11212        if self.mode.is_single_line() {
11213            cx.propagate();
11214            return;
11215        }
11216
11217        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11218        let selections = self
11219            .selections
11220            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11221            .into_iter()
11222            .map(|s| s.range());
11223
11224        self.transact(window, cx, |this, window, cx| {
11225            this.buffer.update(cx, |buffer, cx| {
11226                buffer.autoindent_ranges(selections, cx);
11227            });
11228            let selections = this
11229                .selections
11230                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11231            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11232        });
11233    }
11234
11235    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11236        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11237        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11238        let selections = self.selections.all::<Point>(&display_map);
11239
11240        let mut new_cursors = Vec::new();
11241        let mut edit_ranges = Vec::new();
11242        let mut selections = selections.iter().peekable();
11243        while let Some(selection) = selections.next() {
11244            let mut rows = selection.spanned_rows(false, &display_map);
11245
11246            // Accumulate contiguous regions of rows that we want to delete.
11247            while let Some(next_selection) = selections.peek() {
11248                let next_rows = next_selection.spanned_rows(false, &display_map);
11249                if next_rows.start <= rows.end {
11250                    rows.end = next_rows.end;
11251                    selections.next().unwrap();
11252                } else {
11253                    break;
11254                }
11255            }
11256
11257            let buffer = display_map.buffer_snapshot();
11258            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11259            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11260                // If there's a line after the range, delete the \n from the end of the row range
11261                (
11262                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11263                    rows.end,
11264                )
11265            } else {
11266                // If there isn't a line after the range, delete the \n from the line before the
11267                // start of the row range
11268                edit_start = edit_start.saturating_sub_usize(1);
11269                (buffer.len(), rows.start.previous_row())
11270            };
11271
11272            let text_layout_details = self.text_layout_details(window, cx);
11273            let x = display_map.x_for_display_point(
11274                selection.head().to_display_point(&display_map),
11275                &text_layout_details,
11276            );
11277            let row = Point::new(target_row.0, 0)
11278                .to_display_point(&display_map)
11279                .row();
11280            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11281
11282            new_cursors.push((
11283                selection.id,
11284                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11285                SelectionGoal::None,
11286            ));
11287            edit_ranges.push(edit_start..edit_end);
11288        }
11289
11290        self.transact(window, cx, |this, window, cx| {
11291            let buffer = this.buffer.update(cx, |buffer, cx| {
11292                let empty_str: Arc<str> = Arc::default();
11293                buffer.edit(
11294                    edit_ranges
11295                        .into_iter()
11296                        .map(|range| (range, empty_str.clone())),
11297                    None,
11298                    cx,
11299                );
11300                buffer.snapshot(cx)
11301            });
11302            let new_selections = new_cursors
11303                .into_iter()
11304                .map(|(id, cursor, goal)| {
11305                    let cursor = cursor.to_point(&buffer);
11306                    Selection {
11307                        id,
11308                        start: cursor,
11309                        end: cursor,
11310                        reversed: false,
11311                        goal,
11312                    }
11313                })
11314                .collect();
11315
11316            this.change_selections(Default::default(), window, cx, |s| {
11317                s.select(new_selections);
11318            });
11319        });
11320    }
11321
11322    pub fn join_lines_impl(
11323        &mut self,
11324        insert_whitespace: bool,
11325        window: &mut Window,
11326        cx: &mut Context<Self>,
11327    ) {
11328        if self.read_only(cx) {
11329            return;
11330        }
11331        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11332        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11333            let start = MultiBufferRow(selection.start.row);
11334            // Treat single line selections as if they include the next line. Otherwise this action
11335            // would do nothing for single line selections individual cursors.
11336            let end = if selection.start.row == selection.end.row {
11337                MultiBufferRow(selection.start.row + 1)
11338            } else if selection.end.column == 0 {
11339                // If the selection ends at the start of a line, it's logically at the end of the
11340                // previous line (plus its newline).
11341                // Don't include the end line unless there's only one line selected.
11342                if selection.start.row + 1 == selection.end.row {
11343                    MultiBufferRow(selection.end.row)
11344                } else {
11345                    MultiBufferRow(selection.end.row - 1)
11346                }
11347            } else {
11348                MultiBufferRow(selection.end.row)
11349            };
11350
11351            if let Some(last_row_range) = row_ranges.last_mut()
11352                && start <= last_row_range.end
11353            {
11354                last_row_range.end = end;
11355                continue;
11356            }
11357            row_ranges.push(start..end);
11358        }
11359
11360        let snapshot = self.buffer.read(cx).snapshot(cx);
11361        let mut cursor_positions = Vec::new();
11362        for row_range in &row_ranges {
11363            let anchor = snapshot.anchor_before(Point::new(
11364                row_range.end.previous_row().0,
11365                snapshot.line_len(row_range.end.previous_row()),
11366            ));
11367            cursor_positions.push(anchor..anchor);
11368        }
11369
11370        self.transact(window, cx, |this, window, cx| {
11371            for row_range in row_ranges.into_iter().rev() {
11372                for row in row_range.iter_rows().rev() {
11373                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11374                    let next_line_row = row.next_row();
11375                    let indent = snapshot.indent_size_for_line(next_line_row);
11376                    let mut join_start_column = indent.len;
11377
11378                    if let Some(language_scope) =
11379                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11380                    {
11381                        let line_end =
11382                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11383                        let line_text_after_indent = snapshot
11384                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11385                            .collect::<String>();
11386
11387                        if !line_text_after_indent.is_empty() {
11388                            let block_prefix = language_scope
11389                                .block_comment()
11390                                .map(|c| c.prefix.as_ref())
11391                                .filter(|p| !p.is_empty());
11392                            let doc_prefix = language_scope
11393                                .documentation_comment()
11394                                .map(|c| c.prefix.as_ref())
11395                                .filter(|p| !p.is_empty());
11396                            let all_prefixes = language_scope
11397                                .line_comment_prefixes()
11398                                .iter()
11399                                .map(|p| p.as_ref())
11400                                .chain(block_prefix)
11401                                .chain(doc_prefix)
11402                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11403
11404                            let mut longest_prefix_len = None;
11405                            for prefix in all_prefixes {
11406                                let trimmed = prefix.trim_end();
11407                                if line_text_after_indent.starts_with(trimmed) {
11408                                    let candidate_len =
11409                                        if line_text_after_indent.starts_with(prefix) {
11410                                            prefix.len()
11411                                        } else {
11412                                            trimmed.len()
11413                                        };
11414                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11415                                        longest_prefix_len = Some(candidate_len);
11416                                    }
11417                                }
11418                            }
11419
11420                            if let Some(prefix_len) = longest_prefix_len {
11421                                join_start_column =
11422                                    join_start_column.saturating_add(prefix_len as u32);
11423                            }
11424                        }
11425                    }
11426
11427                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11428
11429                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11430                        && insert_whitespace
11431                    {
11432                        " "
11433                    } else {
11434                        ""
11435                    };
11436
11437                    this.buffer.update(cx, |buffer, cx| {
11438                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11439                    });
11440                }
11441            }
11442
11443            this.change_selections(Default::default(), window, cx, |s| {
11444                s.select_anchor_ranges(cursor_positions)
11445            });
11446        });
11447    }
11448
11449    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11450        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11451        self.join_lines_impl(true, window, cx);
11452    }
11453
11454    pub fn sort_lines_case_sensitive(
11455        &mut self,
11456        _: &SortLinesCaseSensitive,
11457        window: &mut Window,
11458        cx: &mut Context<Self>,
11459    ) {
11460        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11461    }
11462
11463    pub fn sort_lines_by_length(
11464        &mut self,
11465        _: &SortLinesByLength,
11466        window: &mut Window,
11467        cx: &mut Context<Self>,
11468    ) {
11469        self.manipulate_immutable_lines(window, cx, |lines| {
11470            lines.sort_by_key(|&line| line.chars().count())
11471        })
11472    }
11473
11474    pub fn sort_lines_case_insensitive(
11475        &mut self,
11476        _: &SortLinesCaseInsensitive,
11477        window: &mut Window,
11478        cx: &mut Context<Self>,
11479    ) {
11480        self.manipulate_immutable_lines(window, cx, |lines| {
11481            lines.sort_by_key(|line| line.to_lowercase())
11482        })
11483    }
11484
11485    pub fn unique_lines_case_insensitive(
11486        &mut self,
11487        _: &UniqueLinesCaseInsensitive,
11488        window: &mut Window,
11489        cx: &mut Context<Self>,
11490    ) {
11491        self.manipulate_immutable_lines(window, cx, |lines| {
11492            let mut seen = HashSet::default();
11493            lines.retain(|line| seen.insert(line.to_lowercase()));
11494        })
11495    }
11496
11497    pub fn unique_lines_case_sensitive(
11498        &mut self,
11499        _: &UniqueLinesCaseSensitive,
11500        window: &mut Window,
11501        cx: &mut Context<Self>,
11502    ) {
11503        self.manipulate_immutable_lines(window, cx, |lines| {
11504            let mut seen = HashSet::default();
11505            lines.retain(|line| seen.insert(*line));
11506        })
11507    }
11508
11509    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11510        let snapshot = self.buffer.read(cx).snapshot(cx);
11511        for selection in self.selections.disjoint_anchors_arc().iter() {
11512            if snapshot
11513                .language_at(selection.start)
11514                .and_then(|lang| lang.config().wrap_characters.as_ref())
11515                .is_some()
11516            {
11517                return true;
11518            }
11519        }
11520        false
11521    }
11522
11523    fn wrap_selections_in_tag(
11524        &mut self,
11525        _: &WrapSelectionsInTag,
11526        window: &mut Window,
11527        cx: &mut Context<Self>,
11528    ) {
11529        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11530
11531        let snapshot = self.buffer.read(cx).snapshot(cx);
11532
11533        let mut edits = Vec::new();
11534        let mut boundaries = Vec::new();
11535
11536        for selection in self
11537            .selections
11538            .all_adjusted(&self.display_snapshot(cx))
11539            .iter()
11540        {
11541            let Some(wrap_config) = snapshot
11542                .language_at(selection.start)
11543                .and_then(|lang| lang.config().wrap_characters.clone())
11544            else {
11545                continue;
11546            };
11547
11548            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11549            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11550
11551            let start_before = snapshot.anchor_before(selection.start);
11552            let end_after = snapshot.anchor_after(selection.end);
11553
11554            edits.push((start_before..start_before, open_tag));
11555            edits.push((end_after..end_after, close_tag));
11556
11557            boundaries.push((
11558                start_before,
11559                end_after,
11560                wrap_config.start_prefix.len(),
11561                wrap_config.end_suffix.len(),
11562            ));
11563        }
11564
11565        if edits.is_empty() {
11566            return;
11567        }
11568
11569        self.transact(window, cx, |this, window, cx| {
11570            let buffer = this.buffer.update(cx, |buffer, cx| {
11571                buffer.edit(edits, None, cx);
11572                buffer.snapshot(cx)
11573            });
11574
11575            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11576            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11577                boundaries.into_iter()
11578            {
11579                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11580                let close_offset = end_after
11581                    .to_offset(&buffer)
11582                    .saturating_sub_usize(end_suffix_len);
11583                new_selections.push(open_offset..open_offset);
11584                new_selections.push(close_offset..close_offset);
11585            }
11586
11587            this.change_selections(Default::default(), window, cx, |s| {
11588                s.select_ranges(new_selections);
11589            });
11590
11591            this.request_autoscroll(Autoscroll::fit(), cx);
11592        });
11593    }
11594
11595    pub fn toggle_read_only(
11596        &mut self,
11597        _: &workspace::ToggleReadOnlyFile,
11598        _: &mut Window,
11599        cx: &mut Context<Self>,
11600    ) {
11601        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11602            buffer.update(cx, |buffer, cx| {
11603                buffer.set_capability(
11604                    match buffer.capability() {
11605                        Capability::ReadWrite => Capability::Read,
11606                        Capability::Read => Capability::ReadWrite,
11607                        Capability::ReadOnly => Capability::ReadOnly,
11608                    },
11609                    cx,
11610                );
11611            })
11612        }
11613    }
11614
11615    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11616        let Some(project) = self.project.clone() else {
11617            return;
11618        };
11619        let task = self.reload(project, window, cx);
11620        self.detach_and_notify_err(task, window, cx);
11621    }
11622
11623    pub fn restore_file(
11624        &mut self,
11625        _: &::git::RestoreFile,
11626        window: &mut Window,
11627        cx: &mut Context<Self>,
11628    ) {
11629        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11630        let mut buffer_ids = HashSet::default();
11631        let snapshot = self.buffer().read(cx).snapshot(cx);
11632        for selection in self
11633            .selections
11634            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11635        {
11636            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11637        }
11638
11639        let buffer = self.buffer().read(cx);
11640        let ranges = buffer_ids
11641            .into_iter()
11642            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11643            .collect::<Vec<_>>();
11644
11645        self.restore_hunks_in_ranges(ranges, window, cx);
11646    }
11647
11648    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11649        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11650        let selections = self
11651            .selections
11652            .all(&self.display_snapshot(cx))
11653            .into_iter()
11654            .map(|s| s.range())
11655            .collect();
11656        self.restore_hunks_in_ranges(selections, window, cx);
11657    }
11658
11659    /// Restores the diff hunks in the editor's selections and moves the cursor
11660    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11661    /// not all diff hunks are expanded.
11662    pub fn restore_and_next(
11663        &mut self,
11664        _: &::git::RestoreAndNext,
11665        window: &mut Window,
11666        cx: &mut Context<Self>,
11667    ) {
11668        let selections = self
11669            .selections
11670            .all(&self.display_snapshot(cx))
11671            .into_iter()
11672            .map(|selection| selection.range())
11673            .collect();
11674
11675        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11676        self.restore_hunks_in_ranges(selections, window, cx);
11677
11678        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11679        let wrap_around = !all_diff_hunks_expanded;
11680        let snapshot = self.snapshot(window, cx);
11681        let position = self
11682            .selections
11683            .newest::<Point>(&snapshot.display_snapshot)
11684            .head();
11685
11686        self.go_to_hunk_before_or_after_position(
11687            &snapshot,
11688            position,
11689            Direction::Next,
11690            wrap_around,
11691            window,
11692            cx,
11693        );
11694    }
11695
11696    pub fn restore_hunks_in_ranges(
11697        &mut self,
11698        ranges: Vec<Range<Point>>,
11699        window: &mut Window,
11700        cx: &mut Context<Editor>,
11701    ) {
11702        if self.delegate_stage_and_restore {
11703            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11704            if !hunks.is_empty() {
11705                cx.emit(EditorEvent::RestoreRequested { hunks });
11706            }
11707            return;
11708        }
11709        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11710        self.transact(window, cx, |editor, window, cx| {
11711            editor.restore_diff_hunks(hunks, cx);
11712            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11713                selections.refresh()
11714            });
11715        });
11716    }
11717
11718    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11719        let mut revert_changes = HashMap::default();
11720        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11721        for (buffer_id, hunks) in &chunk_by {
11722            let hunks = hunks.collect::<Vec<_>>();
11723            for hunk in &hunks {
11724                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11725            }
11726            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11727        }
11728        if !revert_changes.is_empty() {
11729            self.buffer().update(cx, |multi_buffer, cx| {
11730                for (buffer_id, changes) in revert_changes {
11731                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11732                        buffer.update(cx, |buffer, cx| {
11733                            buffer.edit(
11734                                changes
11735                                    .into_iter()
11736                                    .map(|(range, text)| (range, text.to_string())),
11737                                None,
11738                                cx,
11739                            );
11740                        });
11741                    }
11742                }
11743            });
11744        }
11745    }
11746
11747    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11748        if let Some(status) = self
11749            .addons
11750            .iter()
11751            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11752        {
11753            return Some(status);
11754        }
11755        self.project
11756            .as_ref()?
11757            .read(cx)
11758            .status_for_buffer_id(buffer_id, cx)
11759    }
11760
11761    pub fn open_active_item_in_terminal(
11762        &mut self,
11763        _: &OpenInTerminal,
11764        window: &mut Window,
11765        cx: &mut Context<Self>,
11766    ) {
11767        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11768            let project_path = buffer.read(cx).project_path(cx)?;
11769            let project = self.project()?.read(cx);
11770            let entry = project.entry_for_path(&project_path, cx)?;
11771            let parent = match &entry.canonical_path {
11772                Some(canonical_path) => canonical_path.to_path_buf(),
11773                None => project.absolute_path(&project_path, cx)?,
11774            }
11775            .parent()?
11776            .to_path_buf();
11777            Some(parent)
11778        }) {
11779            window.dispatch_action(
11780                OpenTerminal {
11781                    working_directory,
11782                    local: false,
11783                }
11784                .boxed_clone(),
11785                cx,
11786            );
11787        }
11788    }
11789
11790    fn set_breakpoint_context_menu(
11791        &mut self,
11792        display_row: DisplayRow,
11793        position: Option<Anchor>,
11794        clicked_point: gpui::Point<Pixels>,
11795        window: &mut Window,
11796        cx: &mut Context<Self>,
11797    ) {
11798        let source = self
11799            .buffer
11800            .read(cx)
11801            .snapshot(cx)
11802            .anchor_before(Point::new(display_row.0, 0u32));
11803
11804        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11805
11806        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11807            self,
11808            source,
11809            clicked_point,
11810            context_menu,
11811            window,
11812            cx,
11813        );
11814    }
11815
11816    fn add_edit_breakpoint_block(
11817        &mut self,
11818        anchor: Anchor,
11819        breakpoint: &Breakpoint,
11820        edit_action: BreakpointPromptEditAction,
11821        window: &mut Window,
11822        cx: &mut Context<Self>,
11823    ) {
11824        let weak_editor = cx.weak_entity();
11825        let bp_prompt = cx.new(|cx| {
11826            BreakpointPromptEditor::new(
11827                weak_editor,
11828                anchor,
11829                breakpoint.clone(),
11830                edit_action,
11831                window,
11832                cx,
11833            )
11834        });
11835
11836        let height = bp_prompt.update(cx, |this, cx| {
11837            this.prompt
11838                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11839        });
11840        let cloned_prompt = bp_prompt.clone();
11841        let blocks = vec![BlockProperties {
11842            style: BlockStyle::Sticky,
11843            placement: BlockPlacement::Above(anchor),
11844            height: Some(height),
11845            render: Arc::new(move |cx| {
11846                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11847                cloned_prompt.clone().into_any_element()
11848            }),
11849            priority: 0,
11850        }];
11851
11852        let focus_handle = bp_prompt.focus_handle(cx);
11853        window.focus(&focus_handle, cx);
11854
11855        let block_ids = self.insert_blocks(blocks, None, cx);
11856        bp_prompt.update(cx, |prompt, _| {
11857            prompt.add_block_ids(block_ids);
11858        });
11859    }
11860
11861    pub(crate) fn breakpoint_at_row(
11862        &self,
11863        row: u32,
11864        window: &mut Window,
11865        cx: &mut Context<Self>,
11866    ) -> Option<(Anchor, Breakpoint)> {
11867        let snapshot = self.snapshot(window, cx);
11868        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11869
11870        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11871    }
11872
11873    pub(crate) fn breakpoint_at_anchor(
11874        &self,
11875        breakpoint_position: Anchor,
11876        snapshot: &EditorSnapshot,
11877        cx: &mut Context<Self>,
11878    ) -> Option<(Anchor, Breakpoint)> {
11879        let buffer = self
11880            .buffer
11881            .read(cx)
11882            .buffer_for_anchor(breakpoint_position, cx)?;
11883
11884        let enclosing_excerpt = breakpoint_position.excerpt_id;
11885        let buffer_snapshot = buffer.read(cx).snapshot();
11886
11887        let row = buffer_snapshot
11888            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11889            .row;
11890
11891        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11892        let anchor_end = snapshot
11893            .buffer_snapshot()
11894            .anchor_after(Point::new(row, line_len));
11895
11896        self.breakpoint_store
11897            .as_ref()?
11898            .read_with(cx, |breakpoint_store, cx| {
11899                breakpoint_store
11900                    .breakpoints(
11901                        &buffer,
11902                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11903                        &buffer_snapshot,
11904                        cx,
11905                    )
11906                    .next()
11907                    .and_then(|(bp, _)| {
11908                        let breakpoint_row = buffer_snapshot
11909                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11910                            .row;
11911
11912                        if breakpoint_row == row {
11913                            snapshot
11914                                .buffer_snapshot()
11915                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11916                                .map(|position| (position, bp.bp.clone()))
11917                        } else {
11918                            None
11919                        }
11920                    })
11921            })
11922    }
11923
11924    pub fn edit_log_breakpoint(
11925        &mut self,
11926        _: &EditLogBreakpoint,
11927        window: &mut Window,
11928        cx: &mut Context<Self>,
11929    ) {
11930        if self.breakpoint_store.is_none() {
11931            return;
11932        }
11933
11934        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11935            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11936                message: None,
11937                state: BreakpointState::Enabled,
11938                condition: None,
11939                hit_condition: None,
11940            });
11941
11942            self.add_edit_breakpoint_block(
11943                anchor,
11944                &breakpoint,
11945                BreakpointPromptEditAction::Log,
11946                window,
11947                cx,
11948            );
11949        }
11950    }
11951
11952    fn breakpoints_at_cursors(
11953        &self,
11954        window: &mut Window,
11955        cx: &mut Context<Self>,
11956    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11957        let snapshot = self.snapshot(window, cx);
11958        let cursors = self
11959            .selections
11960            .disjoint_anchors_arc()
11961            .iter()
11962            .map(|selection| {
11963                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11964
11965                let breakpoint_position = self
11966                    .breakpoint_at_row(cursor_position.row, window, cx)
11967                    .map(|bp| bp.0)
11968                    .unwrap_or_else(|| {
11969                        snapshot
11970                            .display_snapshot
11971                            .buffer_snapshot()
11972                            .anchor_after(Point::new(cursor_position.row, 0))
11973                    });
11974
11975                let breakpoint = self
11976                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11977                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11978
11979                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11980            })
11981            // 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.
11982            .collect::<HashMap<Anchor, _>>();
11983
11984        cursors.into_iter().collect()
11985    }
11986
11987    pub fn enable_breakpoint(
11988        &mut self,
11989        _: &crate::actions::EnableBreakpoint,
11990        window: &mut Window,
11991        cx: &mut Context<Self>,
11992    ) {
11993        if self.breakpoint_store.is_none() {
11994            return;
11995        }
11996
11997        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11998            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11999                continue;
12000            };
12001            self.edit_breakpoint_at_anchor(
12002                anchor,
12003                breakpoint,
12004                BreakpointEditAction::InvertState,
12005                cx,
12006            );
12007        }
12008    }
12009
12010    pub fn align_selections(
12011        &mut self,
12012        _: &crate::actions::AlignSelections,
12013        window: &mut Window,
12014        cx: &mut Context<Self>,
12015    ) {
12016        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12017
12018        let display_snapshot = self.display_snapshot(cx);
12019
12020        struct CursorData {
12021            anchor: Anchor,
12022            point: Point,
12023        }
12024        let cursor_data: Vec<CursorData> = self
12025            .selections
12026            .disjoint_anchors()
12027            .iter()
12028            .map(|selection| {
12029                let anchor = if selection.reversed {
12030                    selection.head()
12031                } else {
12032                    selection.tail()
12033                };
12034                CursorData {
12035                    anchor: anchor,
12036                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
12037                }
12038            })
12039            .collect();
12040
12041        let rows_anchors_count: Vec<usize> = cursor_data
12042            .iter()
12043            .map(|cursor| cursor.point.row)
12044            .chunk_by(|&row| row)
12045            .into_iter()
12046            .map(|(_, group)| group.count())
12047            .collect();
12048        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
12049        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
12050        let mut edits = Vec::new();
12051
12052        for column_idx in 0..max_columns {
12053            let mut cursor_index = 0;
12054
12055            // Calculate target_column => position that the selections will go
12056            let mut target_column = 0;
12057            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12058                // Skip rows that don't have this column
12059                if column_idx >= *cursor_count {
12060                    cursor_index += cursor_count;
12061                    continue;
12062                }
12063
12064                let point = &cursor_data[cursor_index + column_idx].point;
12065                let adjusted_column = point.column + rows_column_offset[row_idx];
12066                if adjusted_column > target_column {
12067                    target_column = adjusted_column;
12068                }
12069                cursor_index += cursor_count;
12070            }
12071
12072            // Collect edits for this column
12073            cursor_index = 0;
12074            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12075                // Skip rows that don't have this column
12076                if column_idx >= *cursor_count {
12077                    cursor_index += *cursor_count;
12078                    continue;
12079                }
12080
12081                let point = &cursor_data[cursor_index + column_idx].point;
12082                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
12083                if spaces_needed > 0 {
12084                    let anchor = cursor_data[cursor_index + column_idx]
12085                        .anchor
12086                        .bias_left(&display_snapshot);
12087                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
12088                }
12089                rows_column_offset[row_idx] += spaces_needed;
12090
12091                cursor_index += *cursor_count;
12092            }
12093        }
12094
12095        if !edits.is_empty() {
12096            self.transact(window, cx, |editor, _window, cx| {
12097                editor.edit(edits, cx);
12098            });
12099        }
12100    }
12101
12102    pub fn disable_breakpoint(
12103        &mut self,
12104        _: &crate::actions::DisableBreakpoint,
12105        window: &mut Window,
12106        cx: &mut Context<Self>,
12107    ) {
12108        if self.breakpoint_store.is_none() {
12109            return;
12110        }
12111
12112        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12113            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12114                continue;
12115            };
12116            self.edit_breakpoint_at_anchor(
12117                anchor,
12118                breakpoint,
12119                BreakpointEditAction::InvertState,
12120                cx,
12121            );
12122        }
12123    }
12124
12125    pub fn toggle_breakpoint(
12126        &mut self,
12127        _: &crate::actions::ToggleBreakpoint,
12128        window: &mut Window,
12129        cx: &mut Context<Self>,
12130    ) {
12131        if self.breakpoint_store.is_none() {
12132            return;
12133        }
12134
12135        let snapshot = self.snapshot(window, cx);
12136        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12137            if self.gutter_breakpoint_indicator.0.is_some() {
12138                let display_row = anchor
12139                    .to_point(snapshot.buffer_snapshot())
12140                    .to_display_point(&snapshot.display_snapshot)
12141                    .row();
12142                self.update_breakpoint_collision_on_toggle(
12143                    display_row,
12144                    &BreakpointEditAction::Toggle,
12145                );
12146            }
12147
12148            if let Some(breakpoint) = breakpoint {
12149                self.edit_breakpoint_at_anchor(
12150                    anchor,
12151                    breakpoint,
12152                    BreakpointEditAction::Toggle,
12153                    cx,
12154                );
12155            } else {
12156                self.edit_breakpoint_at_anchor(
12157                    anchor,
12158                    Breakpoint::new_standard(),
12159                    BreakpointEditAction::Toggle,
12160                    cx,
12161                );
12162            }
12163        }
12164    }
12165
12166    fn update_breakpoint_collision_on_toggle(
12167        &mut self,
12168        display_row: DisplayRow,
12169        edit_action: &BreakpointEditAction,
12170    ) {
12171        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12172            if breakpoint_indicator.display_row == display_row
12173                && matches!(edit_action, BreakpointEditAction::Toggle)
12174            {
12175                breakpoint_indicator.collides_with_existing_breakpoint =
12176                    !breakpoint_indicator.collides_with_existing_breakpoint;
12177            }
12178        }
12179    }
12180
12181    pub fn edit_breakpoint_at_anchor(
12182        &mut self,
12183        breakpoint_position: Anchor,
12184        breakpoint: Breakpoint,
12185        edit_action: BreakpointEditAction,
12186        cx: &mut Context<Self>,
12187    ) {
12188        let Some(breakpoint_store) = &self.breakpoint_store else {
12189            return;
12190        };
12191
12192        let Some(buffer) = self
12193            .buffer
12194            .read(cx)
12195            .buffer_for_anchor(breakpoint_position, cx)
12196        else {
12197            return;
12198        };
12199
12200        breakpoint_store.update(cx, |breakpoint_store, cx| {
12201            breakpoint_store.toggle_breakpoint(
12202                buffer,
12203                BreakpointWithPosition {
12204                    position: breakpoint_position.text_anchor,
12205                    bp: breakpoint,
12206                },
12207                edit_action,
12208                cx,
12209            );
12210        });
12211
12212        cx.notify();
12213    }
12214
12215    #[cfg(any(test, feature = "test-support"))]
12216    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12217        self.breakpoint_store.clone()
12218    }
12219
12220    pub fn prepare_restore_change(
12221        &self,
12222        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12223        hunk: &MultiBufferDiffHunk,
12224        cx: &mut App,
12225    ) -> Option<()> {
12226        if hunk.is_created_file() {
12227            return None;
12228        }
12229        let buffer = self.buffer.read(cx);
12230        let diff = buffer.diff_for(hunk.buffer_id)?;
12231        let buffer = buffer.buffer(hunk.buffer_id)?;
12232        let buffer = buffer.read(cx);
12233        let original_text = diff
12234            .read(cx)
12235            .base_text(cx)
12236            .as_rope()
12237            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12238        let buffer_snapshot = buffer.snapshot();
12239        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12240        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12241            probe
12242                .0
12243                .start
12244                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12245                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12246        }) {
12247            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12248            Some(())
12249        } else {
12250            None
12251        }
12252    }
12253
12254    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12255        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12256    }
12257
12258    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12259        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12260    }
12261
12262    pub fn rotate_selections_forward(
12263        &mut self,
12264        _: &RotateSelectionsForward,
12265        window: &mut Window,
12266        cx: &mut Context<Self>,
12267    ) {
12268        self.rotate_selections(window, cx, false)
12269    }
12270
12271    pub fn rotate_selections_backward(
12272        &mut self,
12273        _: &RotateSelectionsBackward,
12274        window: &mut Window,
12275        cx: &mut Context<Self>,
12276    ) {
12277        self.rotate_selections(window, cx, true)
12278    }
12279
12280    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12281        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12282        let display_snapshot = self.display_snapshot(cx);
12283        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12284
12285        if selections.len() < 2 {
12286            return;
12287        }
12288
12289        let (edits, new_selections) = {
12290            let buffer = self.buffer.read(cx).read(cx);
12291            let has_selections = selections.iter().any(|s| !s.is_empty());
12292            if has_selections {
12293                let mut selected_texts: Vec<String> = selections
12294                    .iter()
12295                    .map(|selection| {
12296                        buffer
12297                            .text_for_range(selection.start..selection.end)
12298                            .collect()
12299                    })
12300                    .collect();
12301
12302                if reverse {
12303                    selected_texts.rotate_left(1);
12304                } else {
12305                    selected_texts.rotate_right(1);
12306                }
12307
12308                let mut offset_delta: i64 = 0;
12309                let mut new_selections = Vec::new();
12310                let edits: Vec<_> = selections
12311                    .iter()
12312                    .zip(selected_texts.iter())
12313                    .map(|(selection, new_text)| {
12314                        let old_len = (selection.end.0 - selection.start.0) as i64;
12315                        let new_len = new_text.len() as i64;
12316                        let adjusted_start =
12317                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12318                        let adjusted_end =
12319                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12320
12321                        new_selections.push(Selection {
12322                            id: selection.id,
12323                            start: adjusted_start,
12324                            end: adjusted_end,
12325                            reversed: selection.reversed,
12326                            goal: selection.goal,
12327                        });
12328
12329                        offset_delta += new_len - old_len;
12330                        (selection.start..selection.end, new_text.clone())
12331                    })
12332                    .collect();
12333                (edits, new_selections)
12334            } else {
12335                let mut all_rows: Vec<u32> = selections
12336                    .iter()
12337                    .map(|selection| buffer.offset_to_point(selection.start).row)
12338                    .collect();
12339                all_rows.sort_unstable();
12340                all_rows.dedup();
12341
12342                if all_rows.len() < 2 {
12343                    return;
12344                }
12345
12346                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12347                    .iter()
12348                    .map(|&row| {
12349                        let start = Point::new(row, 0);
12350                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12351                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12352                    })
12353                    .collect();
12354
12355                let mut line_texts: Vec<String> = line_ranges
12356                    .iter()
12357                    .map(|range| buffer.text_for_range(range.clone()).collect())
12358                    .collect();
12359
12360                if reverse {
12361                    line_texts.rotate_left(1);
12362                } else {
12363                    line_texts.rotate_right(1);
12364                }
12365
12366                let edits = line_ranges
12367                    .iter()
12368                    .zip(line_texts.iter())
12369                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12370                    .collect();
12371
12372                let num_rows = all_rows.len();
12373                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12374                    .iter()
12375                    .enumerate()
12376                    .map(|(i, &row)| (row, i))
12377                    .collect();
12378
12379                // Compute new line start offsets after rotation (handles CRLF)
12380                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12381                let first_line_start = line_ranges[0].start.0;
12382                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12383                for text in line_texts.iter().take(num_rows - 1) {
12384                    let prev_start = *new_line_starts.last().unwrap();
12385                    new_line_starts.push(prev_start + text.len() + newline_len);
12386                }
12387
12388                let new_selections = selections
12389                    .iter()
12390                    .map(|selection| {
12391                        let point = buffer.offset_to_point(selection.start);
12392                        let old_index = row_to_index[&point.row];
12393                        let new_index = if reverse {
12394                            (old_index + num_rows - 1) % num_rows
12395                        } else {
12396                            (old_index + 1) % num_rows
12397                        };
12398                        let new_offset =
12399                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12400                        Selection {
12401                            id: selection.id,
12402                            start: new_offset,
12403                            end: new_offset,
12404                            reversed: selection.reversed,
12405                            goal: selection.goal,
12406                        }
12407                    })
12408                    .collect();
12409
12410                (edits, new_selections)
12411            }
12412        };
12413
12414        self.transact(window, cx, |this, window, cx| {
12415            this.buffer.update(cx, |buffer, cx| {
12416                buffer.edit(edits, None, cx);
12417            });
12418            this.change_selections(Default::default(), window, cx, |s| {
12419                s.select(new_selections);
12420            });
12421        });
12422    }
12423
12424    fn manipulate_lines<M>(
12425        &mut self,
12426        window: &mut Window,
12427        cx: &mut Context<Self>,
12428        mut manipulate: M,
12429    ) where
12430        M: FnMut(&str) -> LineManipulationResult,
12431    {
12432        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12433
12434        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12435        let buffer = self.buffer.read(cx).snapshot(cx);
12436
12437        let mut edits = Vec::new();
12438
12439        let selections = self.selections.all::<Point>(&display_map);
12440        let mut selections = selections.iter().peekable();
12441        let mut contiguous_row_selections = Vec::new();
12442        let mut new_selections = Vec::new();
12443        let mut added_lines = 0;
12444        let mut removed_lines = 0;
12445
12446        while let Some(selection) = selections.next() {
12447            let (start_row, end_row) = consume_contiguous_rows(
12448                &mut contiguous_row_selections,
12449                selection,
12450                &display_map,
12451                &mut selections,
12452            );
12453
12454            let start_point = Point::new(start_row.0, 0);
12455            let end_point = Point::new(
12456                end_row.previous_row().0,
12457                buffer.line_len(end_row.previous_row()),
12458            );
12459            let text = buffer
12460                .text_for_range(start_point..end_point)
12461                .collect::<String>();
12462
12463            let LineManipulationResult {
12464                new_text,
12465                line_count_before,
12466                line_count_after,
12467            } = manipulate(&text);
12468
12469            edits.push((start_point..end_point, new_text));
12470
12471            // Selections must change based on added and removed line count
12472            let start_row =
12473                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12474            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12475            new_selections.push(Selection {
12476                id: selection.id,
12477                start: start_row,
12478                end: end_row,
12479                goal: SelectionGoal::None,
12480                reversed: selection.reversed,
12481            });
12482
12483            if line_count_after > line_count_before {
12484                added_lines += line_count_after - line_count_before;
12485            } else if line_count_before > line_count_after {
12486                removed_lines += line_count_before - line_count_after;
12487            }
12488        }
12489
12490        self.transact(window, cx, |this, window, cx| {
12491            let buffer = this.buffer.update(cx, |buffer, cx| {
12492                buffer.edit(edits, None, cx);
12493                buffer.snapshot(cx)
12494            });
12495
12496            // Recalculate offsets on newly edited buffer
12497            let new_selections = new_selections
12498                .iter()
12499                .map(|s| {
12500                    let start_point = Point::new(s.start.0, 0);
12501                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12502                    Selection {
12503                        id: s.id,
12504                        start: buffer.point_to_offset(start_point),
12505                        end: buffer.point_to_offset(end_point),
12506                        goal: s.goal,
12507                        reversed: s.reversed,
12508                    }
12509                })
12510                .collect();
12511
12512            this.change_selections(Default::default(), window, cx, |s| {
12513                s.select(new_selections);
12514            });
12515
12516            this.request_autoscroll(Autoscroll::fit(), cx);
12517        });
12518    }
12519
12520    fn manipulate_immutable_lines<Fn>(
12521        &mut self,
12522        window: &mut Window,
12523        cx: &mut Context<Self>,
12524        mut callback: Fn,
12525    ) where
12526        Fn: FnMut(&mut Vec<&str>),
12527    {
12528        self.manipulate_lines(window, cx, |text| {
12529            let mut lines: Vec<&str> = text.split('\n').collect();
12530            let line_count_before = lines.len();
12531
12532            callback(&mut lines);
12533
12534            LineManipulationResult {
12535                new_text: lines.join("\n"),
12536                line_count_before,
12537                line_count_after: lines.len(),
12538            }
12539        });
12540    }
12541
12542    fn manipulate_mutable_lines<Fn>(
12543        &mut self,
12544        window: &mut Window,
12545        cx: &mut Context<Self>,
12546        mut callback: Fn,
12547    ) where
12548        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12549    {
12550        self.manipulate_lines(window, cx, |text| {
12551            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12552            let line_count_before = lines.len();
12553
12554            callback(&mut lines);
12555
12556            LineManipulationResult {
12557                new_text: lines.join("\n"),
12558                line_count_before,
12559                line_count_after: lines.len(),
12560            }
12561        });
12562    }
12563
12564    pub fn convert_indentation_to_spaces(
12565        &mut self,
12566        _: &ConvertIndentationToSpaces,
12567        window: &mut Window,
12568        cx: &mut Context<Self>,
12569    ) {
12570        let settings = self.buffer.read(cx).language_settings(cx);
12571        let tab_size = settings.tab_size.get() as usize;
12572
12573        self.manipulate_mutable_lines(window, cx, |lines| {
12574            // Allocates a reasonably sized scratch buffer once for the whole loop
12575            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12576            // Avoids recomputing spaces that could be inserted many times
12577            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12578                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12579                .collect();
12580
12581            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12582                let mut chars = line.as_ref().chars();
12583                let mut col = 0;
12584                let mut changed = false;
12585
12586                for ch in chars.by_ref() {
12587                    match ch {
12588                        ' ' => {
12589                            reindented_line.push(' ');
12590                            col += 1;
12591                        }
12592                        '\t' => {
12593                            // \t are converted to spaces depending on the current column
12594                            let spaces_len = tab_size - (col % tab_size);
12595                            reindented_line.extend(&space_cache[spaces_len - 1]);
12596                            col += spaces_len;
12597                            changed = true;
12598                        }
12599                        _ => {
12600                            // If we dont append before break, the character is consumed
12601                            reindented_line.push(ch);
12602                            break;
12603                        }
12604                    }
12605                }
12606
12607                if !changed {
12608                    reindented_line.clear();
12609                    continue;
12610                }
12611                // Append the rest of the line and replace old reference with new one
12612                reindented_line.extend(chars);
12613                *line = Cow::Owned(reindented_line.clone());
12614                reindented_line.clear();
12615            }
12616        });
12617    }
12618
12619    pub fn convert_indentation_to_tabs(
12620        &mut self,
12621        _: &ConvertIndentationToTabs,
12622        window: &mut Window,
12623        cx: &mut Context<Self>,
12624    ) {
12625        let settings = self.buffer.read(cx).language_settings(cx);
12626        let tab_size = settings.tab_size.get() as usize;
12627
12628        self.manipulate_mutable_lines(window, cx, |lines| {
12629            // Allocates a reasonably sized buffer once for the whole loop
12630            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12631            // Avoids recomputing spaces that could be inserted many times
12632            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12633                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12634                .collect();
12635
12636            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12637                let mut chars = line.chars();
12638                let mut spaces_count = 0;
12639                let mut first_non_indent_char = None;
12640                let mut changed = false;
12641
12642                for ch in chars.by_ref() {
12643                    match ch {
12644                        ' ' => {
12645                            // Keep track of spaces. Append \t when we reach tab_size
12646                            spaces_count += 1;
12647                            changed = true;
12648                            if spaces_count == tab_size {
12649                                reindented_line.push('\t');
12650                                spaces_count = 0;
12651                            }
12652                        }
12653                        '\t' => {
12654                            reindented_line.push('\t');
12655                            spaces_count = 0;
12656                        }
12657                        _ => {
12658                            // Dont append it yet, we might have remaining spaces
12659                            first_non_indent_char = Some(ch);
12660                            break;
12661                        }
12662                    }
12663                }
12664
12665                if !changed {
12666                    reindented_line.clear();
12667                    continue;
12668                }
12669                // Remaining spaces that didn't make a full tab stop
12670                if spaces_count > 0 {
12671                    reindented_line.extend(&space_cache[spaces_count - 1]);
12672                }
12673                // If we consume an extra character that was not indentation, add it back
12674                if let Some(extra_char) = first_non_indent_char {
12675                    reindented_line.push(extra_char);
12676                }
12677                // Append the rest of the line and replace old reference with new one
12678                reindented_line.extend(chars);
12679                *line = Cow::Owned(reindented_line.clone());
12680                reindented_line.clear();
12681            }
12682        });
12683    }
12684
12685    pub fn convert_to_upper_case(
12686        &mut self,
12687        _: &ConvertToUpperCase,
12688        window: &mut Window,
12689        cx: &mut Context<Self>,
12690    ) {
12691        self.manipulate_text(window, cx, |text| text.to_uppercase())
12692    }
12693
12694    pub fn convert_to_lower_case(
12695        &mut self,
12696        _: &ConvertToLowerCase,
12697        window: &mut Window,
12698        cx: &mut Context<Self>,
12699    ) {
12700        self.manipulate_text(window, cx, |text| text.to_lowercase())
12701    }
12702
12703    pub fn convert_to_title_case(
12704        &mut self,
12705        _: &ConvertToTitleCase,
12706        window: &mut Window,
12707        cx: &mut Context<Self>,
12708    ) {
12709        self.manipulate_text(window, cx, |text| {
12710            Self::convert_text_case(text, Case::Title)
12711        })
12712    }
12713
12714    pub fn convert_to_snake_case(
12715        &mut self,
12716        _: &ConvertToSnakeCase,
12717        window: &mut Window,
12718        cx: &mut Context<Self>,
12719    ) {
12720        self.manipulate_text(window, cx, |text| {
12721            Self::convert_text_case(text, Case::Snake)
12722        })
12723    }
12724
12725    pub fn convert_to_kebab_case(
12726        &mut self,
12727        _: &ConvertToKebabCase,
12728        window: &mut Window,
12729        cx: &mut Context<Self>,
12730    ) {
12731        self.manipulate_text(window, cx, |text| {
12732            Self::convert_text_case(text, Case::Kebab)
12733        })
12734    }
12735
12736    pub fn convert_to_upper_camel_case(
12737        &mut self,
12738        _: &ConvertToUpperCamelCase,
12739        window: &mut Window,
12740        cx: &mut Context<Self>,
12741    ) {
12742        self.manipulate_text(window, cx, |text| {
12743            Self::convert_text_case(text, Case::UpperCamel)
12744        })
12745    }
12746
12747    pub fn convert_to_lower_camel_case(
12748        &mut self,
12749        _: &ConvertToLowerCamelCase,
12750        window: &mut Window,
12751        cx: &mut Context<Self>,
12752    ) {
12753        self.manipulate_text(window, cx, |text| {
12754            Self::convert_text_case(text, Case::Camel)
12755        })
12756    }
12757
12758    pub fn convert_to_opposite_case(
12759        &mut self,
12760        _: &ConvertToOppositeCase,
12761        window: &mut Window,
12762        cx: &mut Context<Self>,
12763    ) {
12764        self.manipulate_text(window, cx, |text| {
12765            text.chars()
12766                .fold(String::with_capacity(text.len()), |mut t, c| {
12767                    if c.is_uppercase() {
12768                        t.extend(c.to_lowercase());
12769                    } else {
12770                        t.extend(c.to_uppercase());
12771                    }
12772                    t
12773                })
12774        })
12775    }
12776
12777    pub fn convert_to_sentence_case(
12778        &mut self,
12779        _: &ConvertToSentenceCase,
12780        window: &mut Window,
12781        cx: &mut Context<Self>,
12782    ) {
12783        self.manipulate_text(window, cx, |text| {
12784            Self::convert_text_case(text, Case::Sentence)
12785        })
12786    }
12787
12788    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12789        self.manipulate_text(window, cx, |text| {
12790            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12791            if has_upper_case_characters {
12792                text.to_lowercase()
12793            } else {
12794                text.to_uppercase()
12795            }
12796        })
12797    }
12798
12799    pub fn convert_to_rot13(
12800        &mut self,
12801        _: &ConvertToRot13,
12802        window: &mut Window,
12803        cx: &mut Context<Self>,
12804    ) {
12805        self.manipulate_text(window, cx, |text| {
12806            text.chars()
12807                .map(|c| match c {
12808                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12809                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12810                    _ => c,
12811                })
12812                .collect()
12813        })
12814    }
12815
12816    fn convert_text_case(text: &str, case: Case) -> String {
12817        text.lines()
12818            .map(|line| {
12819                let trimmed_start = line.trim_start();
12820                let leading = &line[..line.len() - trimmed_start.len()];
12821                let trimmed = trimmed_start.trim_end();
12822                let trailing = &trimmed_start[trimmed.len()..];
12823                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12824            })
12825            .join("\n")
12826    }
12827
12828    pub fn convert_to_rot47(
12829        &mut self,
12830        _: &ConvertToRot47,
12831        window: &mut Window,
12832        cx: &mut Context<Self>,
12833    ) {
12834        self.manipulate_text(window, cx, |text| {
12835            text.chars()
12836                .map(|c| {
12837                    let code_point = c as u32;
12838                    if code_point >= 33 && code_point <= 126 {
12839                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12840                    }
12841                    c
12842                })
12843                .collect()
12844        })
12845    }
12846
12847    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12848    where
12849        Fn: FnMut(&str) -> String,
12850    {
12851        let buffer = self.buffer.read(cx).snapshot(cx);
12852
12853        let mut new_selections = Vec::new();
12854        let mut edits = Vec::new();
12855        let mut selection_adjustment = 0isize;
12856
12857        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12858            let selection_is_empty = selection.is_empty();
12859
12860            let (start, end) = if selection_is_empty {
12861                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12862                (word_range.start, word_range.end)
12863            } else {
12864                (
12865                    buffer.point_to_offset(selection.start),
12866                    buffer.point_to_offset(selection.end),
12867                )
12868            };
12869
12870            let text = buffer.text_for_range(start..end).collect::<String>();
12871            let old_length = text.len() as isize;
12872            let text = callback(&text);
12873
12874            new_selections.push(Selection {
12875                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12876                end: MultiBufferOffset(
12877                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12878                ),
12879                goal: SelectionGoal::None,
12880                id: selection.id,
12881                reversed: selection.reversed,
12882            });
12883
12884            selection_adjustment += old_length - text.len() as isize;
12885
12886            edits.push((start..end, text));
12887        }
12888
12889        self.transact(window, cx, |this, window, cx| {
12890            this.buffer.update(cx, |buffer, cx| {
12891                buffer.edit(edits, None, cx);
12892            });
12893
12894            this.change_selections(Default::default(), window, cx, |s| {
12895                s.select(new_selections);
12896            });
12897
12898            this.request_autoscroll(Autoscroll::fit(), cx);
12899        });
12900    }
12901
12902    pub fn move_selection_on_drop(
12903        &mut self,
12904        selection: &Selection<Anchor>,
12905        target: DisplayPoint,
12906        is_cut: bool,
12907        window: &mut Window,
12908        cx: &mut Context<Self>,
12909    ) {
12910        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12911        let buffer = display_map.buffer_snapshot();
12912        let mut edits = Vec::new();
12913        let insert_point = display_map
12914            .clip_point(target, Bias::Left)
12915            .to_point(&display_map);
12916        let text = buffer
12917            .text_for_range(selection.start..selection.end)
12918            .collect::<String>();
12919        if is_cut {
12920            edits.push(((selection.start..selection.end), String::new()));
12921        }
12922        let insert_anchor = buffer.anchor_before(insert_point);
12923        edits.push(((insert_anchor..insert_anchor), text));
12924        let last_edit_start = insert_anchor.bias_left(buffer);
12925        let last_edit_end = insert_anchor.bias_right(buffer);
12926        self.transact(window, cx, |this, window, cx| {
12927            this.buffer.update(cx, |buffer, cx| {
12928                buffer.edit(edits, None, cx);
12929            });
12930            this.change_selections(Default::default(), window, cx, |s| {
12931                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12932            });
12933        });
12934    }
12935
12936    pub fn clear_selection_drag_state(&mut self) {
12937        self.selection_drag_state = SelectionDragState::None;
12938    }
12939
12940    pub fn duplicate(
12941        &mut self,
12942        upwards: bool,
12943        whole_lines: bool,
12944        window: &mut Window,
12945        cx: &mut Context<Self>,
12946    ) {
12947        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12948
12949        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12950        let buffer = display_map.buffer_snapshot();
12951        let selections = self.selections.all::<Point>(&display_map);
12952
12953        let mut edits = Vec::new();
12954        let mut selections_iter = selections.iter().peekable();
12955        while let Some(selection) = selections_iter.next() {
12956            let mut rows = selection.spanned_rows(false, &display_map);
12957            // duplicate line-wise
12958            if whole_lines || selection.start == selection.end {
12959                // Avoid duplicating the same lines twice.
12960                while let Some(next_selection) = selections_iter.peek() {
12961                    let next_rows = next_selection.spanned_rows(false, &display_map);
12962                    if next_rows.start < rows.end {
12963                        rows.end = next_rows.end;
12964                        selections_iter.next().unwrap();
12965                    } else {
12966                        break;
12967                    }
12968                }
12969
12970                // Copy the text from the selected row region and splice it either at the start
12971                // or end of the region.
12972                let start = Point::new(rows.start.0, 0);
12973                let end = Point::new(
12974                    rows.end.previous_row().0,
12975                    buffer.line_len(rows.end.previous_row()),
12976                );
12977
12978                let mut text = buffer.text_for_range(start..end).collect::<String>();
12979
12980                let insert_location = if upwards {
12981                    // When duplicating upward, we need to insert before the current line.
12982                    // If we're on the last line and it doesn't end with a newline,
12983                    // we need to add a newline before the duplicated content.
12984                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12985                        && buffer.max_point().column > 0
12986                        && !text.ends_with('\n');
12987
12988                    if needs_leading_newline {
12989                        text.insert(0, '\n');
12990                        end
12991                    } else {
12992                        text.push('\n');
12993                        Point::new(rows.start.0, 0)
12994                    }
12995                } else {
12996                    text.push('\n');
12997                    start
12998                };
12999                edits.push((insert_location..insert_location, text));
13000            } else {
13001                // duplicate character-wise
13002                let start = selection.start;
13003                let end = selection.end;
13004                let text = buffer.text_for_range(start..end).collect::<String>();
13005                edits.push((selection.end..selection.end, text));
13006            }
13007        }
13008
13009        self.transact(window, cx, |this, window, cx| {
13010            this.buffer.update(cx, |buffer, cx| {
13011                buffer.edit(edits, None, cx);
13012            });
13013
13014            // When duplicating upward with whole lines, move the cursor to the duplicated line
13015            if upwards && whole_lines {
13016                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
13017
13018                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13019                    let mut new_ranges = Vec::new();
13020                    let selections = s.all::<Point>(&display_map);
13021                    let mut selections_iter = selections.iter().peekable();
13022
13023                    while let Some(first_selection) = selections_iter.next() {
13024                        // Group contiguous selections together to find the total row span
13025                        let mut group_selections = vec![first_selection];
13026                        let mut rows = first_selection.spanned_rows(false, &display_map);
13027
13028                        while let Some(next_selection) = selections_iter.peek() {
13029                            let next_rows = next_selection.spanned_rows(false, &display_map);
13030                            if next_rows.start < rows.end {
13031                                rows.end = next_rows.end;
13032                                group_selections.push(selections_iter.next().unwrap());
13033                            } else {
13034                                break;
13035                            }
13036                        }
13037
13038                        let row_count = rows.end.0 - rows.start.0;
13039
13040                        // Move all selections in this group up by the total number of duplicated rows
13041                        for selection in group_selections {
13042                            let new_start = Point::new(
13043                                selection.start.row.saturating_sub(row_count),
13044                                selection.start.column,
13045                            );
13046
13047                            let new_end = Point::new(
13048                                selection.end.row.saturating_sub(row_count),
13049                                selection.end.column,
13050                            );
13051
13052                            new_ranges.push(new_start..new_end);
13053                        }
13054                    }
13055
13056                    s.select_ranges(new_ranges);
13057                });
13058            }
13059
13060            this.request_autoscroll(Autoscroll::fit(), cx);
13061        });
13062    }
13063
13064    pub fn duplicate_line_up(
13065        &mut self,
13066        _: &DuplicateLineUp,
13067        window: &mut Window,
13068        cx: &mut Context<Self>,
13069    ) {
13070        self.duplicate(true, true, window, cx);
13071    }
13072
13073    pub fn duplicate_line_down(
13074        &mut self,
13075        _: &DuplicateLineDown,
13076        window: &mut Window,
13077        cx: &mut Context<Self>,
13078    ) {
13079        self.duplicate(false, true, window, cx);
13080    }
13081
13082    pub fn duplicate_selection(
13083        &mut self,
13084        _: &DuplicateSelection,
13085        window: &mut Window,
13086        cx: &mut Context<Self>,
13087    ) {
13088        self.duplicate(false, false, window, cx);
13089    }
13090
13091    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13092        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13093        if self.mode.is_single_line() {
13094            cx.propagate();
13095            return;
13096        }
13097
13098        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13099        let buffer = self.buffer.read(cx).snapshot(cx);
13100
13101        let mut edits = Vec::new();
13102        let mut unfold_ranges = Vec::new();
13103        let mut refold_creases = Vec::new();
13104
13105        let selections = self.selections.all::<Point>(&display_map);
13106        let mut selections = selections.iter().peekable();
13107        let mut contiguous_row_selections = Vec::new();
13108        let mut new_selections = Vec::new();
13109
13110        while let Some(selection) = selections.next() {
13111            // Find all the selections that span a contiguous row range
13112            let (start_row, end_row) = consume_contiguous_rows(
13113                &mut contiguous_row_selections,
13114                selection,
13115                &display_map,
13116                &mut selections,
13117            );
13118
13119            // Move the text spanned by the row range to be before the line preceding the row range
13120            if start_row.0 > 0 {
13121                let range_to_move = Point::new(
13122                    start_row.previous_row().0,
13123                    buffer.line_len(start_row.previous_row()),
13124                )
13125                    ..Point::new(
13126                        end_row.previous_row().0,
13127                        buffer.line_len(end_row.previous_row()),
13128                    );
13129                let insertion_point = display_map
13130                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13131                    .0;
13132
13133                // Don't move lines across excerpts
13134                if buffer
13135                    .excerpt_containing(insertion_point..range_to_move.end)
13136                    .is_some()
13137                {
13138                    let text = buffer
13139                        .text_for_range(range_to_move.clone())
13140                        .flat_map(|s| s.chars())
13141                        .skip(1)
13142                        .chain(['\n'])
13143                        .collect::<String>();
13144
13145                    edits.push((
13146                        buffer.anchor_after(range_to_move.start)
13147                            ..buffer.anchor_before(range_to_move.end),
13148                        String::new(),
13149                    ));
13150                    let insertion_anchor = buffer.anchor_after(insertion_point);
13151                    edits.push((insertion_anchor..insertion_anchor, text));
13152
13153                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13154
13155                    // Move selections up
13156                    new_selections.extend(contiguous_row_selections.drain(..).map(
13157                        |mut selection| {
13158                            selection.start.row -= row_delta;
13159                            selection.end.row -= row_delta;
13160                            selection
13161                        },
13162                    ));
13163
13164                    // Move folds up
13165                    unfold_ranges.push(range_to_move.clone());
13166                    for fold in display_map.folds_in_range(
13167                        buffer.anchor_before(range_to_move.start)
13168                            ..buffer.anchor_after(range_to_move.end),
13169                    ) {
13170                        let mut start = fold.range.start.to_point(&buffer);
13171                        let mut end = fold.range.end.to_point(&buffer);
13172                        start.row -= row_delta;
13173                        end.row -= row_delta;
13174                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13175                    }
13176                }
13177            }
13178
13179            // If we didn't move line(s), preserve the existing selections
13180            new_selections.append(&mut contiguous_row_selections);
13181        }
13182
13183        self.transact(window, cx, |this, window, cx| {
13184            this.unfold_ranges(&unfold_ranges, true, true, cx);
13185            this.buffer.update(cx, |buffer, cx| {
13186                for (range, text) in edits {
13187                    buffer.edit([(range, text)], None, cx);
13188                }
13189            });
13190            this.fold_creases(refold_creases, true, window, cx);
13191            this.change_selections(Default::default(), window, cx, |s| {
13192                s.select(new_selections);
13193            })
13194        });
13195    }
13196
13197    pub fn move_line_down(
13198        &mut self,
13199        _: &MoveLineDown,
13200        window: &mut Window,
13201        cx: &mut Context<Self>,
13202    ) {
13203        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13204        if self.mode.is_single_line() {
13205            cx.propagate();
13206            return;
13207        }
13208
13209        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13210        let buffer = self.buffer.read(cx).snapshot(cx);
13211
13212        let mut edits = Vec::new();
13213        let mut unfold_ranges = Vec::new();
13214        let mut refold_creases = Vec::new();
13215
13216        let selections = self.selections.all::<Point>(&display_map);
13217        let mut selections = selections.iter().peekable();
13218        let mut contiguous_row_selections = Vec::new();
13219        let mut new_selections = Vec::new();
13220
13221        while let Some(selection) = selections.next() {
13222            // Find all the selections that span a contiguous row range
13223            let (start_row, end_row) = consume_contiguous_rows(
13224                &mut contiguous_row_selections,
13225                selection,
13226                &display_map,
13227                &mut selections,
13228            );
13229
13230            // Move the text spanned by the row range to be after the last line of the row range
13231            if end_row.0 <= buffer.max_point().row {
13232                let range_to_move =
13233                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13234                let insertion_point = display_map
13235                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13236                    .0;
13237
13238                // Don't move lines across excerpt boundaries
13239                if buffer
13240                    .excerpt_containing(range_to_move.start..insertion_point)
13241                    .is_some()
13242                {
13243                    let mut text = String::from("\n");
13244                    text.extend(buffer.text_for_range(range_to_move.clone()));
13245                    text.pop(); // Drop trailing newline
13246                    edits.push((
13247                        buffer.anchor_after(range_to_move.start)
13248                            ..buffer.anchor_before(range_to_move.end),
13249                        String::new(),
13250                    ));
13251                    let insertion_anchor = buffer.anchor_after(insertion_point);
13252                    edits.push((insertion_anchor..insertion_anchor, text));
13253
13254                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13255
13256                    // Move selections down
13257                    new_selections.extend(contiguous_row_selections.drain(..).map(
13258                        |mut selection| {
13259                            selection.start.row += row_delta;
13260                            selection.end.row += row_delta;
13261                            selection
13262                        },
13263                    ));
13264
13265                    // Move folds down
13266                    unfold_ranges.push(range_to_move.clone());
13267                    for fold in display_map.folds_in_range(
13268                        buffer.anchor_before(range_to_move.start)
13269                            ..buffer.anchor_after(range_to_move.end),
13270                    ) {
13271                        let mut start = fold.range.start.to_point(&buffer);
13272                        let mut end = fold.range.end.to_point(&buffer);
13273                        start.row += row_delta;
13274                        end.row += row_delta;
13275                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13276                    }
13277                }
13278            }
13279
13280            // If we didn't move line(s), preserve the existing selections
13281            new_selections.append(&mut contiguous_row_selections);
13282        }
13283
13284        self.transact(window, cx, |this, window, cx| {
13285            this.unfold_ranges(&unfold_ranges, true, true, cx);
13286            this.buffer.update(cx, |buffer, cx| {
13287                for (range, text) in edits {
13288                    buffer.edit([(range, text)], None, cx);
13289                }
13290            });
13291            this.fold_creases(refold_creases, true, window, cx);
13292            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13293        });
13294    }
13295
13296    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13297        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13298        let text_layout_details = &self.text_layout_details(window, cx);
13299        self.transact(window, cx, |this, window, cx| {
13300            let edits = this.change_selections(Default::default(), window, cx, |s| {
13301                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13302                s.move_with(&mut |display_map, selection| {
13303                    if !selection.is_empty() {
13304                        return;
13305                    }
13306
13307                    let mut head = selection.head();
13308                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13309                    if head.column() == display_map.line_len(head.row()) {
13310                        transpose_offset = display_map
13311                            .buffer_snapshot()
13312                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13313                    }
13314
13315                    if transpose_offset == MultiBufferOffset(0) {
13316                        return;
13317                    }
13318
13319                    *head.column_mut() += 1;
13320                    head = display_map.clip_point(head, Bias::Right);
13321                    let goal = SelectionGoal::HorizontalPosition(
13322                        display_map
13323                            .x_for_display_point(head, text_layout_details)
13324                            .into(),
13325                    );
13326                    selection.collapse_to(head, goal);
13327
13328                    let transpose_start = display_map
13329                        .buffer_snapshot()
13330                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13331                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13332                        let transpose_end = display_map
13333                            .buffer_snapshot()
13334                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13335                        if let Some(ch) = display_map
13336                            .buffer_snapshot()
13337                            .chars_at(transpose_start)
13338                            .next()
13339                        {
13340                            edits.push((transpose_start..transpose_offset, String::new()));
13341                            edits.push((transpose_end..transpose_end, ch.to_string()));
13342                        }
13343                    }
13344                });
13345                edits
13346            });
13347            this.buffer
13348                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13349            let selections = this
13350                .selections
13351                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13352            this.change_selections(Default::default(), window, cx, |s| {
13353                s.select(selections);
13354            });
13355        });
13356    }
13357
13358    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13359        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13360        if self.mode.is_single_line() {
13361            cx.propagate();
13362            return;
13363        }
13364
13365        self.rewrap_impl(RewrapOptions::default(), cx)
13366    }
13367
13368    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13369        let buffer = self.buffer.read(cx).snapshot(cx);
13370        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13371
13372        #[derive(Clone, Debug, PartialEq)]
13373        enum CommentFormat {
13374            /// single line comment, with prefix for line
13375            Line(String),
13376            /// single line within a block comment, with prefix for line
13377            BlockLine(String),
13378            /// a single line of a block comment that includes the initial delimiter
13379            BlockCommentWithStart(BlockCommentConfig),
13380            /// a single line of a block comment that includes the ending delimiter
13381            BlockCommentWithEnd(BlockCommentConfig),
13382        }
13383
13384        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13385        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13386            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13387                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13388                .peekable();
13389
13390            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13391                row
13392            } else {
13393                return Vec::new();
13394            };
13395
13396            let language_settings = buffer.language_settings_at(selection.head(), cx);
13397            let language_scope = buffer.language_scope_at(selection.head());
13398
13399            let indent_and_prefix_for_row =
13400                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13401                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13402                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13403                        &language_scope
13404                    {
13405                        let indent_end = Point::new(row, indent.len);
13406                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13407                        let line_text_after_indent = buffer
13408                            .text_for_range(indent_end..line_end)
13409                            .collect::<String>();
13410
13411                        let is_within_comment_override = buffer
13412                            .language_scope_at(indent_end)
13413                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13414                        let comment_delimiters = if is_within_comment_override {
13415                            // we are within a comment syntax node, but we don't
13416                            // yet know what kind of comment: block, doc or line
13417                            match (
13418                                language_scope.documentation_comment(),
13419                                language_scope.block_comment(),
13420                            ) {
13421                                (Some(config), _) | (_, Some(config))
13422                                    if buffer.contains_str_at(indent_end, &config.start) =>
13423                                {
13424                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13425                                }
13426                                (Some(config), _) | (_, Some(config))
13427                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13428                                {
13429                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13430                                }
13431                                (Some(config), _) | (_, Some(config))
13432                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13433                                {
13434                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13435                                }
13436                                (_, _) => language_scope
13437                                    .line_comment_prefixes()
13438                                    .iter()
13439                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13440                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13441                            }
13442                        } else {
13443                            // we not in an overridden comment node, but we may
13444                            // be within a non-overridden line comment node
13445                            language_scope
13446                                .line_comment_prefixes()
13447                                .iter()
13448                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13449                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13450                        };
13451
13452                        let rewrap_prefix = language_scope
13453                            .rewrap_prefixes()
13454                            .iter()
13455                            .find_map(|prefix_regex| {
13456                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13457                                    if mat.start() == 0 {
13458                                        Some(mat.as_str().to_string())
13459                                    } else {
13460                                        None
13461                                    }
13462                                })
13463                            })
13464                            .flatten();
13465                        (comment_delimiters, rewrap_prefix)
13466                    } else {
13467                        (None, None)
13468                    };
13469                    (indent, comment_prefix, rewrap_prefix)
13470                };
13471
13472            let mut ranges = Vec::new();
13473            let from_empty_selection = selection.is_empty();
13474
13475            let mut current_range_start = first_row;
13476            let mut prev_row = first_row;
13477            let (
13478                mut current_range_indent,
13479                mut current_range_comment_delimiters,
13480                mut current_range_rewrap_prefix,
13481            ) = indent_and_prefix_for_row(first_row);
13482
13483            for row in non_blank_rows_iter.skip(1) {
13484                let has_paragraph_break = row > prev_row + 1;
13485
13486                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13487                    indent_and_prefix_for_row(row);
13488
13489                let has_indent_change = row_indent != current_range_indent;
13490                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13491
13492                let has_boundary_change = has_comment_change
13493                    || row_rewrap_prefix.is_some()
13494                    || (has_indent_change && current_range_comment_delimiters.is_some());
13495
13496                if has_paragraph_break || has_boundary_change {
13497                    ranges.push((
13498                        language_settings.clone(),
13499                        Point::new(current_range_start, 0)
13500                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13501                        current_range_indent,
13502                        current_range_comment_delimiters.clone(),
13503                        current_range_rewrap_prefix.clone(),
13504                        from_empty_selection,
13505                    ));
13506                    current_range_start = row;
13507                    current_range_indent = row_indent;
13508                    current_range_comment_delimiters = row_comment_delimiters;
13509                    current_range_rewrap_prefix = row_rewrap_prefix;
13510                }
13511                prev_row = row;
13512            }
13513
13514            ranges.push((
13515                language_settings.clone(),
13516                Point::new(current_range_start, 0)
13517                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13518                current_range_indent,
13519                current_range_comment_delimiters,
13520                current_range_rewrap_prefix,
13521                from_empty_selection,
13522            ));
13523
13524            ranges
13525        });
13526
13527        let mut edits = Vec::new();
13528        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13529
13530        for (
13531            language_settings,
13532            wrap_range,
13533            mut indent_size,
13534            comment_prefix,
13535            rewrap_prefix,
13536            from_empty_selection,
13537        ) in wrap_ranges
13538        {
13539            let mut start_row = wrap_range.start.row;
13540            let mut end_row = wrap_range.end.row;
13541
13542            // Skip selections that overlap with a range that has already been rewrapped.
13543            let selection_range = start_row..end_row;
13544            if rewrapped_row_ranges
13545                .iter()
13546                .any(|range| range.overlaps(&selection_range))
13547            {
13548                continue;
13549            }
13550
13551            let tab_size = language_settings.tab_size;
13552
13553            let (line_prefix, inside_comment) = match &comment_prefix {
13554                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13555                    (Some(prefix.as_str()), true)
13556                }
13557                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13558                    (Some(prefix.as_ref()), true)
13559                }
13560                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13561                    start: _,
13562                    end: _,
13563                    prefix,
13564                    tab_size,
13565                })) => {
13566                    indent_size.len += tab_size;
13567                    (Some(prefix.as_ref()), true)
13568                }
13569                None => (None, false),
13570            };
13571            let indent_prefix = indent_size.chars().collect::<String>();
13572            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13573
13574            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13575                RewrapBehavior::InComments => inside_comment,
13576                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13577                RewrapBehavior::Anywhere => true,
13578            };
13579
13580            let should_rewrap = options.override_language_settings
13581                || allow_rewrap_based_on_language
13582                || self.hard_wrap.is_some();
13583            if !should_rewrap {
13584                continue;
13585            }
13586
13587            if from_empty_selection {
13588                'expand_upwards: while start_row > 0 {
13589                    let prev_row = start_row - 1;
13590                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13591                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13592                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13593                    {
13594                        start_row = prev_row;
13595                    } else {
13596                        break 'expand_upwards;
13597                    }
13598                }
13599
13600                'expand_downwards: while end_row < buffer.max_point().row {
13601                    let next_row = end_row + 1;
13602                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13603                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13604                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13605                    {
13606                        end_row = next_row;
13607                    } else {
13608                        break 'expand_downwards;
13609                    }
13610                }
13611            }
13612
13613            let start = Point::new(start_row, 0);
13614            let start_offset = ToOffset::to_offset(&start, &buffer);
13615            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13616            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13617            let mut first_line_delimiter = None;
13618            let mut last_line_delimiter = None;
13619            let Some(lines_without_prefixes) = selection_text
13620                .lines()
13621                .enumerate()
13622                .map(|(ix, line)| {
13623                    let line_trimmed = line.trim_start();
13624                    if rewrap_prefix.is_some() && ix > 0 {
13625                        Ok(line_trimmed)
13626                    } else if let Some(
13627                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13628                            start,
13629                            prefix,
13630                            end,
13631                            tab_size,
13632                        })
13633                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13634                            start,
13635                            prefix,
13636                            end,
13637                            tab_size,
13638                        }),
13639                    ) = &comment_prefix
13640                    {
13641                        let line_trimmed = line_trimmed
13642                            .strip_prefix(start.as_ref())
13643                            .map(|s| {
13644                                let mut indent_size = indent_size;
13645                                indent_size.len -= tab_size;
13646                                let indent_prefix: String = indent_size.chars().collect();
13647                                first_line_delimiter = Some((indent_prefix, start));
13648                                s.trim_start()
13649                            })
13650                            .unwrap_or(line_trimmed);
13651                        let line_trimmed = line_trimmed
13652                            .strip_suffix(end.as_ref())
13653                            .map(|s| {
13654                                last_line_delimiter = Some(end);
13655                                s.trim_end()
13656                            })
13657                            .unwrap_or(line_trimmed);
13658                        let line_trimmed = line_trimmed
13659                            .strip_prefix(prefix.as_ref())
13660                            .unwrap_or(line_trimmed);
13661                        Ok(line_trimmed)
13662                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13663                        line_trimmed.strip_prefix(prefix).with_context(|| {
13664                            format!("line did not start with prefix {prefix:?}: {line:?}")
13665                        })
13666                    } else {
13667                        line_trimmed
13668                            .strip_prefix(&line_prefix.trim_start())
13669                            .with_context(|| {
13670                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13671                            })
13672                    }
13673                })
13674                .collect::<Result<Vec<_>, _>>()
13675                .log_err()
13676            else {
13677                continue;
13678            };
13679
13680            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13681                buffer
13682                    .language_settings_at(Point::new(start_row, 0), cx)
13683                    .preferred_line_length as usize
13684            });
13685
13686            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13687                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13688            } else {
13689                line_prefix.clone()
13690            };
13691
13692            let wrapped_text = {
13693                let mut wrapped_text = wrap_with_prefix(
13694                    line_prefix,
13695                    subsequent_lines_prefix,
13696                    lines_without_prefixes.join("\n"),
13697                    wrap_column,
13698                    tab_size,
13699                    options.preserve_existing_whitespace,
13700                );
13701
13702                if let Some((indent, delimiter)) = first_line_delimiter {
13703                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13704                }
13705                if let Some(last_line) = last_line_delimiter {
13706                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13707                }
13708
13709                wrapped_text
13710            };
13711
13712            // TODO: should always use char-based diff while still supporting cursor behavior that
13713            // matches vim.
13714            let mut diff_options = DiffOptions::default();
13715            if options.override_language_settings {
13716                diff_options.max_word_diff_len = 0;
13717                diff_options.max_word_diff_line_count = 0;
13718            } else {
13719                diff_options.max_word_diff_len = usize::MAX;
13720                diff_options.max_word_diff_line_count = usize::MAX;
13721            }
13722
13723            for (old_range, new_text) in
13724                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13725            {
13726                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13727                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13728                edits.push((edit_start..edit_end, new_text));
13729            }
13730
13731            rewrapped_row_ranges.push(start_row..=end_row);
13732        }
13733
13734        self.buffer
13735            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13736    }
13737
13738    pub fn cut_common(
13739        &mut self,
13740        cut_no_selection_line: bool,
13741        window: &mut Window,
13742        cx: &mut Context<Self>,
13743    ) -> ClipboardItem {
13744        let mut text = String::new();
13745        let buffer = self.buffer.read(cx).snapshot(cx);
13746        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13747        let mut clipboard_selections = Vec::with_capacity(selections.len());
13748        {
13749            let max_point = buffer.max_point();
13750            let mut is_first = true;
13751            let mut prev_selection_was_entire_line = false;
13752            for selection in &mut selections {
13753                let is_entire_line =
13754                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13755                if is_entire_line {
13756                    selection.start = Point::new(selection.start.row, 0);
13757                    if !selection.is_empty() && selection.end.column == 0 {
13758                        selection.end = cmp::min(max_point, selection.end);
13759                    } else {
13760                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13761                    }
13762                    selection.goal = SelectionGoal::None;
13763                }
13764                if is_first {
13765                    is_first = false;
13766                } else if !prev_selection_was_entire_line {
13767                    text += "\n";
13768                }
13769                prev_selection_was_entire_line = is_entire_line;
13770                let mut len = 0;
13771                for chunk in buffer.text_for_range(selection.start..selection.end) {
13772                    text.push_str(chunk);
13773                    len += chunk.len();
13774                }
13775
13776                clipboard_selections.push(ClipboardSelection::for_buffer(
13777                    len,
13778                    is_entire_line,
13779                    selection.range(),
13780                    &buffer,
13781                    self.project.as_ref(),
13782                    cx,
13783                ));
13784            }
13785        }
13786
13787        self.transact(window, cx, |this, window, cx| {
13788            this.change_selections(Default::default(), window, cx, |s| {
13789                s.select(selections);
13790            });
13791            this.insert("", window, cx);
13792        });
13793        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13794    }
13795
13796    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13797        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13798        let item = self.cut_common(true, window, cx);
13799        cx.write_to_clipboard(item);
13800    }
13801
13802    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13803        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13804        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13805            s.move_with(&mut |snapshot, sel| {
13806                if sel.is_empty() {
13807                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13808                }
13809                if sel.is_empty() {
13810                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13811                }
13812            });
13813        });
13814        let item = self.cut_common(false, window, cx);
13815        cx.set_global(KillRing(item))
13816    }
13817
13818    pub fn kill_ring_yank(
13819        &mut self,
13820        _: &KillRingYank,
13821        window: &mut Window,
13822        cx: &mut Context<Self>,
13823    ) {
13824        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13825        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13826            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13827                (kill_ring.text().to_string(), kill_ring.metadata_json())
13828            } else {
13829                return;
13830            }
13831        } else {
13832            return;
13833        };
13834        self.do_paste(&text, metadata, false, window, cx);
13835    }
13836
13837    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13838        self.do_copy(true, cx);
13839    }
13840
13841    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13842        self.do_copy(false, cx);
13843    }
13844
13845    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13846        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13847        let buffer = self.buffer.read(cx).read(cx);
13848        let mut text = String::new();
13849        let mut clipboard_selections = Vec::with_capacity(selections.len());
13850
13851        let max_point = buffer.max_point();
13852        let mut is_first = true;
13853        for selection in &selections {
13854            let mut start = selection.start;
13855            let mut end = selection.end;
13856            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13857            let mut add_trailing_newline = false;
13858            if is_entire_line {
13859                start = Point::new(start.row, 0);
13860                let next_line_start = Point::new(end.row + 1, 0);
13861                if next_line_start <= max_point {
13862                    end = next_line_start;
13863                } else {
13864                    // We're on the last line without a trailing newline.
13865                    // Copy to the end of the line and add a newline afterwards.
13866                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13867                    add_trailing_newline = true;
13868                }
13869            }
13870
13871            let mut trimmed_selections = Vec::new();
13872            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13873                let row = MultiBufferRow(start.row);
13874                let first_indent = buffer.indent_size_for_line(row);
13875                if first_indent.len == 0 || start.column > first_indent.len {
13876                    trimmed_selections.push(start..end);
13877                } else {
13878                    trimmed_selections.push(
13879                        Point::new(row.0, first_indent.len)
13880                            ..Point::new(row.0, buffer.line_len(row)),
13881                    );
13882                    for row in start.row + 1..=end.row {
13883                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13884                        if row == end.row {
13885                            line_len = end.column;
13886                        }
13887                        if line_len == 0 {
13888                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13889                            continue;
13890                        }
13891                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13892                        if row_indent_size.len >= first_indent.len {
13893                            trimmed_selections
13894                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13895                        } else {
13896                            trimmed_selections.clear();
13897                            trimmed_selections.push(start..end);
13898                            break;
13899                        }
13900                    }
13901                }
13902            } else {
13903                trimmed_selections.push(start..end);
13904            }
13905
13906            let is_multiline_trim = trimmed_selections.len() > 1;
13907            let mut selection_len: usize = 0;
13908            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13909
13910            for trimmed_range in trimmed_selections {
13911                if is_first {
13912                    is_first = false;
13913                } else if is_multiline_trim || !prev_selection_was_entire_line {
13914                    text.push('\n');
13915                    if is_multiline_trim {
13916                        selection_len += 1;
13917                    }
13918                }
13919                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13920                    text.push_str(chunk);
13921                    selection_len += chunk.len();
13922                }
13923                if add_trailing_newline {
13924                    text.push('\n');
13925                    selection_len += 1;
13926                }
13927            }
13928
13929            clipboard_selections.push(ClipboardSelection::for_buffer(
13930                selection_len,
13931                is_entire_line,
13932                start..end,
13933                &buffer,
13934                self.project.as_ref(),
13935                cx,
13936            ));
13937        }
13938
13939        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13940            text,
13941            clipboard_selections,
13942        ));
13943    }
13944
13945    pub fn do_paste(
13946        &mut self,
13947        text: &String,
13948        clipboard_selections: Option<Vec<ClipboardSelection>>,
13949        handle_entire_lines: bool,
13950        window: &mut Window,
13951        cx: &mut Context<Self>,
13952    ) {
13953        if self.read_only(cx) {
13954            return;
13955        }
13956
13957        let clipboard_text = Cow::Borrowed(text.as_str());
13958
13959        self.transact(window, cx, |this, window, cx| {
13960            let had_active_edit_prediction = this.has_active_edit_prediction();
13961            let display_map = this.display_snapshot(cx);
13962            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13963            let cursor_offset = this
13964                .selections
13965                .last::<MultiBufferOffset>(&display_map)
13966                .head();
13967
13968            if let Some(mut clipboard_selections) = clipboard_selections {
13969                let all_selections_were_entire_line =
13970                    clipboard_selections.iter().all(|s| s.is_entire_line);
13971                let first_selection_indent_column =
13972                    clipboard_selections.first().map(|s| s.first_line_indent);
13973                if clipboard_selections.len() != old_selections.len() {
13974                    clipboard_selections.drain(..);
13975                }
13976                let mut auto_indent_on_paste = true;
13977
13978                this.buffer.update(cx, |buffer, cx| {
13979                    let snapshot = buffer.read(cx);
13980                    auto_indent_on_paste = snapshot
13981                        .language_settings_at(cursor_offset, cx)
13982                        .auto_indent_on_paste;
13983
13984                    let mut start_offset = 0;
13985                    let mut edits = Vec::new();
13986                    let mut original_indent_columns = Vec::new();
13987                    for (ix, selection) in old_selections.iter().enumerate() {
13988                        let to_insert;
13989                        let entire_line;
13990                        let original_indent_column;
13991                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13992                            let end_offset = start_offset + clipboard_selection.len;
13993                            to_insert = &clipboard_text[start_offset..end_offset];
13994                            entire_line = clipboard_selection.is_entire_line;
13995                            start_offset = if entire_line {
13996                                end_offset
13997                            } else {
13998                                end_offset + 1
13999                            };
14000                            original_indent_column = Some(clipboard_selection.first_line_indent);
14001                        } else {
14002                            to_insert = &*clipboard_text;
14003                            entire_line = all_selections_were_entire_line;
14004                            original_indent_column = first_selection_indent_column
14005                        }
14006
14007                        let (range, to_insert) =
14008                            if selection.is_empty() && handle_entire_lines && entire_line {
14009                                // If the corresponding selection was empty when this slice of the
14010                                // clipboard text was written, then the entire line containing the
14011                                // selection was copied. If this selection is also currently empty,
14012                                // then paste the line before the current line of the buffer.
14013                                let column = selection.start.to_point(&snapshot).column as usize;
14014                                let line_start = selection.start - column;
14015                                (line_start..line_start, Cow::Borrowed(to_insert))
14016                            } else {
14017                                let language = snapshot.language_at(selection.head());
14018                                let range = selection.range();
14019                                if let Some(language) = language
14020                                    && language.name() == "Markdown"
14021                                {
14022                                    edit_for_markdown_paste(
14023                                        &snapshot,
14024                                        range,
14025                                        to_insert,
14026                                        url::Url::parse(to_insert).ok(),
14027                                    )
14028                                } else {
14029                                    (range, Cow::Borrowed(to_insert))
14030                                }
14031                            };
14032
14033                        edits.push((range, to_insert));
14034                        original_indent_columns.push(original_indent_column);
14035                    }
14036                    drop(snapshot);
14037
14038                    buffer.edit(
14039                        edits,
14040                        if auto_indent_on_paste {
14041                            Some(AutoindentMode::Block {
14042                                original_indent_columns,
14043                            })
14044                        } else {
14045                            None
14046                        },
14047                        cx,
14048                    );
14049                });
14050
14051                let selections = this
14052                    .selections
14053                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
14054                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14055            } else {
14056                let url = url::Url::parse(&clipboard_text).ok();
14057
14058                let auto_indent_mode = if !clipboard_text.is_empty() {
14059                    Some(AutoindentMode::Block {
14060                        original_indent_columns: Vec::new(),
14061                    })
14062                } else {
14063                    None
14064                };
14065
14066                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
14067                    let snapshot = buffer.snapshot(cx);
14068
14069                    let anchors = old_selections
14070                        .iter()
14071                        .map(|s| {
14072                            let anchor = snapshot.anchor_after(s.head());
14073                            s.map(|_| anchor)
14074                        })
14075                        .collect::<Vec<_>>();
14076
14077                    let mut edits = Vec::new();
14078
14079                    // When pasting text without metadata (e.g. copied from an
14080                    // external editor using multiple cursors) and the number of
14081                    // lines matches the number of selections, distribute one
14082                    // line per cursor instead of pasting the whole text at each.
14083                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14084                    let distribute_lines =
14085                        old_selections.len() > 1 && lines.len() == old_selections.len();
14086
14087                    for (ix, selection) in old_selections.iter().enumerate() {
14088                        let language = snapshot.language_at(selection.head());
14089                        let range = selection.range();
14090
14091                        let text_for_cursor: &str = if distribute_lines {
14092                            lines[ix]
14093                        } else {
14094                            &clipboard_text
14095                        };
14096
14097                        let (edit_range, edit_text) = if let Some(language) = language
14098                            && language.name() == "Markdown"
14099                        {
14100                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14101                        } else {
14102                            (range, Cow::Borrowed(text_for_cursor))
14103                        };
14104
14105                        edits.push((edit_range, edit_text));
14106                    }
14107
14108                    drop(snapshot);
14109                    buffer.edit(edits, auto_indent_mode, cx);
14110
14111                    anchors
14112                });
14113
14114                this.change_selections(Default::default(), window, cx, |s| {
14115                    s.select_anchors(selection_anchors);
14116                });
14117            }
14118
14119            //   🤔                 |    ..     | show_in_menu |
14120            // | ..                  |   true        true
14121            // | had_edit_prediction |   false       true
14122
14123            let trigger_in_words =
14124                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14125
14126            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14127        });
14128    }
14129
14130    pub fn diff_clipboard_with_selection(
14131        &mut self,
14132        _: &DiffClipboardWithSelection,
14133        window: &mut Window,
14134        cx: &mut Context<Self>,
14135    ) {
14136        let selections = self
14137            .selections
14138            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14139
14140        if selections.is_empty() {
14141            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14142            return;
14143        };
14144
14145        let clipboard_text = cx.read_from_clipboard().and_then(|item| {
14146            item.entries().iter().find_map(|entry| match entry {
14147                ClipboardEntry::String(text) => Some(text.text().to_string()),
14148                _ => None,
14149            })
14150        });
14151
14152        let Some(clipboard_text) = clipboard_text else {
14153            log::warn!("Clipboard doesn't contain text.");
14154            return;
14155        };
14156
14157        window.dispatch_action(
14158            Box::new(DiffClipboardWithSelectionData {
14159                clipboard_text,
14160                editor: cx.entity(),
14161            }),
14162            cx,
14163        );
14164    }
14165
14166    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14167        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14168        if let Some(item) = cx.read_from_clipboard() {
14169            let clipboard_string = item.entries().iter().find_map(|entry| match entry {
14170                ClipboardEntry::String(s) => Some(s),
14171                _ => None,
14172            });
14173            match clipboard_string {
14174                Some(clipboard_string) => self.do_paste(
14175                    clipboard_string.text(),
14176                    clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14177                    true,
14178                    window,
14179                    cx,
14180                ),
14181                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14182            }
14183        }
14184    }
14185
14186    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14187        if self.read_only(cx) {
14188            return;
14189        }
14190
14191        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14192
14193        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14194            if let Some((selections, _)) =
14195                self.selection_history.transaction(transaction_id).cloned()
14196            {
14197                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14198                    s.select_anchors(selections.to_vec());
14199                });
14200            } else {
14201                log::error!(
14202                    "No entry in selection_history found for undo. \
14203                     This may correspond to a bug where undo does not update the selection. \
14204                     If this is occurring, please add details to \
14205                     https://github.com/zed-industries/zed/issues/22692"
14206                );
14207            }
14208            self.request_autoscroll(Autoscroll::fit(), cx);
14209            self.unmark_text(window, cx);
14210            self.refresh_edit_prediction(true, false, window, cx);
14211            cx.emit(EditorEvent::Edited { transaction_id });
14212            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14213        }
14214    }
14215
14216    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14217        if self.read_only(cx) {
14218            return;
14219        }
14220
14221        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14222
14223        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14224            if let Some((_, Some(selections))) =
14225                self.selection_history.transaction(transaction_id).cloned()
14226            {
14227                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14228                    s.select_anchors(selections.to_vec());
14229                });
14230            } else {
14231                log::error!(
14232                    "No entry in selection_history found for redo. \
14233                     This may correspond to a bug where undo does not update the selection. \
14234                     If this is occurring, please add details to \
14235                     https://github.com/zed-industries/zed/issues/22692"
14236                );
14237            }
14238            self.request_autoscroll(Autoscroll::fit(), cx);
14239            self.unmark_text(window, cx);
14240            self.refresh_edit_prediction(true, false, window, cx);
14241            cx.emit(EditorEvent::Edited { transaction_id });
14242        }
14243    }
14244
14245    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14246        self.buffer
14247            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14248    }
14249
14250    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14251        self.buffer
14252            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14253    }
14254
14255    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14256        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14257        self.change_selections(Default::default(), window, cx, |s| {
14258            s.move_with(&mut |map, selection| {
14259                let cursor = if selection.is_empty() {
14260                    movement::left(map, selection.start)
14261                } else {
14262                    selection.start
14263                };
14264                selection.collapse_to(cursor, SelectionGoal::None);
14265            });
14266        })
14267    }
14268
14269    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14270        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14271        self.change_selections(Default::default(), window, cx, |s| {
14272            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14273        })
14274    }
14275
14276    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14277        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14278        self.change_selections(Default::default(), window, cx, |s| {
14279            s.move_with(&mut |map, selection| {
14280                let cursor = if selection.is_empty() {
14281                    movement::right(map, selection.end)
14282                } else {
14283                    selection.end
14284                };
14285                selection.collapse_to(cursor, SelectionGoal::None)
14286            });
14287        })
14288    }
14289
14290    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14291        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14292        self.change_selections(Default::default(), window, cx, |s| {
14293            s.move_heads_with(&mut |map, head, _| {
14294                (movement::right(map, head), SelectionGoal::None)
14295            });
14296        });
14297    }
14298
14299    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14300        if self.take_rename(true, window, cx).is_some() {
14301            return;
14302        }
14303
14304        if self.mode.is_single_line() {
14305            cx.propagate();
14306            return;
14307        }
14308
14309        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14310
14311        let text_layout_details = &self.text_layout_details(window, cx);
14312        let selection_count = self.selections.count();
14313        let first_selection = self.selections.first_anchor();
14314
14315        self.change_selections(Default::default(), window, cx, |s| {
14316            s.move_with(&mut |map, selection| {
14317                if !selection.is_empty() {
14318                    selection.goal = SelectionGoal::None;
14319                }
14320                let (cursor, goal) = movement::up(
14321                    map,
14322                    selection.start,
14323                    selection.goal,
14324                    false,
14325                    text_layout_details,
14326                );
14327                selection.collapse_to(cursor, goal);
14328            });
14329        });
14330
14331        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14332        {
14333            cx.propagate();
14334        }
14335    }
14336
14337    pub fn move_up_by_lines(
14338        &mut self,
14339        action: &MoveUpByLines,
14340        window: &mut Window,
14341        cx: &mut Context<Self>,
14342    ) {
14343        if self.take_rename(true, window, cx).is_some() {
14344            return;
14345        }
14346
14347        if self.mode.is_single_line() {
14348            cx.propagate();
14349            return;
14350        }
14351
14352        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14353
14354        let text_layout_details = &self.text_layout_details(window, cx);
14355
14356        self.change_selections(Default::default(), window, cx, |s| {
14357            s.move_with(&mut |map, selection| {
14358                if !selection.is_empty() {
14359                    selection.goal = SelectionGoal::None;
14360                }
14361                let (cursor, goal) = movement::up_by_rows(
14362                    map,
14363                    selection.start,
14364                    action.lines,
14365                    selection.goal,
14366                    false,
14367                    text_layout_details,
14368                );
14369                selection.collapse_to(cursor, goal);
14370            });
14371        })
14372    }
14373
14374    pub fn move_down_by_lines(
14375        &mut self,
14376        action: &MoveDownByLines,
14377        window: &mut Window,
14378        cx: &mut Context<Self>,
14379    ) {
14380        if self.take_rename(true, window, cx).is_some() {
14381            return;
14382        }
14383
14384        if self.mode.is_single_line() {
14385            cx.propagate();
14386            return;
14387        }
14388
14389        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14390
14391        let text_layout_details = &self.text_layout_details(window, cx);
14392
14393        self.change_selections(Default::default(), window, cx, |s| {
14394            s.move_with(&mut |map, selection| {
14395                if !selection.is_empty() {
14396                    selection.goal = SelectionGoal::None;
14397                }
14398                let (cursor, goal) = movement::down_by_rows(
14399                    map,
14400                    selection.start,
14401                    action.lines,
14402                    selection.goal,
14403                    false,
14404                    text_layout_details,
14405                );
14406                selection.collapse_to(cursor, goal);
14407            });
14408        })
14409    }
14410
14411    pub fn select_down_by_lines(
14412        &mut self,
14413        action: &SelectDownByLines,
14414        window: &mut Window,
14415        cx: &mut Context<Self>,
14416    ) {
14417        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14418        let text_layout_details = &self.text_layout_details(window, cx);
14419        self.change_selections(Default::default(), window, cx, |s| {
14420            s.move_heads_with(&mut |map, head, goal| {
14421                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14422            })
14423        })
14424    }
14425
14426    pub fn select_up_by_lines(
14427        &mut self,
14428        action: &SelectUpByLines,
14429        window: &mut Window,
14430        cx: &mut Context<Self>,
14431    ) {
14432        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14433        let text_layout_details = &self.text_layout_details(window, cx);
14434        self.change_selections(Default::default(), window, cx, |s| {
14435            s.move_heads_with(&mut |map, head, goal| {
14436                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14437            })
14438        })
14439    }
14440
14441    pub fn select_page_up(
14442        &mut self,
14443        _: &SelectPageUp,
14444        window: &mut Window,
14445        cx: &mut Context<Self>,
14446    ) {
14447        let Some(row_count) = self.visible_row_count() else {
14448            return;
14449        };
14450
14451        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14452
14453        let text_layout_details = &self.text_layout_details(window, cx);
14454
14455        self.change_selections(Default::default(), window, cx, |s| {
14456            s.move_heads_with(&mut |map, head, goal| {
14457                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14458            })
14459        })
14460    }
14461
14462    pub fn move_page_up(
14463        &mut self,
14464        action: &MovePageUp,
14465        window: &mut Window,
14466        cx: &mut Context<Self>,
14467    ) {
14468        if self.take_rename(true, window, cx).is_some() {
14469            return;
14470        }
14471
14472        if self
14473            .context_menu
14474            .borrow_mut()
14475            .as_mut()
14476            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14477            .unwrap_or(false)
14478        {
14479            return;
14480        }
14481
14482        if matches!(self.mode, EditorMode::SingleLine) {
14483            cx.propagate();
14484            return;
14485        }
14486
14487        let Some(row_count) = self.visible_row_count() else {
14488            return;
14489        };
14490
14491        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14492
14493        let effects = if action.center_cursor {
14494            SelectionEffects::scroll(Autoscroll::center())
14495        } else {
14496            SelectionEffects::default()
14497        };
14498
14499        let text_layout_details = &self.text_layout_details(window, cx);
14500
14501        self.change_selections(effects, window, cx, |s| {
14502            s.move_with(&mut |map, selection| {
14503                if !selection.is_empty() {
14504                    selection.goal = SelectionGoal::None;
14505                }
14506                let (cursor, goal) = movement::up_by_rows(
14507                    map,
14508                    selection.end,
14509                    row_count,
14510                    selection.goal,
14511                    false,
14512                    text_layout_details,
14513                );
14514                selection.collapse_to(cursor, goal);
14515            });
14516        });
14517    }
14518
14519    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14520        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14521        let text_layout_details = &self.text_layout_details(window, cx);
14522        self.change_selections(Default::default(), window, cx, |s| {
14523            s.move_heads_with(&mut |map, head, goal| {
14524                movement::up(map, head, goal, false, text_layout_details)
14525            })
14526        })
14527    }
14528
14529    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14530        self.take_rename(true, window, cx);
14531
14532        if self.mode.is_single_line() {
14533            cx.propagate();
14534            return;
14535        }
14536
14537        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14538
14539        let text_layout_details = &self.text_layout_details(window, cx);
14540        let selection_count = self.selections.count();
14541        let first_selection = self.selections.first_anchor();
14542
14543        self.change_selections(Default::default(), window, cx, |s| {
14544            s.move_with(&mut |map, selection| {
14545                if !selection.is_empty() {
14546                    selection.goal = SelectionGoal::None;
14547                }
14548                let (cursor, goal) = movement::down(
14549                    map,
14550                    selection.end,
14551                    selection.goal,
14552                    false,
14553                    text_layout_details,
14554                );
14555                selection.collapse_to(cursor, goal);
14556            });
14557        });
14558
14559        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14560        {
14561            cx.propagate();
14562        }
14563    }
14564
14565    pub fn select_page_down(
14566        &mut self,
14567        _: &SelectPageDown,
14568        window: &mut Window,
14569        cx: &mut Context<Self>,
14570    ) {
14571        let Some(row_count) = self.visible_row_count() else {
14572            return;
14573        };
14574
14575        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14576
14577        let text_layout_details = &self.text_layout_details(window, cx);
14578
14579        self.change_selections(Default::default(), window, cx, |s| {
14580            s.move_heads_with(&mut |map, head, goal| {
14581                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14582            })
14583        })
14584    }
14585
14586    pub fn move_page_down(
14587        &mut self,
14588        action: &MovePageDown,
14589        window: &mut Window,
14590        cx: &mut Context<Self>,
14591    ) {
14592        if self.take_rename(true, window, cx).is_some() {
14593            return;
14594        }
14595
14596        if self
14597            .context_menu
14598            .borrow_mut()
14599            .as_mut()
14600            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14601            .unwrap_or(false)
14602        {
14603            return;
14604        }
14605
14606        if matches!(self.mode, EditorMode::SingleLine) {
14607            cx.propagate();
14608            return;
14609        }
14610
14611        let Some(row_count) = self.visible_row_count() else {
14612            return;
14613        };
14614
14615        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14616
14617        let effects = if action.center_cursor {
14618            SelectionEffects::scroll(Autoscroll::center())
14619        } else {
14620            SelectionEffects::default()
14621        };
14622
14623        let text_layout_details = &self.text_layout_details(window, cx);
14624        self.change_selections(effects, window, cx, |s| {
14625            s.move_with(&mut |map, selection| {
14626                if !selection.is_empty() {
14627                    selection.goal = SelectionGoal::None;
14628                }
14629                let (cursor, goal) = movement::down_by_rows(
14630                    map,
14631                    selection.end,
14632                    row_count,
14633                    selection.goal,
14634                    false,
14635                    text_layout_details,
14636                );
14637                selection.collapse_to(cursor, goal);
14638            });
14639        });
14640    }
14641
14642    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14643        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14644        let text_layout_details = &self.text_layout_details(window, cx);
14645        self.change_selections(Default::default(), window, cx, |s| {
14646            s.move_heads_with(&mut |map, head, goal| {
14647                movement::down(map, head, goal, false, text_layout_details)
14648            })
14649        });
14650    }
14651
14652    pub fn context_menu_first(
14653        &mut self,
14654        _: &ContextMenuFirst,
14655        window: &mut Window,
14656        cx: &mut Context<Self>,
14657    ) {
14658        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14659            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14660        }
14661    }
14662
14663    pub fn context_menu_prev(
14664        &mut self,
14665        _: &ContextMenuPrevious,
14666        window: &mut Window,
14667        cx: &mut Context<Self>,
14668    ) {
14669        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14670            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14671        }
14672    }
14673
14674    pub fn context_menu_next(
14675        &mut self,
14676        _: &ContextMenuNext,
14677        window: &mut Window,
14678        cx: &mut Context<Self>,
14679    ) {
14680        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14681            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14682        }
14683    }
14684
14685    pub fn context_menu_last(
14686        &mut self,
14687        _: &ContextMenuLast,
14688        window: &mut Window,
14689        cx: &mut Context<Self>,
14690    ) {
14691        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14692            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14693        }
14694    }
14695
14696    pub fn signature_help_prev(
14697        &mut self,
14698        _: &SignatureHelpPrevious,
14699        _: &mut Window,
14700        cx: &mut Context<Self>,
14701    ) {
14702        if let Some(popover) = self.signature_help_state.popover_mut() {
14703            if popover.current_signature == 0 {
14704                popover.current_signature = popover.signatures.len() - 1;
14705            } else {
14706                popover.current_signature -= 1;
14707            }
14708            cx.notify();
14709        }
14710    }
14711
14712    pub fn signature_help_next(
14713        &mut self,
14714        _: &SignatureHelpNext,
14715        _: &mut Window,
14716        cx: &mut Context<Self>,
14717    ) {
14718        if let Some(popover) = self.signature_help_state.popover_mut() {
14719            if popover.current_signature + 1 == popover.signatures.len() {
14720                popover.current_signature = 0;
14721            } else {
14722                popover.current_signature += 1;
14723            }
14724            cx.notify();
14725        }
14726    }
14727
14728    pub fn move_to_previous_word_start(
14729        &mut self,
14730        _: &MoveToPreviousWordStart,
14731        window: &mut Window,
14732        cx: &mut Context<Self>,
14733    ) {
14734        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14735        self.change_selections(Default::default(), window, cx, |s| {
14736            s.move_cursors_with(&mut |map, head, _| {
14737                (
14738                    movement::previous_word_start(map, head),
14739                    SelectionGoal::None,
14740                )
14741            });
14742        })
14743    }
14744
14745    pub fn move_to_previous_subword_start(
14746        &mut self,
14747        _: &MoveToPreviousSubwordStart,
14748        window: &mut Window,
14749        cx: &mut Context<Self>,
14750    ) {
14751        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14752        self.change_selections(Default::default(), window, cx, |s| {
14753            s.move_cursors_with(&mut |map, head, _| {
14754                (
14755                    movement::previous_subword_start(map, head),
14756                    SelectionGoal::None,
14757                )
14758            });
14759        })
14760    }
14761
14762    pub fn select_to_previous_word_start(
14763        &mut self,
14764        _: &SelectToPreviousWordStart,
14765        window: &mut Window,
14766        cx: &mut Context<Self>,
14767    ) {
14768        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14769        self.change_selections(Default::default(), window, cx, |s| {
14770            s.move_heads_with(&mut |map, head, _| {
14771                (
14772                    movement::previous_word_start(map, head),
14773                    SelectionGoal::None,
14774                )
14775            });
14776        })
14777    }
14778
14779    pub fn select_to_previous_subword_start(
14780        &mut self,
14781        _: &SelectToPreviousSubwordStart,
14782        window: &mut Window,
14783        cx: &mut Context<Self>,
14784    ) {
14785        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14786        self.change_selections(Default::default(), window, cx, |s| {
14787            s.move_heads_with(&mut |map, head, _| {
14788                (
14789                    movement::previous_subword_start(map, head),
14790                    SelectionGoal::None,
14791                )
14792            });
14793        })
14794    }
14795
14796    pub fn delete_to_previous_word_start(
14797        &mut self,
14798        action: &DeleteToPreviousWordStart,
14799        window: &mut Window,
14800        cx: &mut Context<Self>,
14801    ) {
14802        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14803        self.transact(window, cx, |this, window, cx| {
14804            this.select_autoclose_pair(window, cx);
14805            this.change_selections(Default::default(), window, cx, |s| {
14806                s.move_with(&mut |map, selection| {
14807                    if selection.is_empty() {
14808                        let mut cursor = if action.ignore_newlines {
14809                            movement::previous_word_start(map, selection.head())
14810                        } else {
14811                            movement::previous_word_start_or_newline(map, selection.head())
14812                        };
14813                        cursor = movement::adjust_greedy_deletion(
14814                            map,
14815                            selection.head(),
14816                            cursor,
14817                            action.ignore_brackets,
14818                        );
14819                        selection.set_head(cursor, SelectionGoal::None);
14820                    }
14821                });
14822            });
14823            this.insert("", window, cx);
14824        });
14825    }
14826
14827    pub fn delete_to_previous_subword_start(
14828        &mut self,
14829        action: &DeleteToPreviousSubwordStart,
14830        window: &mut Window,
14831        cx: &mut Context<Self>,
14832    ) {
14833        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14834        self.transact(window, cx, |this, window, cx| {
14835            this.select_autoclose_pair(window, cx);
14836            this.change_selections(Default::default(), window, cx, |s| {
14837                s.move_with(&mut |map, selection| {
14838                    if selection.is_empty() {
14839                        let mut cursor = if action.ignore_newlines {
14840                            movement::previous_subword_start(map, selection.head())
14841                        } else {
14842                            movement::previous_subword_start_or_newline(map, selection.head())
14843                        };
14844                        cursor = movement::adjust_greedy_deletion(
14845                            map,
14846                            selection.head(),
14847                            cursor,
14848                            action.ignore_brackets,
14849                        );
14850                        selection.set_head(cursor, SelectionGoal::None);
14851                    }
14852                });
14853            });
14854            this.insert("", window, cx);
14855        });
14856    }
14857
14858    pub fn move_to_next_word_end(
14859        &mut self,
14860        _: &MoveToNextWordEnd,
14861        window: &mut Window,
14862        cx: &mut Context<Self>,
14863    ) {
14864        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14865        self.change_selections(Default::default(), window, cx, |s| {
14866            s.move_cursors_with(&mut |map, head, _| {
14867                (movement::next_word_end(map, head), SelectionGoal::None)
14868            });
14869        })
14870    }
14871
14872    pub fn move_to_next_subword_end(
14873        &mut self,
14874        _: &MoveToNextSubwordEnd,
14875        window: &mut Window,
14876        cx: &mut Context<Self>,
14877    ) {
14878        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14879        self.change_selections(Default::default(), window, cx, |s| {
14880            s.move_cursors_with(&mut |map, head, _| {
14881                (movement::next_subword_end(map, head), SelectionGoal::None)
14882            });
14883        })
14884    }
14885
14886    pub fn select_to_next_word_end(
14887        &mut self,
14888        _: &SelectToNextWordEnd,
14889        window: &mut Window,
14890        cx: &mut Context<Self>,
14891    ) {
14892        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14893        self.change_selections(Default::default(), window, cx, |s| {
14894            s.move_heads_with(&mut |map, head, _| {
14895                (movement::next_word_end(map, head), SelectionGoal::None)
14896            });
14897        })
14898    }
14899
14900    pub fn select_to_next_subword_end(
14901        &mut self,
14902        _: &SelectToNextSubwordEnd,
14903        window: &mut Window,
14904        cx: &mut Context<Self>,
14905    ) {
14906        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14907        self.change_selections(Default::default(), window, cx, |s| {
14908            s.move_heads_with(&mut |map, head, _| {
14909                (movement::next_subword_end(map, head), SelectionGoal::None)
14910            });
14911        })
14912    }
14913
14914    pub fn delete_to_next_word_end(
14915        &mut self,
14916        action: &DeleteToNextWordEnd,
14917        window: &mut Window,
14918        cx: &mut Context<Self>,
14919    ) {
14920        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14921        self.transact(window, cx, |this, window, cx| {
14922            this.change_selections(Default::default(), window, cx, |s| {
14923                s.move_with(&mut |map, selection| {
14924                    if selection.is_empty() {
14925                        let mut cursor = if action.ignore_newlines {
14926                            movement::next_word_end(map, selection.head())
14927                        } else {
14928                            movement::next_word_end_or_newline(map, selection.head())
14929                        };
14930                        cursor = movement::adjust_greedy_deletion(
14931                            map,
14932                            selection.head(),
14933                            cursor,
14934                            action.ignore_brackets,
14935                        );
14936                        selection.set_head(cursor, SelectionGoal::None);
14937                    }
14938                });
14939            });
14940            this.insert("", window, cx);
14941        });
14942    }
14943
14944    pub fn delete_to_next_subword_end(
14945        &mut self,
14946        action: &DeleteToNextSubwordEnd,
14947        window: &mut Window,
14948        cx: &mut Context<Self>,
14949    ) {
14950        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14951        self.transact(window, cx, |this, window, cx| {
14952            this.change_selections(Default::default(), window, cx, |s| {
14953                s.move_with(&mut |map, selection| {
14954                    if selection.is_empty() {
14955                        let mut cursor = if action.ignore_newlines {
14956                            movement::next_subword_end(map, selection.head())
14957                        } else {
14958                            movement::next_subword_end_or_newline(map, selection.head())
14959                        };
14960                        cursor = movement::adjust_greedy_deletion(
14961                            map,
14962                            selection.head(),
14963                            cursor,
14964                            action.ignore_brackets,
14965                        );
14966                        selection.set_head(cursor, SelectionGoal::None);
14967                    }
14968                });
14969            });
14970            this.insert("", window, cx);
14971        });
14972    }
14973
14974    pub fn move_to_beginning_of_line(
14975        &mut self,
14976        action: &MoveToBeginningOfLine,
14977        window: &mut Window,
14978        cx: &mut Context<Self>,
14979    ) {
14980        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
14981        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14982        self.change_selections(Default::default(), window, cx, |s| {
14983            s.move_cursors_with(&mut |map, head, _| {
14984                (
14985                    movement::indented_line_beginning(
14986                        map,
14987                        head,
14988                        action.stop_at_soft_wraps,
14989                        stop_at_indent,
14990                    ),
14991                    SelectionGoal::None,
14992                )
14993            });
14994        })
14995    }
14996
14997    pub fn select_to_beginning_of_line(
14998        &mut self,
14999        action: &SelectToBeginningOfLine,
15000        window: &mut Window,
15001        cx: &mut Context<Self>,
15002    ) {
15003        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15004        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15005        self.change_selections(Default::default(), window, cx, |s| {
15006            s.move_heads_with(&mut |map, head, _| {
15007                (
15008                    movement::indented_line_beginning(
15009                        map,
15010                        head,
15011                        action.stop_at_soft_wraps,
15012                        stop_at_indent,
15013                    ),
15014                    SelectionGoal::None,
15015                )
15016            });
15017        });
15018    }
15019
15020    pub fn delete_to_beginning_of_line(
15021        &mut self,
15022        action: &DeleteToBeginningOfLine,
15023        window: &mut Window,
15024        cx: &mut Context<Self>,
15025    ) {
15026        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15027        self.transact(window, cx, |this, window, cx| {
15028            this.change_selections(Default::default(), window, cx, |s| {
15029                s.move_with(&mut |_, selection| {
15030                    selection.reversed = true;
15031                });
15032            });
15033
15034            this.select_to_beginning_of_line(
15035                &SelectToBeginningOfLine {
15036                    stop_at_soft_wraps: false,
15037                    stop_at_indent: action.stop_at_indent,
15038                },
15039                window,
15040                cx,
15041            );
15042            this.backspace(&Backspace, window, cx);
15043        });
15044    }
15045
15046    pub fn move_to_end_of_line(
15047        &mut self,
15048        action: &MoveToEndOfLine,
15049        window: &mut Window,
15050        cx: &mut Context<Self>,
15051    ) {
15052        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15053        self.change_selections(Default::default(), window, cx, |s| {
15054            s.move_cursors_with(&mut |map, head, _| {
15055                (
15056                    movement::line_end(map, head, action.stop_at_soft_wraps),
15057                    SelectionGoal::None,
15058                )
15059            });
15060        })
15061    }
15062
15063    pub fn select_to_end_of_line(
15064        &mut self,
15065        action: &SelectToEndOfLine,
15066        window: &mut Window,
15067        cx: &mut Context<Self>,
15068    ) {
15069        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15070        self.change_selections(Default::default(), window, cx, |s| {
15071            s.move_heads_with(&mut |map, head, _| {
15072                (
15073                    movement::line_end(map, head, action.stop_at_soft_wraps),
15074                    SelectionGoal::None,
15075                )
15076            });
15077        })
15078    }
15079
15080    pub fn delete_to_end_of_line(
15081        &mut self,
15082        _: &DeleteToEndOfLine,
15083        window: &mut Window,
15084        cx: &mut Context<Self>,
15085    ) {
15086        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15087        self.transact(window, cx, |this, window, cx| {
15088            this.select_to_end_of_line(
15089                &SelectToEndOfLine {
15090                    stop_at_soft_wraps: false,
15091                },
15092                window,
15093                cx,
15094            );
15095            this.delete(&Delete, window, cx);
15096        });
15097    }
15098
15099    pub fn cut_to_end_of_line(
15100        &mut self,
15101        action: &CutToEndOfLine,
15102        window: &mut Window,
15103        cx: &mut Context<Self>,
15104    ) {
15105        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15106        self.transact(window, cx, |this, window, cx| {
15107            this.select_to_end_of_line(
15108                &SelectToEndOfLine {
15109                    stop_at_soft_wraps: false,
15110                },
15111                window,
15112                cx,
15113            );
15114            if !action.stop_at_newlines {
15115                this.change_selections(Default::default(), window, cx, |s| {
15116                    s.move_with(&mut |_, sel| {
15117                        if sel.is_empty() {
15118                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15119                        }
15120                    });
15121                });
15122            }
15123            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15124            let item = this.cut_common(false, window, cx);
15125            cx.write_to_clipboard(item);
15126        });
15127    }
15128
15129    pub fn move_to_start_of_paragraph(
15130        &mut self,
15131        _: &MoveToStartOfParagraph,
15132        window: &mut Window,
15133        cx: &mut Context<Self>,
15134    ) {
15135        if matches!(self.mode, EditorMode::SingleLine) {
15136            cx.propagate();
15137            return;
15138        }
15139        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15140        self.change_selections(Default::default(), window, cx, |s| {
15141            s.move_with(&mut |map, selection| {
15142                selection.collapse_to(
15143                    movement::start_of_paragraph(map, selection.head(), 1),
15144                    SelectionGoal::None,
15145                )
15146            });
15147        })
15148    }
15149
15150    pub fn move_to_end_of_paragraph(
15151        &mut self,
15152        _: &MoveToEndOfParagraph,
15153        window: &mut Window,
15154        cx: &mut Context<Self>,
15155    ) {
15156        if matches!(self.mode, EditorMode::SingleLine) {
15157            cx.propagate();
15158            return;
15159        }
15160        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15161        self.change_selections(Default::default(), window, cx, |s| {
15162            s.move_with(&mut |map, selection| {
15163                selection.collapse_to(
15164                    movement::end_of_paragraph(map, selection.head(), 1),
15165                    SelectionGoal::None,
15166                )
15167            });
15168        })
15169    }
15170
15171    pub fn select_to_start_of_paragraph(
15172        &mut self,
15173        _: &SelectToStartOfParagraph,
15174        window: &mut Window,
15175        cx: &mut Context<Self>,
15176    ) {
15177        if matches!(self.mode, EditorMode::SingleLine) {
15178            cx.propagate();
15179            return;
15180        }
15181        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15182        self.change_selections(Default::default(), window, cx, |s| {
15183            s.move_heads_with(&mut |map, head, _| {
15184                (
15185                    movement::start_of_paragraph(map, head, 1),
15186                    SelectionGoal::None,
15187                )
15188            });
15189        })
15190    }
15191
15192    pub fn select_to_end_of_paragraph(
15193        &mut self,
15194        _: &SelectToEndOfParagraph,
15195        window: &mut Window,
15196        cx: &mut Context<Self>,
15197    ) {
15198        if matches!(self.mode, EditorMode::SingleLine) {
15199            cx.propagate();
15200            return;
15201        }
15202        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15203        self.change_selections(Default::default(), window, cx, |s| {
15204            s.move_heads_with(&mut |map, head, _| {
15205                (
15206                    movement::end_of_paragraph(map, head, 1),
15207                    SelectionGoal::None,
15208                )
15209            });
15210        })
15211    }
15212
15213    pub fn move_to_start_of_excerpt(
15214        &mut self,
15215        _: &MoveToStartOfExcerpt,
15216        window: &mut Window,
15217        cx: &mut Context<Self>,
15218    ) {
15219        if matches!(self.mode, EditorMode::SingleLine) {
15220            cx.propagate();
15221            return;
15222        }
15223        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15224        self.change_selections(Default::default(), window, cx, |s| {
15225            s.move_with(&mut |map, selection| {
15226                selection.collapse_to(
15227                    movement::start_of_excerpt(
15228                        map,
15229                        selection.head(),
15230                        workspace::searchable::Direction::Prev,
15231                    ),
15232                    SelectionGoal::None,
15233                )
15234            });
15235        })
15236    }
15237
15238    pub fn move_to_start_of_next_excerpt(
15239        &mut self,
15240        _: &MoveToStartOfNextExcerpt,
15241        window: &mut Window,
15242        cx: &mut Context<Self>,
15243    ) {
15244        if matches!(self.mode, EditorMode::SingleLine) {
15245            cx.propagate();
15246            return;
15247        }
15248
15249        self.change_selections(Default::default(), window, cx, |s| {
15250            s.move_with(&mut |map, selection| {
15251                selection.collapse_to(
15252                    movement::start_of_excerpt(
15253                        map,
15254                        selection.head(),
15255                        workspace::searchable::Direction::Next,
15256                    ),
15257                    SelectionGoal::None,
15258                )
15259            });
15260        })
15261    }
15262
15263    pub fn move_to_end_of_excerpt(
15264        &mut self,
15265        _: &MoveToEndOfExcerpt,
15266        window: &mut Window,
15267        cx: &mut Context<Self>,
15268    ) {
15269        if matches!(self.mode, EditorMode::SingleLine) {
15270            cx.propagate();
15271            return;
15272        }
15273        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15274        self.change_selections(Default::default(), window, cx, |s| {
15275            s.move_with(&mut |map, selection| {
15276                selection.collapse_to(
15277                    movement::end_of_excerpt(
15278                        map,
15279                        selection.head(),
15280                        workspace::searchable::Direction::Next,
15281                    ),
15282                    SelectionGoal::None,
15283                )
15284            });
15285        })
15286    }
15287
15288    pub fn move_to_end_of_previous_excerpt(
15289        &mut self,
15290        _: &MoveToEndOfPreviousExcerpt,
15291        window: &mut Window,
15292        cx: &mut Context<Self>,
15293    ) {
15294        if matches!(self.mode, EditorMode::SingleLine) {
15295            cx.propagate();
15296            return;
15297        }
15298        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15299        self.change_selections(Default::default(), window, cx, |s| {
15300            s.move_with(&mut |map, selection| {
15301                selection.collapse_to(
15302                    movement::end_of_excerpt(
15303                        map,
15304                        selection.head(),
15305                        workspace::searchable::Direction::Prev,
15306                    ),
15307                    SelectionGoal::None,
15308                )
15309            });
15310        })
15311    }
15312
15313    pub fn select_to_start_of_excerpt(
15314        &mut self,
15315        _: &SelectToStartOfExcerpt,
15316        window: &mut Window,
15317        cx: &mut Context<Self>,
15318    ) {
15319        if matches!(self.mode, EditorMode::SingleLine) {
15320            cx.propagate();
15321            return;
15322        }
15323        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15324        self.change_selections(Default::default(), window, cx, |s| {
15325            s.move_heads_with(&mut |map, head, _| {
15326                (
15327                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15328                    SelectionGoal::None,
15329                )
15330            });
15331        })
15332    }
15333
15334    pub fn select_to_start_of_next_excerpt(
15335        &mut self,
15336        _: &SelectToStartOfNextExcerpt,
15337        window: &mut Window,
15338        cx: &mut Context<Self>,
15339    ) {
15340        if matches!(self.mode, EditorMode::SingleLine) {
15341            cx.propagate();
15342            return;
15343        }
15344        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15345        self.change_selections(Default::default(), window, cx, |s| {
15346            s.move_heads_with(&mut |map, head, _| {
15347                (
15348                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15349                    SelectionGoal::None,
15350                )
15351            });
15352        })
15353    }
15354
15355    pub fn select_to_end_of_excerpt(
15356        &mut self,
15357        _: &SelectToEndOfExcerpt,
15358        window: &mut Window,
15359        cx: &mut Context<Self>,
15360    ) {
15361        if matches!(self.mode, EditorMode::SingleLine) {
15362            cx.propagate();
15363            return;
15364        }
15365        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15366        self.change_selections(Default::default(), window, cx, |s| {
15367            s.move_heads_with(&mut |map, head, _| {
15368                (
15369                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15370                    SelectionGoal::None,
15371                )
15372            });
15373        })
15374    }
15375
15376    pub fn select_to_end_of_previous_excerpt(
15377        &mut self,
15378        _: &SelectToEndOfPreviousExcerpt,
15379        window: &mut Window,
15380        cx: &mut Context<Self>,
15381    ) {
15382        if matches!(self.mode, EditorMode::SingleLine) {
15383            cx.propagate();
15384            return;
15385        }
15386        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15387        self.change_selections(Default::default(), window, cx, |s| {
15388            s.move_heads_with(&mut |map, head, _| {
15389                (
15390                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15391                    SelectionGoal::None,
15392                )
15393            });
15394        })
15395    }
15396
15397    pub fn move_to_beginning(
15398        &mut self,
15399        _: &MoveToBeginning,
15400        window: &mut Window,
15401        cx: &mut Context<Self>,
15402    ) {
15403        if matches!(self.mode, EditorMode::SingleLine) {
15404            cx.propagate();
15405            return;
15406        }
15407        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15408        self.change_selections(Default::default(), window, cx, |s| {
15409            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15410        });
15411    }
15412
15413    pub fn select_to_beginning(
15414        &mut self,
15415        _: &SelectToBeginning,
15416        window: &mut Window,
15417        cx: &mut Context<Self>,
15418    ) {
15419        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15420        selection.set_head(Point::zero(), SelectionGoal::None);
15421        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15422        self.change_selections(Default::default(), window, cx, |s| {
15423            s.select(vec![selection]);
15424        });
15425    }
15426
15427    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15428        if matches!(self.mode, EditorMode::SingleLine) {
15429            cx.propagate();
15430            return;
15431        }
15432        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15433        let cursor = self.buffer.read(cx).read(cx).len();
15434        self.change_selections(Default::default(), window, cx, |s| {
15435            s.select_ranges(vec![cursor..cursor])
15436        });
15437    }
15438
15439    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15440        self.nav_history = nav_history;
15441    }
15442
15443    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15444        self.nav_history.as_ref()
15445    }
15446
15447    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15448        self.push_to_nav_history(
15449            self.selections.newest_anchor().head(),
15450            None,
15451            false,
15452            true,
15453            cx,
15454        );
15455    }
15456
15457    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15458        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15459        let buffer = self.buffer.read(cx).read(cx);
15460        let cursor_position = cursor_anchor.to_point(&buffer);
15461        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15462        let scroll_top_row = scroll_anchor.top_row(&buffer);
15463        drop(buffer);
15464
15465        NavigationData {
15466            cursor_anchor,
15467            cursor_position,
15468            scroll_anchor,
15469            scroll_top_row,
15470        }
15471    }
15472
15473    fn navigation_entry(
15474        &self,
15475        cursor_anchor: Anchor,
15476        cx: &mut Context<Self>,
15477    ) -> Option<NavigationEntry> {
15478        let Some(history) = self.nav_history.clone() else {
15479            return None;
15480        };
15481        let data = self.navigation_data(cursor_anchor, cx);
15482        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15483    }
15484
15485    fn push_to_nav_history(
15486        &mut self,
15487        cursor_anchor: Anchor,
15488        new_position: Option<Point>,
15489        is_deactivate: bool,
15490        always: bool,
15491        cx: &mut Context<Self>,
15492    ) {
15493        let data = self.navigation_data(cursor_anchor, cx);
15494        if let Some(nav_history) = self.nav_history.as_mut() {
15495            if let Some(new_position) = new_position {
15496                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15497                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15498                    return;
15499                }
15500            }
15501
15502            nav_history.push(Some(data), cx);
15503            cx.emit(EditorEvent::PushedToNavHistory {
15504                anchor: cursor_anchor,
15505                is_deactivate,
15506            })
15507        }
15508    }
15509
15510    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15511        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15512        let buffer = self.buffer.read(cx).snapshot(cx);
15513        let mut selection = self
15514            .selections
15515            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15516        selection.set_head(buffer.len(), SelectionGoal::None);
15517        self.change_selections(Default::default(), window, cx, |s| {
15518            s.select(vec![selection]);
15519        });
15520    }
15521
15522    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15523        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15524        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15525            s.select_ranges([Anchor::min()..Anchor::max()]);
15526        });
15527    }
15528
15529    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15530        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15531        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15532        let mut selections = self.selections.all::<Point>(&display_map);
15533        let max_point = display_map.buffer_snapshot().max_point();
15534        for selection in &mut selections {
15535            let rows = selection.spanned_rows(true, &display_map);
15536            selection.start = Point::new(rows.start.0, 0);
15537            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15538            selection.reversed = false;
15539        }
15540        self.change_selections(Default::default(), window, cx, |s| {
15541            s.select(selections);
15542        });
15543    }
15544
15545    pub fn split_selection_into_lines(
15546        &mut self,
15547        action: &SplitSelectionIntoLines,
15548        window: &mut Window,
15549        cx: &mut Context<Self>,
15550    ) {
15551        let selections = self
15552            .selections
15553            .all::<Point>(&self.display_snapshot(cx))
15554            .into_iter()
15555            .map(|selection| selection.start..selection.end)
15556            .collect::<Vec<_>>();
15557        self.unfold_ranges(&selections, true, false, cx);
15558
15559        let mut new_selection_ranges = Vec::new();
15560        {
15561            let buffer = self.buffer.read(cx).read(cx);
15562            for selection in selections {
15563                for row in selection.start.row..selection.end.row {
15564                    let line_start = Point::new(row, 0);
15565                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15566
15567                    if action.keep_selections {
15568                        // Keep the selection range for each line
15569                        let selection_start = if row == selection.start.row {
15570                            selection.start
15571                        } else {
15572                            line_start
15573                        };
15574                        new_selection_ranges.push(selection_start..line_end);
15575                    } else {
15576                        // Collapse to cursor at end of line
15577                        new_selection_ranges.push(line_end..line_end);
15578                    }
15579                }
15580
15581                let is_multiline_selection = selection.start.row != selection.end.row;
15582                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15583                // so this action feels more ergonomic when paired with other selection operations
15584                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15585                if !should_skip_last {
15586                    if action.keep_selections {
15587                        if is_multiline_selection {
15588                            let line_start = Point::new(selection.end.row, 0);
15589                            new_selection_ranges.push(line_start..selection.end);
15590                        } else {
15591                            new_selection_ranges.push(selection.start..selection.end);
15592                        }
15593                    } else {
15594                        new_selection_ranges.push(selection.end..selection.end);
15595                    }
15596                }
15597            }
15598        }
15599        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15600            s.select_ranges(new_selection_ranges);
15601        });
15602    }
15603
15604    pub fn add_selection_above(
15605        &mut self,
15606        action: &AddSelectionAbove,
15607        window: &mut Window,
15608        cx: &mut Context<Self>,
15609    ) {
15610        self.add_selection(true, action.skip_soft_wrap, window, cx);
15611    }
15612
15613    pub fn add_selection_below(
15614        &mut self,
15615        action: &AddSelectionBelow,
15616        window: &mut Window,
15617        cx: &mut Context<Self>,
15618    ) {
15619        self.add_selection(false, action.skip_soft_wrap, window, cx);
15620    }
15621
15622    fn add_selection(
15623        &mut self,
15624        above: bool,
15625        skip_soft_wrap: bool,
15626        window: &mut Window,
15627        cx: &mut Context<Self>,
15628    ) {
15629        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15630
15631        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15632        let all_selections = self.selections.all::<Point>(&display_map);
15633        let text_layout_details = self.text_layout_details(window, cx);
15634
15635        let (mut columnar_selections, new_selections_to_columnarize) = {
15636            if let Some(state) = self.add_selections_state.as_ref() {
15637                let columnar_selection_ids: HashSet<_> = state
15638                    .groups
15639                    .iter()
15640                    .flat_map(|group| group.stack.iter())
15641                    .copied()
15642                    .collect();
15643
15644                all_selections
15645                    .into_iter()
15646                    .partition(|s| columnar_selection_ids.contains(&s.id))
15647            } else {
15648                (Vec::new(), all_selections)
15649            }
15650        };
15651
15652        let mut state = self
15653            .add_selections_state
15654            .take()
15655            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15656
15657        for selection in new_selections_to_columnarize {
15658            let range = selection.display_range(&display_map).sorted();
15659            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15660            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15661            let positions = start_x.min(end_x)..start_x.max(end_x);
15662            let mut stack = Vec::new();
15663            for row in range.start.row().0..=range.end.row().0 {
15664                if let Some(selection) = self.selections.build_columnar_selection(
15665                    &display_map,
15666                    DisplayRow(row),
15667                    &positions,
15668                    selection.reversed,
15669                    &text_layout_details,
15670                ) {
15671                    stack.push(selection.id);
15672                    columnar_selections.push(selection);
15673                }
15674            }
15675            if !stack.is_empty() {
15676                if above {
15677                    stack.reverse();
15678                }
15679                state.groups.push(AddSelectionsGroup { above, stack });
15680            }
15681        }
15682
15683        let mut final_selections = Vec::new();
15684        let end_row = if above {
15685            DisplayRow(0)
15686        } else {
15687            display_map.max_point().row()
15688        };
15689
15690        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15691        // positions to place new selections, so we need to keep track of the
15692        // column range of the oldest selection in each group, because
15693        // intermediate selections may have been clamped to shorter lines.
15694        // selections may have been clamped to shorter lines.
15695        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15696            let mut map = HashMap::default();
15697            for group in state.groups.iter() {
15698                if let Some(oldest_id) = group.stack.first() {
15699                    if let Some(oldest_selection) =
15700                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15701                    {
15702                        let start_col = oldest_selection.start.column;
15703                        let end_col = oldest_selection.end.column;
15704                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15705                        for id in &group.stack {
15706                            map.insert(*id, goal_columns.clone());
15707                        }
15708                    }
15709                }
15710            }
15711            map
15712        } else {
15713            HashMap::default()
15714        };
15715
15716        let mut last_added_item_per_group = HashMap::default();
15717        for group in state.groups.iter_mut() {
15718            if let Some(last_id) = group.stack.last() {
15719                last_added_item_per_group.insert(*last_id, group);
15720            }
15721        }
15722
15723        for selection in columnar_selections {
15724            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15725                if above == group.above {
15726                    let range = selection.display_range(&display_map).sorted();
15727                    debug_assert_eq!(range.start.row(), range.end.row());
15728                    let row = range.start.row();
15729                    let positions =
15730                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15731                            Pixels::from(start)..Pixels::from(end)
15732                        } else {
15733                            let start_x =
15734                                display_map.x_for_display_point(range.start, &text_layout_details);
15735                            let end_x =
15736                                display_map.x_for_display_point(range.end, &text_layout_details);
15737                            start_x.min(end_x)..start_x.max(end_x)
15738                        };
15739
15740                    let maybe_new_selection = if skip_soft_wrap {
15741                        let goal_columns = goal_columns_by_selection_id
15742                            .remove(&selection.id)
15743                            .unwrap_or_else(|| {
15744                                let start_col = selection.start.column;
15745                                let end_col = selection.end.column;
15746                                start_col.min(end_col)..start_col.max(end_col)
15747                            });
15748                        self.selections.find_next_columnar_selection_by_buffer_row(
15749                            &display_map,
15750                            row,
15751                            end_row,
15752                            above,
15753                            &goal_columns,
15754                            selection.reversed,
15755                            &text_layout_details,
15756                        )
15757                    } else {
15758                        self.selections.find_next_columnar_selection_by_display_row(
15759                            &display_map,
15760                            row,
15761                            end_row,
15762                            above,
15763                            &positions,
15764                            selection.reversed,
15765                            &text_layout_details,
15766                        )
15767                    };
15768
15769                    if let Some(new_selection) = maybe_new_selection {
15770                        group.stack.push(new_selection.id);
15771                        if above {
15772                            final_selections.push(new_selection);
15773                            final_selections.push(selection);
15774                        } else {
15775                            final_selections.push(selection);
15776                            final_selections.push(new_selection);
15777                        }
15778                    } else {
15779                        final_selections.push(selection);
15780                    }
15781                } else {
15782                    group.stack.pop();
15783                }
15784            } else {
15785                final_selections.push(selection);
15786            }
15787        }
15788
15789        self.change_selections(Default::default(), window, cx, |s| {
15790            s.select(final_selections);
15791        });
15792
15793        let final_selection_ids: HashSet<_> = self
15794            .selections
15795            .all::<Point>(&display_map)
15796            .iter()
15797            .map(|s| s.id)
15798            .collect();
15799        state.groups.retain_mut(|group| {
15800            // selections might get merged above so we remove invalid items from stacks
15801            group.stack.retain(|id| final_selection_ids.contains(id));
15802
15803            // single selection in stack can be treated as initial state
15804            group.stack.len() > 1
15805        });
15806
15807        if !state.groups.is_empty() {
15808            self.add_selections_state = Some(state);
15809        }
15810    }
15811
15812    pub fn insert_snippet_at_selections(
15813        &mut self,
15814        action: &InsertSnippet,
15815        window: &mut Window,
15816        cx: &mut Context<Self>,
15817    ) {
15818        self.try_insert_snippet_at_selections(action, window, cx)
15819            .log_err();
15820    }
15821
15822    fn try_insert_snippet_at_selections(
15823        &mut self,
15824        action: &InsertSnippet,
15825        window: &mut Window,
15826        cx: &mut Context<Self>,
15827    ) -> Result<()> {
15828        let insertion_ranges = self
15829            .selections
15830            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15831            .into_iter()
15832            .map(|selection| selection.range())
15833            .collect_vec();
15834
15835        let snippet = if let Some(snippet_body) = &action.snippet {
15836            if action.language.is_none() && action.name.is_none() {
15837                Snippet::parse(snippet_body)?
15838            } else {
15839                bail!("`snippet` is mutually exclusive with `language` and `name`")
15840            }
15841        } else if let Some(name) = &action.name {
15842            let project = self.project().context("no project")?;
15843            let snippet_store = project.read(cx).snippets().read(cx);
15844            let snippet = snippet_store
15845                .snippets_for(action.language.clone(), cx)
15846                .into_iter()
15847                .find(|snippet| snippet.name == *name)
15848                .context("snippet not found")?;
15849            Snippet::parse(&snippet.body)?
15850        } else {
15851            // todo(andrew): open modal to select snippet
15852            bail!("`name` or `snippet` is required")
15853        };
15854
15855        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15856    }
15857
15858    fn select_match_ranges(
15859        &mut self,
15860        range: Range<MultiBufferOffset>,
15861        reversed: bool,
15862        replace_newest: bool,
15863        auto_scroll: Option<Autoscroll>,
15864        window: &mut Window,
15865        cx: &mut Context<Editor>,
15866    ) {
15867        self.unfold_ranges(
15868            std::slice::from_ref(&range),
15869            false,
15870            auto_scroll.is_some(),
15871            cx,
15872        );
15873        let effects = if let Some(scroll) = auto_scroll {
15874            SelectionEffects::scroll(scroll)
15875        } else {
15876            SelectionEffects::no_scroll()
15877        };
15878        self.change_selections(effects, window, cx, |s| {
15879            if replace_newest {
15880                s.delete(s.newest_anchor().id);
15881            }
15882            if reversed {
15883                s.insert_range(range.end..range.start);
15884            } else {
15885                s.insert_range(range);
15886            }
15887        });
15888    }
15889
15890    pub fn select_next_match_internal(
15891        &mut self,
15892        display_map: &DisplaySnapshot,
15893        replace_newest: bool,
15894        autoscroll: Option<Autoscroll>,
15895        window: &mut Window,
15896        cx: &mut Context<Self>,
15897    ) -> Result<()> {
15898        let buffer = display_map.buffer_snapshot();
15899        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15900        if let Some(mut select_next_state) = self.select_next_state.take() {
15901            let query = &select_next_state.query;
15902            if !select_next_state.done {
15903                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15904                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15905                let mut next_selected_range = None;
15906
15907                let bytes_after_last_selection =
15908                    buffer.bytes_in_range(last_selection.end..buffer.len());
15909                let bytes_before_first_selection =
15910                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15911                let query_matches = query
15912                    .stream_find_iter(bytes_after_last_selection)
15913                    .map(|result| (last_selection.end, result))
15914                    .chain(
15915                        query
15916                            .stream_find_iter(bytes_before_first_selection)
15917                            .map(|result| (MultiBufferOffset(0), result)),
15918                    );
15919
15920                for (start_offset, query_match) in query_matches {
15921                    let query_match = query_match.unwrap(); // can only fail due to I/O
15922                    let offset_range =
15923                        start_offset + query_match.start()..start_offset + query_match.end();
15924
15925                    if !select_next_state.wordwise
15926                        || (!buffer.is_inside_word(offset_range.start, None)
15927                            && !buffer.is_inside_word(offset_range.end, None))
15928                    {
15929                        let idx = selections
15930                            .partition_point(|selection| selection.end <= offset_range.start);
15931                        let overlaps = selections
15932                            .get(idx)
15933                            .map_or(false, |selection| selection.start < offset_range.end);
15934
15935                        if !overlaps {
15936                            next_selected_range = Some(offset_range);
15937                            break;
15938                        }
15939                    }
15940                }
15941
15942                if let Some(next_selected_range) = next_selected_range {
15943                    self.select_match_ranges(
15944                        next_selected_range,
15945                        last_selection.reversed,
15946                        replace_newest,
15947                        autoscroll,
15948                        window,
15949                        cx,
15950                    );
15951                } else {
15952                    select_next_state.done = true;
15953                }
15954            }
15955
15956            self.select_next_state = Some(select_next_state);
15957        } else {
15958            let mut only_carets = true;
15959            let mut same_text_selected = true;
15960            let mut selected_text = None;
15961
15962            let mut selections_iter = selections.iter().peekable();
15963            while let Some(selection) = selections_iter.next() {
15964                if selection.start != selection.end {
15965                    only_carets = false;
15966                }
15967
15968                if same_text_selected {
15969                    if selected_text.is_none() {
15970                        selected_text =
15971                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15972                    }
15973
15974                    if let Some(next_selection) = selections_iter.peek() {
15975                        if next_selection.len() == selection.len() {
15976                            let next_selected_text = buffer
15977                                .text_for_range(next_selection.range())
15978                                .collect::<String>();
15979                            if Some(next_selected_text) != selected_text {
15980                                same_text_selected = false;
15981                                selected_text = None;
15982                            }
15983                        } else {
15984                            same_text_selected = false;
15985                            selected_text = None;
15986                        }
15987                    }
15988                }
15989            }
15990
15991            if only_carets {
15992                for selection in &mut selections {
15993                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15994                    selection.start = word_range.start;
15995                    selection.end = word_range.end;
15996                    selection.goal = SelectionGoal::None;
15997                    selection.reversed = false;
15998                    self.select_match_ranges(
15999                        selection.start..selection.end,
16000                        selection.reversed,
16001                        replace_newest,
16002                        autoscroll,
16003                        window,
16004                        cx,
16005                    );
16006                }
16007
16008                if selections.len() == 1 {
16009                    let selection = selections
16010                        .last()
16011                        .expect("ensured that there's only one selection");
16012                    let query = buffer
16013                        .text_for_range(selection.start..selection.end)
16014                        .collect::<String>();
16015                    let is_empty = query.is_empty();
16016                    let select_state = SelectNextState {
16017                        query: self.build_query(&[query], cx)?,
16018                        wordwise: true,
16019                        done: is_empty,
16020                    };
16021                    self.select_next_state = Some(select_state);
16022                } else {
16023                    self.select_next_state = None;
16024                }
16025            } else if let Some(selected_text) = selected_text {
16026                self.select_next_state = Some(SelectNextState {
16027                    query: self.build_query(&[selected_text], cx)?,
16028                    wordwise: false,
16029                    done: false,
16030                });
16031                self.select_next_match_internal(
16032                    display_map,
16033                    replace_newest,
16034                    autoscroll,
16035                    window,
16036                    cx,
16037                )?;
16038            }
16039        }
16040        Ok(())
16041    }
16042
16043    pub fn select_all_matches(
16044        &mut self,
16045        _action: &SelectAllMatches,
16046        window: &mut Window,
16047        cx: &mut Context<Self>,
16048    ) -> Result<()> {
16049        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16050
16051        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16052
16053        self.select_next_match_internal(&display_map, false, None, window, cx)?;
16054        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
16055        else {
16056            return Ok(());
16057        };
16058
16059        let mut new_selections = Vec::new();
16060
16061        let reversed = self
16062            .selections
16063            .oldest::<MultiBufferOffset>(&display_map)
16064            .reversed;
16065        let buffer = display_map.buffer_snapshot();
16066        let query_matches = select_next_state
16067            .query
16068            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
16069
16070        for query_match in query_matches.into_iter() {
16071            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
16072            let offset_range = if reversed {
16073                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
16074            } else {
16075                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
16076            };
16077
16078            if !select_next_state.wordwise
16079                || (!buffer.is_inside_word(offset_range.start, None)
16080                    && !buffer.is_inside_word(offset_range.end, None))
16081            {
16082                new_selections.push(offset_range.start..offset_range.end);
16083            }
16084        }
16085
16086        select_next_state.done = true;
16087
16088        if new_selections.is_empty() {
16089            log::error!("bug: new_selections is empty in select_all_matches");
16090            return Ok(());
16091        }
16092
16093        self.unfold_ranges(&new_selections, false, false, cx);
16094        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16095            selections.select_ranges(new_selections)
16096        });
16097
16098        Ok(())
16099    }
16100
16101    pub fn select_next(
16102        &mut self,
16103        action: &SelectNext,
16104        window: &mut Window,
16105        cx: &mut Context<Self>,
16106    ) -> Result<()> {
16107        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16108        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16109        self.select_next_match_internal(
16110            &display_map,
16111            action.replace_newest,
16112            Some(Autoscroll::newest()),
16113            window,
16114            cx,
16115        )
16116    }
16117
16118    pub fn select_previous(
16119        &mut self,
16120        action: &SelectPrevious,
16121        window: &mut Window,
16122        cx: &mut Context<Self>,
16123    ) -> Result<()> {
16124        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16125        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16126        let buffer = display_map.buffer_snapshot();
16127        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16128        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16129            let query = &select_prev_state.query;
16130            if !select_prev_state.done {
16131                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16132                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16133                let mut next_selected_range = None;
16134                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16135                let bytes_before_last_selection =
16136                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16137                let bytes_after_first_selection =
16138                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16139                let query_matches = query
16140                    .stream_find_iter(bytes_before_last_selection)
16141                    .map(|result| (last_selection.start, result))
16142                    .chain(
16143                        query
16144                            .stream_find_iter(bytes_after_first_selection)
16145                            .map(|result| (buffer.len(), result)),
16146                    );
16147                for (end_offset, query_match) in query_matches {
16148                    let query_match = query_match.unwrap(); // can only fail due to I/O
16149                    let offset_range =
16150                        end_offset - query_match.end()..end_offset - query_match.start();
16151
16152                    if !select_prev_state.wordwise
16153                        || (!buffer.is_inside_word(offset_range.start, None)
16154                            && !buffer.is_inside_word(offset_range.end, None))
16155                    {
16156                        next_selected_range = Some(offset_range);
16157                        break;
16158                    }
16159                }
16160
16161                if let Some(next_selected_range) = next_selected_range {
16162                    self.select_match_ranges(
16163                        next_selected_range,
16164                        last_selection.reversed,
16165                        action.replace_newest,
16166                        Some(Autoscroll::newest()),
16167                        window,
16168                        cx,
16169                    );
16170                } else {
16171                    select_prev_state.done = true;
16172                }
16173            }
16174
16175            self.select_prev_state = Some(select_prev_state);
16176        } else {
16177            let mut only_carets = true;
16178            let mut same_text_selected = true;
16179            let mut selected_text = None;
16180
16181            let mut selections_iter = selections.iter().peekable();
16182            while let Some(selection) = selections_iter.next() {
16183                if selection.start != selection.end {
16184                    only_carets = false;
16185                }
16186
16187                if same_text_selected {
16188                    if selected_text.is_none() {
16189                        selected_text =
16190                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16191                    }
16192
16193                    if let Some(next_selection) = selections_iter.peek() {
16194                        if next_selection.len() == selection.len() {
16195                            let next_selected_text = buffer
16196                                .text_for_range(next_selection.range())
16197                                .collect::<String>();
16198                            if Some(next_selected_text) != selected_text {
16199                                same_text_selected = false;
16200                                selected_text = None;
16201                            }
16202                        } else {
16203                            same_text_selected = false;
16204                            selected_text = None;
16205                        }
16206                    }
16207                }
16208            }
16209
16210            if only_carets {
16211                for selection in &mut selections {
16212                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16213                    selection.start = word_range.start;
16214                    selection.end = word_range.end;
16215                    selection.goal = SelectionGoal::None;
16216                    selection.reversed = false;
16217                    self.select_match_ranges(
16218                        selection.start..selection.end,
16219                        selection.reversed,
16220                        action.replace_newest,
16221                        Some(Autoscroll::newest()),
16222                        window,
16223                        cx,
16224                    );
16225                }
16226                if selections.len() == 1 {
16227                    let selection = selections
16228                        .last()
16229                        .expect("ensured that there's only one selection");
16230                    let query = buffer
16231                        .text_for_range(selection.start..selection.end)
16232                        .collect::<String>();
16233                    let is_empty = query.is_empty();
16234                    let select_state = SelectNextState {
16235                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16236                        wordwise: true,
16237                        done: is_empty,
16238                    };
16239                    self.select_prev_state = Some(select_state);
16240                } else {
16241                    self.select_prev_state = None;
16242                }
16243            } else if let Some(selected_text) = selected_text {
16244                self.select_prev_state = Some(SelectNextState {
16245                    query: self
16246                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16247                    wordwise: false,
16248                    done: false,
16249                });
16250                self.select_previous(action, window, cx)?;
16251            }
16252        }
16253        Ok(())
16254    }
16255
16256    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16257    /// setting the case sensitivity based on the global
16258    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16259    /// editor's settings.
16260    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16261    where
16262        I: IntoIterator<Item = P>,
16263        P: AsRef<[u8]>,
16264    {
16265        let case_sensitive = self
16266            .select_next_is_case_sensitive
16267            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16268
16269        let mut builder = AhoCorasickBuilder::new();
16270        builder.ascii_case_insensitive(!case_sensitive);
16271        builder.build(patterns)
16272    }
16273
16274    pub fn find_next_match(
16275        &mut self,
16276        _: &FindNextMatch,
16277        window: &mut Window,
16278        cx: &mut Context<Self>,
16279    ) -> Result<()> {
16280        let selections = self.selections.disjoint_anchors_arc();
16281        match selections.first() {
16282            Some(first) if selections.len() >= 2 => {
16283                self.change_selections(Default::default(), window, cx, |s| {
16284                    s.select_ranges([first.range()]);
16285                });
16286            }
16287            _ => self.select_next(
16288                &SelectNext {
16289                    replace_newest: true,
16290                },
16291                window,
16292                cx,
16293            )?,
16294        }
16295        Ok(())
16296    }
16297
16298    pub fn find_previous_match(
16299        &mut self,
16300        _: &FindPreviousMatch,
16301        window: &mut Window,
16302        cx: &mut Context<Self>,
16303    ) -> Result<()> {
16304        let selections = self.selections.disjoint_anchors_arc();
16305        match selections.last() {
16306            Some(last) if selections.len() >= 2 => {
16307                self.change_selections(Default::default(), window, cx, |s| {
16308                    s.select_ranges([last.range()]);
16309                });
16310            }
16311            _ => self.select_previous(
16312                &SelectPrevious {
16313                    replace_newest: true,
16314                },
16315                window,
16316                cx,
16317            )?,
16318        }
16319        Ok(())
16320    }
16321
16322    pub fn toggle_comments(
16323        &mut self,
16324        action: &ToggleComments,
16325        window: &mut Window,
16326        cx: &mut Context<Self>,
16327    ) {
16328        if self.read_only(cx) {
16329            return;
16330        }
16331        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16332        let text_layout_details = &self.text_layout_details(window, cx);
16333        self.transact(window, cx, |this, window, cx| {
16334            let mut selections = this
16335                .selections
16336                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16337            let mut edits = Vec::new();
16338            let mut selection_edit_ranges = Vec::new();
16339            let mut last_toggled_row = None;
16340            let snapshot = this.buffer.read(cx).read(cx);
16341            let empty_str: Arc<str> = Arc::default();
16342            let mut suffixes_inserted = Vec::new();
16343            let ignore_indent = action.ignore_indent;
16344
16345            fn comment_prefix_range(
16346                snapshot: &MultiBufferSnapshot,
16347                row: MultiBufferRow,
16348                comment_prefix: &str,
16349                comment_prefix_whitespace: &str,
16350                ignore_indent: bool,
16351            ) -> Range<Point> {
16352                let indent_size = if ignore_indent {
16353                    0
16354                } else {
16355                    snapshot.indent_size_for_line(row).len
16356                };
16357
16358                let start = Point::new(row.0, indent_size);
16359
16360                let mut line_bytes = snapshot
16361                    .bytes_in_range(start..snapshot.max_point())
16362                    .flatten()
16363                    .copied();
16364
16365                // If this line currently begins with the line comment prefix, then record
16366                // the range containing the prefix.
16367                if line_bytes
16368                    .by_ref()
16369                    .take(comment_prefix.len())
16370                    .eq(comment_prefix.bytes())
16371                {
16372                    // Include any whitespace that matches the comment prefix.
16373                    let matching_whitespace_len = line_bytes
16374                        .zip(comment_prefix_whitespace.bytes())
16375                        .take_while(|(a, b)| a == b)
16376                        .count() as u32;
16377                    let end = Point::new(
16378                        start.row,
16379                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16380                    );
16381                    start..end
16382                } else {
16383                    start..start
16384                }
16385            }
16386
16387            fn comment_suffix_range(
16388                snapshot: &MultiBufferSnapshot,
16389                row: MultiBufferRow,
16390                comment_suffix: &str,
16391                comment_suffix_has_leading_space: bool,
16392            ) -> Range<Point> {
16393                let end = Point::new(row.0, snapshot.line_len(row));
16394                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16395
16396                let mut line_end_bytes = snapshot
16397                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16398                    .flatten()
16399                    .copied();
16400
16401                let leading_space_len = if suffix_start_column > 0
16402                    && line_end_bytes.next() == Some(b' ')
16403                    && comment_suffix_has_leading_space
16404                {
16405                    1
16406                } else {
16407                    0
16408                };
16409
16410                // If this line currently begins with the line comment prefix, then record
16411                // the range containing the prefix.
16412                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16413                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16414                    start..end
16415                } else {
16416                    end..end
16417                }
16418            }
16419
16420            // TODO: Handle selections that cross excerpts
16421            for selection in &mut selections {
16422                let start_column = snapshot
16423                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16424                    .len;
16425                let language = if let Some(language) =
16426                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16427                {
16428                    language
16429                } else {
16430                    continue;
16431                };
16432
16433                selection_edit_ranges.clear();
16434
16435                // If multiple selections contain a given row, avoid processing that
16436                // row more than once.
16437                let mut start_row = MultiBufferRow(selection.start.row);
16438                if last_toggled_row == Some(start_row) {
16439                    start_row = start_row.next_row();
16440                }
16441                let end_row =
16442                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16443                        MultiBufferRow(selection.end.row - 1)
16444                    } else {
16445                        MultiBufferRow(selection.end.row)
16446                    };
16447                last_toggled_row = Some(end_row);
16448
16449                if start_row > end_row {
16450                    continue;
16451                }
16452
16453                // If the language has line comments, toggle those.
16454                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16455
16456                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16457                if ignore_indent {
16458                    full_comment_prefixes = full_comment_prefixes
16459                        .into_iter()
16460                        .map(|s| Arc::from(s.trim_end()))
16461                        .collect();
16462                }
16463
16464                if !full_comment_prefixes.is_empty() {
16465                    let first_prefix = full_comment_prefixes
16466                        .first()
16467                        .expect("prefixes is non-empty");
16468                    let prefix_trimmed_lengths = full_comment_prefixes
16469                        .iter()
16470                        .map(|p| p.trim_end_matches(' ').len())
16471                        .collect::<SmallVec<[usize; 4]>>();
16472
16473                    let mut all_selection_lines_are_comments = true;
16474
16475                    for row in start_row.0..=end_row.0 {
16476                        let row = MultiBufferRow(row);
16477                        if start_row < end_row && snapshot.is_line_blank(row) {
16478                            continue;
16479                        }
16480
16481                        let prefix_range = full_comment_prefixes
16482                            .iter()
16483                            .zip(prefix_trimmed_lengths.iter().copied())
16484                            .map(|(prefix, trimmed_prefix_len)| {
16485                                comment_prefix_range(
16486                                    snapshot.deref(),
16487                                    row,
16488                                    &prefix[..trimmed_prefix_len],
16489                                    &prefix[trimmed_prefix_len..],
16490                                    ignore_indent,
16491                                )
16492                            })
16493                            .max_by_key(|range| range.end.column - range.start.column)
16494                            .expect("prefixes is non-empty");
16495
16496                        if prefix_range.is_empty() {
16497                            all_selection_lines_are_comments = false;
16498                        }
16499
16500                        selection_edit_ranges.push(prefix_range);
16501                    }
16502
16503                    if all_selection_lines_are_comments {
16504                        edits.extend(
16505                            selection_edit_ranges
16506                                .iter()
16507                                .cloned()
16508                                .map(|range| (range, empty_str.clone())),
16509                        );
16510                    } else {
16511                        let min_column = selection_edit_ranges
16512                            .iter()
16513                            .map(|range| range.start.column)
16514                            .min()
16515                            .unwrap_or(0);
16516                        edits.extend(selection_edit_ranges.iter().map(|range| {
16517                            let position = Point::new(range.start.row, min_column);
16518                            (position..position, first_prefix.clone())
16519                        }));
16520                    }
16521                } else if let Some(BlockCommentConfig {
16522                    start: full_comment_prefix,
16523                    end: comment_suffix,
16524                    ..
16525                }) = language.block_comment()
16526                {
16527                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16528                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16529                    let prefix_range = comment_prefix_range(
16530                        snapshot.deref(),
16531                        start_row,
16532                        comment_prefix,
16533                        comment_prefix_whitespace,
16534                        ignore_indent,
16535                    );
16536                    let suffix_range = comment_suffix_range(
16537                        snapshot.deref(),
16538                        end_row,
16539                        comment_suffix.trim_start_matches(' '),
16540                        comment_suffix.starts_with(' '),
16541                    );
16542
16543                    if prefix_range.is_empty() || suffix_range.is_empty() {
16544                        edits.push((
16545                            prefix_range.start..prefix_range.start,
16546                            full_comment_prefix.clone(),
16547                        ));
16548                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16549                        suffixes_inserted.push((end_row, comment_suffix.len()));
16550                    } else {
16551                        edits.push((prefix_range, empty_str.clone()));
16552                        edits.push((suffix_range, empty_str.clone()));
16553                    }
16554                } else {
16555                    continue;
16556                }
16557            }
16558
16559            drop(snapshot);
16560            this.buffer.update(cx, |buffer, cx| {
16561                buffer.edit(edits, None, cx);
16562            });
16563
16564            // Adjust selections so that they end before any comment suffixes that
16565            // were inserted.
16566            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16567            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16568            let snapshot = this.buffer.read(cx).read(cx);
16569            for selection in &mut selections {
16570                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16571                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16572                        Ordering::Less => {
16573                            suffixes_inserted.next();
16574                            continue;
16575                        }
16576                        Ordering::Greater => break,
16577                        Ordering::Equal => {
16578                            if selection.end.column == snapshot.line_len(row) {
16579                                if selection.is_empty() {
16580                                    selection.start.column -= suffix_len as u32;
16581                                }
16582                                selection.end.column -= suffix_len as u32;
16583                            }
16584                            break;
16585                        }
16586                    }
16587                }
16588            }
16589
16590            drop(snapshot);
16591            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16592
16593            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16594            let selections_on_single_row = selections.windows(2).all(|selections| {
16595                selections[0].start.row == selections[1].start.row
16596                    && selections[0].end.row == selections[1].end.row
16597                    && selections[0].start.row == selections[0].end.row
16598            });
16599            let selections_selecting = selections
16600                .iter()
16601                .any(|selection| selection.start != selection.end);
16602            let advance_downwards = action.advance_downwards
16603                && selections_on_single_row
16604                && !selections_selecting
16605                && !matches!(this.mode, EditorMode::SingleLine);
16606
16607            if advance_downwards {
16608                let snapshot = this.buffer.read(cx).snapshot(cx);
16609
16610                this.change_selections(Default::default(), window, cx, |s| {
16611                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16612                        let mut point = display_point.to_point(display_snapshot);
16613                        point.row += 1;
16614                        point = snapshot.clip_point(point, Bias::Left);
16615                        let display_point = point.to_display_point(display_snapshot);
16616                        let goal = SelectionGoal::HorizontalPosition(
16617                            display_snapshot
16618                                .x_for_display_point(display_point, text_layout_details)
16619                                .into(),
16620                        );
16621                        (display_point, goal)
16622                    })
16623                });
16624            }
16625        });
16626    }
16627
16628    pub fn select_enclosing_symbol(
16629        &mut self,
16630        _: &SelectEnclosingSymbol,
16631        window: &mut Window,
16632        cx: &mut Context<Self>,
16633    ) {
16634        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16635
16636        let buffer = self.buffer.read(cx).snapshot(cx);
16637        let old_selections = self
16638            .selections
16639            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16640            .into_boxed_slice();
16641
16642        fn update_selection(
16643            selection: &Selection<MultiBufferOffset>,
16644            buffer_snap: &MultiBufferSnapshot,
16645        ) -> Option<Selection<MultiBufferOffset>> {
16646            let cursor = selection.head();
16647            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16648            for symbol in symbols.iter().rev() {
16649                let start = symbol.range.start.to_offset(buffer_snap);
16650                let end = symbol.range.end.to_offset(buffer_snap);
16651                let new_range = start..end;
16652                if start < selection.start || end > selection.end {
16653                    return Some(Selection {
16654                        id: selection.id,
16655                        start: new_range.start,
16656                        end: new_range.end,
16657                        goal: SelectionGoal::None,
16658                        reversed: selection.reversed,
16659                    });
16660                }
16661            }
16662            None
16663        }
16664
16665        let mut selected_larger_symbol = false;
16666        let new_selections = old_selections
16667            .iter()
16668            .map(|selection| match update_selection(selection, &buffer) {
16669                Some(new_selection) => {
16670                    if new_selection.range() != selection.range() {
16671                        selected_larger_symbol = true;
16672                    }
16673                    new_selection
16674                }
16675                None => selection.clone(),
16676            })
16677            .collect::<Vec<_>>();
16678
16679        if selected_larger_symbol {
16680            self.change_selections(Default::default(), window, cx, |s| {
16681                s.select(new_selections);
16682            });
16683        }
16684    }
16685
16686    pub fn select_larger_syntax_node(
16687        &mut self,
16688        _: &SelectLargerSyntaxNode,
16689        window: &mut Window,
16690        cx: &mut Context<Self>,
16691    ) {
16692        let Some(visible_row_count) = self.visible_row_count() else {
16693            return;
16694        };
16695        let old_selections: Box<[_]> = self
16696            .selections
16697            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16698            .into();
16699        if old_selections.is_empty() {
16700            return;
16701        }
16702
16703        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16704
16705        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16706        let buffer = self.buffer.read(cx).snapshot(cx);
16707
16708        let mut selected_larger_node = false;
16709        let mut new_selections = old_selections
16710            .iter()
16711            .map(|selection| {
16712                let old_range = selection.start..selection.end;
16713
16714                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16715                    // manually select word at selection
16716                    if ["string_content", "inline"].contains(&node.kind()) {
16717                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16718                        // ignore if word is already selected
16719                        if !word_range.is_empty() && old_range != word_range {
16720                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16721                            // only select word if start and end point belongs to same word
16722                            if word_range == last_word_range {
16723                                selected_larger_node = true;
16724                                return Selection {
16725                                    id: selection.id,
16726                                    start: word_range.start,
16727                                    end: word_range.end,
16728                                    goal: SelectionGoal::None,
16729                                    reversed: selection.reversed,
16730                                };
16731                            }
16732                        }
16733                    }
16734                }
16735
16736                let mut new_range = old_range.clone();
16737                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16738                    new_range = range;
16739                    if !node.is_named() {
16740                        continue;
16741                    }
16742                    if !display_map.intersects_fold(new_range.start)
16743                        && !display_map.intersects_fold(new_range.end)
16744                    {
16745                        break;
16746                    }
16747                }
16748
16749                selected_larger_node |= new_range != old_range;
16750                Selection {
16751                    id: selection.id,
16752                    start: new_range.start,
16753                    end: new_range.end,
16754                    goal: SelectionGoal::None,
16755                    reversed: selection.reversed,
16756                }
16757            })
16758            .collect::<Vec<_>>();
16759
16760        if !selected_larger_node {
16761            return; // don't put this call in the history
16762        }
16763
16764        // scroll based on transformation done to the last selection created by the user
16765        let (last_old, last_new) = old_selections
16766            .last()
16767            .zip(new_selections.last().cloned())
16768            .expect("old_selections isn't empty");
16769
16770        let is_selection_reversed = if new_selections.len() == 1 {
16771            let should_be_reversed = last_old.start != last_new.start;
16772            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16773            should_be_reversed
16774        } else {
16775            last_new.reversed
16776        };
16777
16778        if selected_larger_node {
16779            self.select_syntax_node_history.disable_clearing = true;
16780            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16781                s.select(new_selections.clone());
16782            });
16783            self.select_syntax_node_history.disable_clearing = false;
16784        }
16785
16786        let start_row = last_new.start.to_display_point(&display_map).row().0;
16787        let end_row = last_new.end.to_display_point(&display_map).row().0;
16788        let selection_height = end_row - start_row + 1;
16789        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16790
16791        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16792        let scroll_behavior = if fits_on_the_screen {
16793            self.request_autoscroll(Autoscroll::fit(), cx);
16794            SelectSyntaxNodeScrollBehavior::FitSelection
16795        } else if is_selection_reversed {
16796            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16797            SelectSyntaxNodeScrollBehavior::CursorTop
16798        } else {
16799            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16800            SelectSyntaxNodeScrollBehavior::CursorBottom
16801        };
16802
16803        let old_selections: Box<[Selection<Anchor>]> = old_selections
16804            .iter()
16805            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16806            .collect();
16807        self.select_syntax_node_history.push((
16808            old_selections,
16809            scroll_behavior,
16810            is_selection_reversed,
16811        ));
16812    }
16813
16814    pub fn select_smaller_syntax_node(
16815        &mut self,
16816        _: &SelectSmallerSyntaxNode,
16817        window: &mut Window,
16818        cx: &mut Context<Self>,
16819    ) {
16820        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16821
16822        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16823            self.select_syntax_node_history.pop()
16824        {
16825            if let Some(selection) = selections.last_mut() {
16826                selection.reversed = is_selection_reversed;
16827            }
16828
16829            let snapshot = self.buffer.read(cx).snapshot(cx);
16830            let selections: Vec<Selection<MultiBufferOffset>> = selections
16831                .iter()
16832                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16833                .collect();
16834
16835            self.select_syntax_node_history.disable_clearing = true;
16836            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16837                s.select(selections);
16838            });
16839            self.select_syntax_node_history.disable_clearing = false;
16840
16841            match scroll_behavior {
16842                SelectSyntaxNodeScrollBehavior::CursorTop => {
16843                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16844                }
16845                SelectSyntaxNodeScrollBehavior::FitSelection => {
16846                    self.request_autoscroll(Autoscroll::fit(), cx);
16847                }
16848                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16849                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16850                }
16851            }
16852        }
16853    }
16854
16855    pub fn unwrap_syntax_node(
16856        &mut self,
16857        _: &UnwrapSyntaxNode,
16858        window: &mut Window,
16859        cx: &mut Context<Self>,
16860    ) {
16861        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16862
16863        let buffer = self.buffer.read(cx).snapshot(cx);
16864        let selections = self
16865            .selections
16866            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16867            .into_iter()
16868            // subtracting the offset requires sorting
16869            .sorted_by_key(|i| i.start);
16870
16871        let full_edits = selections
16872            .into_iter()
16873            .filter_map(|selection| {
16874                let child = if selection.is_empty()
16875                    && let Some((_, ancestor_range)) =
16876                        buffer.syntax_ancestor(selection.start..selection.end)
16877                {
16878                    ancestor_range
16879                } else {
16880                    selection.range()
16881                };
16882
16883                let mut parent = child.clone();
16884                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16885                    parent = ancestor_range;
16886                    if parent.start < child.start || parent.end > child.end {
16887                        break;
16888                    }
16889                }
16890
16891                if parent == child {
16892                    return None;
16893                }
16894                let text = buffer.text_for_range(child).collect::<String>();
16895                Some((selection.id, parent, text))
16896            })
16897            .collect::<Vec<_>>();
16898        if full_edits.is_empty() {
16899            return;
16900        }
16901
16902        self.transact(window, cx, |this, window, cx| {
16903            this.buffer.update(cx, |buffer, cx| {
16904                buffer.edit(
16905                    full_edits
16906                        .iter()
16907                        .map(|(_, p, t)| (p.clone(), t.clone()))
16908                        .collect::<Vec<_>>(),
16909                    None,
16910                    cx,
16911                );
16912            });
16913            this.change_selections(Default::default(), window, cx, |s| {
16914                let mut offset = 0;
16915                let mut selections = vec![];
16916                for (id, parent, text) in full_edits {
16917                    let start = parent.start - offset;
16918                    offset += (parent.end - parent.start) - text.len();
16919                    selections.push(Selection {
16920                        id,
16921                        start,
16922                        end: start + text.len(),
16923                        reversed: false,
16924                        goal: Default::default(),
16925                    });
16926                }
16927                s.select(selections);
16928            });
16929        });
16930    }
16931
16932    pub fn select_next_syntax_node(
16933        &mut self,
16934        _: &SelectNextSyntaxNode,
16935        window: &mut Window,
16936        cx: &mut Context<Self>,
16937    ) {
16938        let old_selections: Box<[_]> = self
16939            .selections
16940            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16941            .into();
16942        if old_selections.is_empty() {
16943            return;
16944        }
16945
16946        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16947
16948        let buffer = self.buffer.read(cx).snapshot(cx);
16949        let mut selected_sibling = false;
16950
16951        let new_selections = old_selections
16952            .iter()
16953            .map(|selection| {
16954                let old_range = selection.start..selection.end;
16955
16956                let old_range =
16957                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16958                let excerpt = buffer.excerpt_containing(old_range.clone());
16959
16960                if let Some(mut excerpt) = excerpt
16961                    && let Some(node) = excerpt
16962                        .buffer()
16963                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16964                {
16965                    let new_range = excerpt.map_range_from_buffer(
16966                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16967                    );
16968                    selected_sibling = true;
16969                    Selection {
16970                        id: selection.id,
16971                        start: new_range.start,
16972                        end: new_range.end,
16973                        goal: SelectionGoal::None,
16974                        reversed: selection.reversed,
16975                    }
16976                } else {
16977                    selection.clone()
16978                }
16979            })
16980            .collect::<Vec<_>>();
16981
16982        if selected_sibling {
16983            self.change_selections(
16984                SelectionEffects::scroll(Autoscroll::fit()),
16985                window,
16986                cx,
16987                |s| {
16988                    s.select(new_selections);
16989                },
16990            );
16991        }
16992    }
16993
16994    pub fn select_prev_syntax_node(
16995        &mut self,
16996        _: &SelectPreviousSyntaxNode,
16997        window: &mut Window,
16998        cx: &mut Context<Self>,
16999    ) {
17000        let old_selections: Box<[_]> = self
17001            .selections
17002            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17003            .into();
17004        if old_selections.is_empty() {
17005            return;
17006        }
17007
17008        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17009
17010        let buffer = self.buffer.read(cx).snapshot(cx);
17011        let mut selected_sibling = false;
17012
17013        let new_selections = old_selections
17014            .iter()
17015            .map(|selection| {
17016                let old_range = selection.start..selection.end;
17017                let old_range =
17018                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17019                let excerpt = buffer.excerpt_containing(old_range.clone());
17020
17021                if let Some(mut excerpt) = excerpt
17022                    && let Some(node) = excerpt
17023                        .buffer()
17024                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
17025                {
17026                    let new_range = excerpt.map_range_from_buffer(
17027                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17028                    );
17029                    selected_sibling = true;
17030                    Selection {
17031                        id: selection.id,
17032                        start: new_range.start,
17033                        end: new_range.end,
17034                        goal: SelectionGoal::None,
17035                        reversed: selection.reversed,
17036                    }
17037                } else {
17038                    selection.clone()
17039                }
17040            })
17041            .collect::<Vec<_>>();
17042
17043        if selected_sibling {
17044            self.change_selections(
17045                SelectionEffects::scroll(Autoscroll::fit()),
17046                window,
17047                cx,
17048                |s| {
17049                    s.select(new_selections);
17050                },
17051            );
17052        }
17053    }
17054
17055    pub fn move_to_start_of_larger_syntax_node(
17056        &mut self,
17057        _: &MoveToStartOfLargerSyntaxNode,
17058        window: &mut Window,
17059        cx: &mut Context<Self>,
17060    ) {
17061        self.move_cursors_to_syntax_nodes(window, cx, false);
17062    }
17063
17064    pub fn move_to_end_of_larger_syntax_node(
17065        &mut self,
17066        _: &MoveToEndOfLargerSyntaxNode,
17067        window: &mut Window,
17068        cx: &mut Context<Self>,
17069    ) {
17070        self.move_cursors_to_syntax_nodes(window, cx, true);
17071    }
17072
17073    fn find_syntax_node_boundary(
17074        &self,
17075        selection_pos: MultiBufferOffset,
17076        move_to_end: bool,
17077        display_map: &DisplaySnapshot,
17078        buffer: &MultiBufferSnapshot,
17079    ) -> MultiBufferOffset {
17080        let old_range = selection_pos..selection_pos;
17081        let mut new_pos = selection_pos;
17082        let mut search_range = old_range;
17083        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17084            search_range = range.clone();
17085            if !node.is_named()
17086                || display_map.intersects_fold(range.start)
17087                || display_map.intersects_fold(range.end)
17088                // If cursor is already at the end of the syntax node, continue searching
17089                || (move_to_end && range.end == selection_pos)
17090                // If cursor is already at the start of the syntax node, continue searching
17091                || (!move_to_end && range.start == selection_pos)
17092            {
17093                continue;
17094            }
17095
17096            // If we found a string_content node, find the largest parent that is still string_content
17097            // Enables us to skip to the end of strings without taking multiple steps inside the string
17098            let (_, final_range) = if node.kind() == "string_content" {
17099                let mut current_node = node;
17100                let mut current_range = range;
17101                while let Some((parent, parent_range)) =
17102                    buffer.syntax_ancestor(current_range.clone())
17103                {
17104                    if parent.kind() == "string_content" {
17105                        current_node = parent;
17106                        current_range = parent_range;
17107                    } else {
17108                        break;
17109                    }
17110                }
17111
17112                (current_node, current_range)
17113            } else {
17114                (node, range)
17115            };
17116
17117            new_pos = if move_to_end {
17118                final_range.end
17119            } else {
17120                final_range.start
17121            };
17122
17123            break;
17124        }
17125
17126        new_pos
17127    }
17128
17129    fn move_cursors_to_syntax_nodes(
17130        &mut self,
17131        window: &mut Window,
17132        cx: &mut Context<Self>,
17133        move_to_end: bool,
17134    ) -> bool {
17135        let old_selections: Box<[_]> = self
17136            .selections
17137            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17138            .into();
17139        if old_selections.is_empty() {
17140            return false;
17141        }
17142
17143        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17144
17145        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17146        let buffer = self.buffer.read(cx).snapshot(cx);
17147
17148        let mut any_cursor_moved = false;
17149        let new_selections = old_selections
17150            .iter()
17151            .map(|selection| {
17152                if !selection.is_empty() {
17153                    return selection.clone();
17154                }
17155
17156                let selection_pos = selection.head();
17157                let new_pos = self.find_syntax_node_boundary(
17158                    selection_pos,
17159                    move_to_end,
17160                    &display_map,
17161                    &buffer,
17162                );
17163
17164                any_cursor_moved |= new_pos != selection_pos;
17165
17166                Selection {
17167                    id: selection.id,
17168                    start: new_pos,
17169                    end: new_pos,
17170                    goal: SelectionGoal::None,
17171                    reversed: false,
17172                }
17173            })
17174            .collect::<Vec<_>>();
17175
17176        self.change_selections(Default::default(), window, cx, |s| {
17177            s.select(new_selections);
17178        });
17179        self.request_autoscroll(Autoscroll::newest(), cx);
17180
17181        any_cursor_moved
17182    }
17183
17184    pub fn select_to_start_of_larger_syntax_node(
17185        &mut self,
17186        _: &SelectToStartOfLargerSyntaxNode,
17187        window: &mut Window,
17188        cx: &mut Context<Self>,
17189    ) {
17190        self.select_to_syntax_nodes(window, cx, false);
17191    }
17192
17193    pub fn select_to_end_of_larger_syntax_node(
17194        &mut self,
17195        _: &SelectToEndOfLargerSyntaxNode,
17196        window: &mut Window,
17197        cx: &mut Context<Self>,
17198    ) {
17199        self.select_to_syntax_nodes(window, cx, true);
17200    }
17201
17202    fn select_to_syntax_nodes(
17203        &mut self,
17204        window: &mut Window,
17205        cx: &mut Context<Self>,
17206        move_to_end: bool,
17207    ) {
17208        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17209
17210        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17211        let buffer = self.buffer.read(cx).snapshot(cx);
17212        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17213
17214        let new_selections = old_selections
17215            .iter()
17216            .map(|selection| {
17217                let new_pos = self.find_syntax_node_boundary(
17218                    selection.head(),
17219                    move_to_end,
17220                    &display_map,
17221                    &buffer,
17222                );
17223
17224                let mut new_selection = selection.clone();
17225                new_selection.set_head(new_pos, SelectionGoal::None);
17226                new_selection
17227            })
17228            .collect::<Vec<_>>();
17229
17230        self.change_selections(Default::default(), window, cx, |s| {
17231            s.select(new_selections);
17232        });
17233    }
17234
17235    pub fn move_to_enclosing_bracket(
17236        &mut self,
17237        _: &MoveToEnclosingBracket,
17238        window: &mut Window,
17239        cx: &mut Context<Self>,
17240    ) {
17241        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17242        self.change_selections(Default::default(), window, cx, |s| {
17243            s.move_offsets_with(&mut |snapshot, selection| {
17244                let Some(enclosing_bracket_ranges) =
17245                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17246                else {
17247                    return;
17248                };
17249
17250                let mut best_length = usize::MAX;
17251                let mut best_inside = false;
17252                let mut best_in_bracket_range = false;
17253                let mut best_destination = None;
17254                for (open, close) in enclosing_bracket_ranges {
17255                    let close = close.to_inclusive();
17256                    let length = *close.end() - open.start;
17257                    let inside = selection.start >= open.end && selection.end <= *close.start();
17258                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17259                        || close.contains(&selection.head());
17260
17261                    // If best is next to a bracket and current isn't, skip
17262                    if !in_bracket_range && best_in_bracket_range {
17263                        continue;
17264                    }
17265
17266                    // Prefer smaller lengths unless best is inside and current isn't
17267                    if length > best_length && (best_inside || !inside) {
17268                        continue;
17269                    }
17270
17271                    best_length = length;
17272                    best_inside = inside;
17273                    best_in_bracket_range = in_bracket_range;
17274                    best_destination = Some(
17275                        if close.contains(&selection.start) && close.contains(&selection.end) {
17276                            if inside { open.end } else { open.start }
17277                        } else if inside {
17278                            *close.start()
17279                        } else {
17280                            *close.end()
17281                        },
17282                    );
17283                }
17284
17285                if let Some(destination) = best_destination {
17286                    selection.collapse_to(destination, SelectionGoal::None);
17287                }
17288            })
17289        });
17290    }
17291
17292    pub fn undo_selection(
17293        &mut self,
17294        _: &UndoSelection,
17295        window: &mut Window,
17296        cx: &mut Context<Self>,
17297    ) {
17298        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17299        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17300            self.selection_history.mode = SelectionHistoryMode::Undoing;
17301            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17302                this.end_selection(window, cx);
17303                this.change_selections(
17304                    SelectionEffects::scroll(Autoscroll::newest()),
17305                    window,
17306                    cx,
17307                    |s| s.select_anchors(entry.selections.to_vec()),
17308                );
17309            });
17310            self.selection_history.mode = SelectionHistoryMode::Normal;
17311
17312            self.select_next_state = entry.select_next_state;
17313            self.select_prev_state = entry.select_prev_state;
17314            self.add_selections_state = entry.add_selections_state;
17315        }
17316    }
17317
17318    pub fn redo_selection(
17319        &mut self,
17320        _: &RedoSelection,
17321        window: &mut Window,
17322        cx: &mut Context<Self>,
17323    ) {
17324        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17325        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17326            self.selection_history.mode = SelectionHistoryMode::Redoing;
17327            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17328                this.end_selection(window, cx);
17329                this.change_selections(
17330                    SelectionEffects::scroll(Autoscroll::newest()),
17331                    window,
17332                    cx,
17333                    |s| s.select_anchors(entry.selections.to_vec()),
17334                );
17335            });
17336            self.selection_history.mode = SelectionHistoryMode::Normal;
17337
17338            self.select_next_state = entry.select_next_state;
17339            self.select_prev_state = entry.select_prev_state;
17340            self.add_selections_state = entry.add_selections_state;
17341        }
17342    }
17343
17344    pub fn expand_excerpts(
17345        &mut self,
17346        action: &ExpandExcerpts,
17347        _: &mut Window,
17348        cx: &mut Context<Self>,
17349    ) {
17350        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17351    }
17352
17353    pub fn expand_excerpts_down(
17354        &mut self,
17355        action: &ExpandExcerptsDown,
17356        _: &mut Window,
17357        cx: &mut Context<Self>,
17358    ) {
17359        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17360    }
17361
17362    pub fn expand_excerpts_up(
17363        &mut self,
17364        action: &ExpandExcerptsUp,
17365        _: &mut Window,
17366        cx: &mut Context<Self>,
17367    ) {
17368        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17369    }
17370
17371    pub fn expand_excerpts_for_direction(
17372        &mut self,
17373        lines: u32,
17374        direction: ExpandExcerptDirection,
17375        cx: &mut Context<Self>,
17376    ) {
17377        let selections = self.selections.disjoint_anchors_arc();
17378
17379        let lines = if lines == 0 {
17380            EditorSettings::get_global(cx).expand_excerpt_lines
17381        } else {
17382            lines
17383        };
17384
17385        let snapshot = self.buffer.read(cx).snapshot(cx);
17386        let excerpt_ids = selections
17387            .iter()
17388            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17389            .unique()
17390            .sorted()
17391            .collect::<Vec<_>>();
17392
17393        if self.delegate_expand_excerpts {
17394            cx.emit(EditorEvent::ExpandExcerptsRequested {
17395                excerpt_ids,
17396                lines,
17397                direction,
17398            });
17399            return;
17400        }
17401
17402        self.buffer.update(cx, |buffer, cx| {
17403            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17404        })
17405    }
17406
17407    pub fn expand_excerpt(
17408        &mut self,
17409        excerpt: ExcerptId,
17410        direction: ExpandExcerptDirection,
17411        window: &mut Window,
17412        cx: &mut Context<Self>,
17413    ) {
17414        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17415
17416        if self.delegate_expand_excerpts {
17417            cx.emit(EditorEvent::ExpandExcerptsRequested {
17418                excerpt_ids: vec![excerpt],
17419                lines: lines_to_expand,
17420                direction,
17421            });
17422            return;
17423        }
17424
17425        let current_scroll_position = self.scroll_position(cx);
17426        let mut scroll = None;
17427
17428        if direction == ExpandExcerptDirection::Down {
17429            let multi_buffer = self.buffer.read(cx);
17430            let snapshot = multi_buffer.snapshot(cx);
17431            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17432                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17433                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17434            {
17435                let buffer_snapshot = buffer.read(cx).snapshot();
17436                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17437                let last_row = buffer_snapshot.max_point().row;
17438                let lines_below = last_row.saturating_sub(excerpt_end_row);
17439                if lines_below >= lines_to_expand {
17440                    scroll = Some(
17441                        current_scroll_position
17442                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17443                    );
17444                }
17445            }
17446        }
17447        if direction == ExpandExcerptDirection::Up
17448            && self
17449                .buffer
17450                .read(cx)
17451                .snapshot(cx)
17452                .excerpt_before(excerpt)
17453                .is_none()
17454        {
17455            scroll = Some(current_scroll_position);
17456        }
17457
17458        self.buffer.update(cx, |buffer, cx| {
17459            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17460        });
17461
17462        if let Some(new_scroll_position) = scroll {
17463            self.set_scroll_position(new_scroll_position, window, cx);
17464        }
17465    }
17466
17467    pub fn go_to_singleton_buffer_point(
17468        &mut self,
17469        point: Point,
17470        window: &mut Window,
17471        cx: &mut Context<Self>,
17472    ) {
17473        self.go_to_singleton_buffer_range(point..point, window, cx);
17474    }
17475
17476    pub fn go_to_singleton_buffer_range(
17477        &mut self,
17478        range: Range<Point>,
17479        window: &mut Window,
17480        cx: &mut Context<Self>,
17481    ) {
17482        let multibuffer = self.buffer().read(cx);
17483        let Some(buffer) = multibuffer.as_singleton() else {
17484            return;
17485        };
17486        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17487            return;
17488        };
17489        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17490            return;
17491        };
17492        self.change_selections(
17493            SelectionEffects::default().nav_history(true),
17494            window,
17495            cx,
17496            |s| s.select_anchor_ranges([start..end]),
17497        );
17498    }
17499
17500    pub fn go_to_diagnostic(
17501        &mut self,
17502        action: &GoToDiagnostic,
17503        window: &mut Window,
17504        cx: &mut Context<Self>,
17505    ) {
17506        if !self.diagnostics_enabled() {
17507            return;
17508        }
17509        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17510        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17511    }
17512
17513    pub fn go_to_prev_diagnostic(
17514        &mut self,
17515        action: &GoToPreviousDiagnostic,
17516        window: &mut Window,
17517        cx: &mut Context<Self>,
17518    ) {
17519        if !self.diagnostics_enabled() {
17520            return;
17521        }
17522        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17523        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17524    }
17525
17526    pub fn go_to_diagnostic_impl(
17527        &mut self,
17528        direction: Direction,
17529        severity: GoToDiagnosticSeverityFilter,
17530        window: &mut Window,
17531        cx: &mut Context<Self>,
17532    ) {
17533        let buffer = self.buffer.read(cx).snapshot(cx);
17534        let selection = self
17535            .selections
17536            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17537
17538        let mut active_group_id = None;
17539        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17540            && active_group.active_range.start.to_offset(&buffer) == selection.start
17541        {
17542            active_group_id = Some(active_group.group_id);
17543        }
17544
17545        fn filtered<'a>(
17546            severity: GoToDiagnosticSeverityFilter,
17547            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17548        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17549            diagnostics
17550                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17551                .filter(|entry| entry.range.start != entry.range.end)
17552                .filter(|entry| !entry.diagnostic.is_unnecessary)
17553        }
17554
17555        let before = filtered(
17556            severity,
17557            buffer
17558                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17559                .filter(|entry| entry.range.start <= selection.start),
17560        );
17561        let after = filtered(
17562            severity,
17563            buffer
17564                .diagnostics_in_range(selection.start..buffer.len())
17565                .filter(|entry| entry.range.start >= selection.start),
17566        );
17567
17568        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17569        if direction == Direction::Prev {
17570            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17571            {
17572                for diagnostic in prev_diagnostics.into_iter().rev() {
17573                    if diagnostic.range.start != selection.start
17574                        || active_group_id
17575                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17576                    {
17577                        found = Some(diagnostic);
17578                        break 'outer;
17579                    }
17580                }
17581            }
17582        } else {
17583            for diagnostic in after.chain(before) {
17584                if diagnostic.range.start != selection.start
17585                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17586                {
17587                    found = Some(diagnostic);
17588                    break;
17589                }
17590            }
17591        }
17592        let Some(next_diagnostic) = found else {
17593            return;
17594        };
17595
17596        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17597        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17598            return;
17599        };
17600        let snapshot = self.snapshot(window, cx);
17601        if snapshot.intersects_fold(next_diagnostic.range.start) {
17602            self.unfold_ranges(
17603                std::slice::from_ref(&next_diagnostic.range),
17604                true,
17605                false,
17606                cx,
17607            );
17608        }
17609        self.change_selections(Default::default(), window, cx, |s| {
17610            s.select_ranges(vec![
17611                next_diagnostic.range.start..next_diagnostic.range.start,
17612            ])
17613        });
17614        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17615        self.refresh_edit_prediction(false, true, window, cx);
17616    }
17617
17618    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17619        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17620        let snapshot = self.snapshot(window, cx);
17621        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17622        self.go_to_hunk_before_or_after_position(
17623            &snapshot,
17624            selection.head(),
17625            Direction::Next,
17626            true,
17627            window,
17628            cx,
17629        );
17630    }
17631
17632    pub fn go_to_hunk_before_or_after_position(
17633        &mut self,
17634        snapshot: &EditorSnapshot,
17635        position: Point,
17636        direction: Direction,
17637        wrap_around: bool,
17638        window: &mut Window,
17639        cx: &mut Context<Editor>,
17640    ) {
17641        let row = if direction == Direction::Next {
17642            self.hunk_after_position(snapshot, position, wrap_around)
17643                .map(|hunk| hunk.row_range.start)
17644        } else {
17645            self.hunk_before_position(snapshot, position, wrap_around)
17646        };
17647
17648        if let Some(row) = row {
17649            let destination = Point::new(row.0, 0);
17650            let autoscroll = Autoscroll::center();
17651
17652            self.unfold_ranges(&[destination..destination], false, false, cx);
17653            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17654                s.select_ranges([destination..destination]);
17655            });
17656        }
17657    }
17658
17659    fn hunk_after_position(
17660        &mut self,
17661        snapshot: &EditorSnapshot,
17662        position: Point,
17663        wrap_around: bool,
17664    ) -> Option<MultiBufferDiffHunk> {
17665        let result = snapshot
17666            .buffer_snapshot()
17667            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17668            .find(|hunk| hunk.row_range.start.0 > position.row);
17669
17670        if wrap_around {
17671            result.or_else(|| {
17672                snapshot
17673                    .buffer_snapshot()
17674                    .diff_hunks_in_range(Point::zero()..position)
17675                    .find(|hunk| hunk.row_range.end.0 < position.row)
17676            })
17677        } else {
17678            result
17679        }
17680    }
17681
17682    fn go_to_prev_hunk(
17683        &mut self,
17684        _: &GoToPreviousHunk,
17685        window: &mut Window,
17686        cx: &mut Context<Self>,
17687    ) {
17688        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17689        let snapshot = self.snapshot(window, cx);
17690        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17691        self.go_to_hunk_before_or_after_position(
17692            &snapshot,
17693            selection.head(),
17694            Direction::Prev,
17695            true,
17696            window,
17697            cx,
17698        );
17699    }
17700
17701    fn hunk_before_position(
17702        &mut self,
17703        snapshot: &EditorSnapshot,
17704        position: Point,
17705        wrap_around: bool,
17706    ) -> Option<MultiBufferRow> {
17707        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17708
17709        if wrap_around {
17710            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17711        } else {
17712            result
17713        }
17714    }
17715
17716    fn go_to_next_change(
17717        &mut self,
17718        _: &GoToNextChange,
17719        window: &mut Window,
17720        cx: &mut Context<Self>,
17721    ) {
17722        if let Some(selections) = self
17723            .change_list
17724            .next_change(1, Direction::Next)
17725            .map(|s| s.to_vec())
17726        {
17727            self.change_selections(Default::default(), window, cx, |s| {
17728                let map = s.display_snapshot();
17729                s.select_display_ranges(selections.iter().map(|a| {
17730                    let point = a.to_display_point(&map);
17731                    point..point
17732                }))
17733            })
17734        }
17735    }
17736
17737    fn go_to_previous_change(
17738        &mut self,
17739        _: &GoToPreviousChange,
17740        window: &mut Window,
17741        cx: &mut Context<Self>,
17742    ) {
17743        if let Some(selections) = self
17744            .change_list
17745            .next_change(1, Direction::Prev)
17746            .map(|s| s.to_vec())
17747        {
17748            self.change_selections(Default::default(), window, cx, |s| {
17749                let map = s.display_snapshot();
17750                s.select_display_ranges(selections.iter().map(|a| {
17751                    let point = a.to_display_point(&map);
17752                    point..point
17753                }))
17754            })
17755        }
17756    }
17757
17758    pub fn go_to_next_document_highlight(
17759        &mut self,
17760        _: &GoToNextDocumentHighlight,
17761        window: &mut Window,
17762        cx: &mut Context<Self>,
17763    ) {
17764        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17765    }
17766
17767    pub fn go_to_prev_document_highlight(
17768        &mut self,
17769        _: &GoToPreviousDocumentHighlight,
17770        window: &mut Window,
17771        cx: &mut Context<Self>,
17772    ) {
17773        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17774    }
17775
17776    pub fn go_to_document_highlight_before_or_after_position(
17777        &mut self,
17778        direction: Direction,
17779        window: &mut Window,
17780        cx: &mut Context<Editor>,
17781    ) {
17782        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17783        let snapshot = self.snapshot(window, cx);
17784        let buffer = &snapshot.buffer_snapshot();
17785        let position = self
17786            .selections
17787            .newest::<Point>(&snapshot.display_snapshot)
17788            .head();
17789        let anchor_position = buffer.anchor_after(position);
17790
17791        // Get all document highlights (both read and write)
17792        let mut all_highlights = Vec::new();
17793
17794        if let Some((_, read_highlights)) = self
17795            .background_highlights
17796            .get(&HighlightKey::DocumentHighlightRead)
17797        {
17798            all_highlights.extend(read_highlights.iter());
17799        }
17800
17801        if let Some((_, write_highlights)) = self
17802            .background_highlights
17803            .get(&HighlightKey::DocumentHighlightWrite)
17804        {
17805            all_highlights.extend(write_highlights.iter());
17806        }
17807
17808        if all_highlights.is_empty() {
17809            return;
17810        }
17811
17812        // Sort highlights by position
17813        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17814
17815        let target_highlight = match direction {
17816            Direction::Next => {
17817                // Find the first highlight after the current position
17818                all_highlights
17819                    .iter()
17820                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17821            }
17822            Direction::Prev => {
17823                // Find the last highlight before the current position
17824                all_highlights
17825                    .iter()
17826                    .rev()
17827                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17828            }
17829        };
17830
17831        if let Some(highlight) = target_highlight {
17832            let destination = highlight.start.to_point(buffer);
17833            let autoscroll = Autoscroll::center();
17834
17835            self.unfold_ranges(&[destination..destination], false, false, cx);
17836            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17837                s.select_ranges([destination..destination]);
17838            });
17839        }
17840    }
17841
17842    fn go_to_line<T: 'static>(
17843        &mut self,
17844        position: Anchor,
17845        highlight_color: Option<Hsla>,
17846        window: &mut Window,
17847        cx: &mut Context<Self>,
17848    ) {
17849        let snapshot = self.snapshot(window, cx).display_snapshot;
17850        let position = position.to_point(&snapshot.buffer_snapshot());
17851        let start = snapshot
17852            .buffer_snapshot()
17853            .clip_point(Point::new(position.row, 0), Bias::Left);
17854        let end = start + Point::new(1, 0);
17855        let start = snapshot.buffer_snapshot().anchor_before(start);
17856        let end = snapshot.buffer_snapshot().anchor_before(end);
17857
17858        self.highlight_rows::<T>(
17859            start..end,
17860            highlight_color
17861                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17862            Default::default(),
17863            cx,
17864        );
17865
17866        if self.buffer.read(cx).is_singleton() {
17867            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17868        }
17869    }
17870
17871    pub fn go_to_definition(
17872        &mut self,
17873        _: &GoToDefinition,
17874        window: &mut Window,
17875        cx: &mut Context<Self>,
17876    ) -> Task<Result<Navigated>> {
17877        let definition =
17878            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17879        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17880        cx.spawn_in(window, async move |editor, cx| {
17881            if definition.await? == Navigated::Yes {
17882                return Ok(Navigated::Yes);
17883            }
17884            match fallback_strategy {
17885                GoToDefinitionFallback::None => Ok(Navigated::No),
17886                GoToDefinitionFallback::FindAllReferences => {
17887                    match editor.update_in(cx, |editor, window, cx| {
17888                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17889                    })? {
17890                        Some(references) => references.await,
17891                        None => Ok(Navigated::No),
17892                    }
17893                }
17894            }
17895        })
17896    }
17897
17898    pub fn go_to_declaration(
17899        &mut self,
17900        _: &GoToDeclaration,
17901        window: &mut Window,
17902        cx: &mut Context<Self>,
17903    ) -> Task<Result<Navigated>> {
17904        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17905    }
17906
17907    pub fn go_to_declaration_split(
17908        &mut self,
17909        _: &GoToDeclaration,
17910        window: &mut Window,
17911        cx: &mut Context<Self>,
17912    ) -> Task<Result<Navigated>> {
17913        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17914    }
17915
17916    pub fn go_to_implementation(
17917        &mut self,
17918        _: &GoToImplementation,
17919        window: &mut Window,
17920        cx: &mut Context<Self>,
17921    ) -> Task<Result<Navigated>> {
17922        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17923    }
17924
17925    pub fn go_to_implementation_split(
17926        &mut self,
17927        _: &GoToImplementationSplit,
17928        window: &mut Window,
17929        cx: &mut Context<Self>,
17930    ) -> Task<Result<Navigated>> {
17931        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17932    }
17933
17934    pub fn go_to_type_definition(
17935        &mut self,
17936        _: &GoToTypeDefinition,
17937        window: &mut Window,
17938        cx: &mut Context<Self>,
17939    ) -> Task<Result<Navigated>> {
17940        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17941    }
17942
17943    pub fn go_to_definition_split(
17944        &mut self,
17945        _: &GoToDefinitionSplit,
17946        window: &mut Window,
17947        cx: &mut Context<Self>,
17948    ) -> Task<Result<Navigated>> {
17949        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17950    }
17951
17952    pub fn go_to_type_definition_split(
17953        &mut self,
17954        _: &GoToTypeDefinitionSplit,
17955        window: &mut Window,
17956        cx: &mut Context<Self>,
17957    ) -> Task<Result<Navigated>> {
17958        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17959    }
17960
17961    fn go_to_definition_of_kind(
17962        &mut self,
17963        kind: GotoDefinitionKind,
17964        split: bool,
17965        window: &mut Window,
17966        cx: &mut Context<Self>,
17967    ) -> Task<Result<Navigated>> {
17968        let Some(provider) = self.semantics_provider.clone() else {
17969            return Task::ready(Ok(Navigated::No));
17970        };
17971        let head = self
17972            .selections
17973            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17974            .head();
17975        let buffer = self.buffer.read(cx);
17976        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17977            return Task::ready(Ok(Navigated::No));
17978        };
17979        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17980            return Task::ready(Ok(Navigated::No));
17981        };
17982
17983        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17984
17985        cx.spawn_in(window, async move |editor, cx| {
17986            let Some(definitions) = definitions.await? else {
17987                return Ok(Navigated::No);
17988            };
17989            let navigated = editor
17990                .update_in(cx, |editor, window, cx| {
17991                    editor.navigate_to_hover_links(
17992                        Some(kind),
17993                        definitions
17994                            .into_iter()
17995                            .filter(|location| {
17996                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17997                            })
17998                            .map(HoverLink::Text)
17999                            .collect::<Vec<_>>(),
18000                        nav_entry,
18001                        split,
18002                        window,
18003                        cx,
18004                    )
18005                })?
18006                .await?;
18007            anyhow::Ok(navigated)
18008        })
18009    }
18010
18011    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18012        let selection = self.selections.newest_anchor();
18013        let head = selection.head();
18014        let tail = selection.tail();
18015
18016        let Some((buffer, start_position)) =
18017            self.buffer.read(cx).text_anchor_for_position(head, cx)
18018        else {
18019            return;
18020        };
18021
18022        let end_position = if head != tail {
18023            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18024                return;
18025            };
18026            Some(pos)
18027        } else {
18028            None
18029        };
18030
18031        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18032            let url = if let Some(end_pos) = end_position {
18033                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18034            } else {
18035                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18036            };
18037
18038            if let Some(url) = url {
18039                cx.update(|window, cx| {
18040                    if parse_zed_link(&url, cx).is_some() {
18041                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18042                    } else {
18043                        cx.open_url(&url);
18044                    }
18045                })?;
18046            }
18047
18048            anyhow::Ok(())
18049        });
18050
18051        url_finder.detach();
18052    }
18053
18054    pub fn open_selected_filename(
18055        &mut self,
18056        _: &OpenSelectedFilename,
18057        window: &mut Window,
18058        cx: &mut Context<Self>,
18059    ) {
18060        let Some(workspace) = self.workspace() else {
18061            return;
18062        };
18063
18064        let position = self.selections.newest_anchor().head();
18065
18066        let Some((buffer, buffer_position)) =
18067            self.buffer.read(cx).text_anchor_for_position(position, cx)
18068        else {
18069            return;
18070        };
18071
18072        let project = self.project.clone();
18073
18074        cx.spawn_in(window, async move |_, cx| {
18075            let result = find_file(&buffer, project, buffer_position, cx).await;
18076
18077            if let Some((_, path)) = result {
18078                workspace
18079                    .update_in(cx, |workspace, window, cx| {
18080                        workspace.open_resolved_path(path, window, cx)
18081                    })?
18082                    .await?;
18083            }
18084            anyhow::Ok(())
18085        })
18086        .detach();
18087    }
18088
18089    pub(crate) fn navigate_to_hover_links(
18090        &mut self,
18091        kind: Option<GotoDefinitionKind>,
18092        definitions: Vec<HoverLink>,
18093        origin: Option<NavigationEntry>,
18094        split: bool,
18095        window: &mut Window,
18096        cx: &mut Context<Editor>,
18097    ) -> Task<Result<Navigated>> {
18098        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18099        let mut first_url_or_file = None;
18100        let definitions: Vec<_> = definitions
18101            .into_iter()
18102            .filter_map(|def| match def {
18103                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18104                HoverLink::InlayHint(lsp_location, server_id) => {
18105                    let computation =
18106                        self.compute_target_location(lsp_location, server_id, window, cx);
18107                    Some(cx.background_spawn(computation))
18108                }
18109                HoverLink::Url(url) => {
18110                    first_url_or_file = Some(Either::Left(url));
18111                    None
18112                }
18113                HoverLink::File(path) => {
18114                    first_url_or_file = Some(Either::Right(path));
18115                    None
18116                }
18117            })
18118            .collect();
18119
18120        let workspace = self.workspace();
18121
18122        cx.spawn_in(window, async move |editor, cx| {
18123            let locations: Vec<Location> = future::join_all(definitions)
18124                .await
18125                .into_iter()
18126                .filter_map(|location| location.transpose())
18127                .collect::<Result<_>>()
18128                .context("location tasks")?;
18129            let mut locations = cx.update(|_, cx| {
18130                locations
18131                    .into_iter()
18132                    .map(|location| {
18133                        let buffer = location.buffer.read(cx);
18134                        (location.buffer, location.range.to_point(buffer))
18135                    })
18136                    .into_group_map()
18137            })?;
18138            let mut num_locations = 0;
18139            for ranges in locations.values_mut() {
18140                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18141                ranges.dedup();
18142                num_locations += ranges.len();
18143            }
18144
18145            if num_locations > 1 {
18146                let tab_kind = match kind {
18147                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18148                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18149                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18150                    Some(GotoDefinitionKind::Type) => "Types",
18151                };
18152                let title = editor
18153                    .update_in(cx, |_, _, cx| {
18154                        let target = locations
18155                            .iter()
18156                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18157                            .map(|(buffer, location)| {
18158                                buffer
18159                                    .read(cx)
18160                                    .text_for_range(location.clone())
18161                                    .collect::<String>()
18162                            })
18163                            .filter(|text| !text.contains('\n'))
18164                            .unique()
18165                            .take(3)
18166                            .join(", ");
18167                        if target.is_empty() {
18168                            tab_kind.to_owned()
18169                        } else {
18170                            format!("{tab_kind} for {target}")
18171                        }
18172                    })
18173                    .context("buffer title")?;
18174
18175                let Some(workspace) = workspace else {
18176                    return Ok(Navigated::No);
18177                };
18178
18179                let opened = workspace
18180                    .update_in(cx, |workspace, window, cx| {
18181                        let allow_preview = PreviewTabsSettings::get_global(cx)
18182                            .enable_preview_multibuffer_from_code_navigation;
18183                        if let Some((target_editor, target_pane)) =
18184                            Self::open_locations_in_multibuffer(
18185                                workspace,
18186                                locations,
18187                                title,
18188                                split,
18189                                allow_preview,
18190                                MultibufferSelectionMode::First,
18191                                window,
18192                                cx,
18193                            )
18194                        {
18195                            // We create our own nav history instead of using
18196                            // `target_editor.nav_history` because `nav_history`
18197                            // seems to be populated asynchronously when an item
18198                            // is added to a pane
18199                            let mut nav_history = target_pane
18200                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18201                            target_editor.update(cx, |editor, cx| {
18202                                let nav_data = editor
18203                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18204                                let target =
18205                                    Some(nav_history.navigation_entry(Some(
18206                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18207                                    )));
18208                                nav_history.push_tag(origin, target);
18209                            })
18210                        }
18211                    })
18212                    .is_ok();
18213
18214                anyhow::Ok(Navigated::from_bool(opened))
18215            } else if num_locations == 0 {
18216                // If there is one url or file, open it directly
18217                match first_url_or_file {
18218                    Some(Either::Left(url)) => {
18219                        cx.update(|window, cx| {
18220                            if parse_zed_link(&url, cx).is_some() {
18221                                window
18222                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18223                            } else {
18224                                cx.open_url(&url);
18225                            }
18226                        })?;
18227                        Ok(Navigated::Yes)
18228                    }
18229                    Some(Either::Right(path)) => {
18230                        // TODO(andrew): respect preview tab settings
18231                        //               `enable_keep_preview_on_code_navigation` and
18232                        //               `enable_preview_file_from_code_navigation`
18233                        let Some(workspace) = workspace else {
18234                            return Ok(Navigated::No);
18235                        };
18236                        workspace
18237                            .update_in(cx, |workspace, window, cx| {
18238                                workspace.open_resolved_path(path, window, cx)
18239                            })?
18240                            .await?;
18241                        Ok(Navigated::Yes)
18242                    }
18243                    None => Ok(Navigated::No),
18244                }
18245            } else {
18246                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18247                let target_range = target_ranges.first().unwrap().clone();
18248
18249                editor.update_in(cx, |editor, window, cx| {
18250                    let range = editor.range_for_match(&target_range);
18251                    let range = collapse_multiline_range(range);
18252
18253                    if !split
18254                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18255                    {
18256                        editor.go_to_singleton_buffer_range(range, window, cx);
18257
18258                        let target =
18259                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18260                        if let Some(mut nav_history) = editor.nav_history.clone() {
18261                            nav_history.push_tag(origin, target);
18262                        }
18263                    } else {
18264                        let Some(workspace) = workspace else {
18265                            return Navigated::No;
18266                        };
18267                        let pane = workspace.read(cx).active_pane().clone();
18268                        window.defer(cx, move |window, cx| {
18269                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18270                                workspace.update(cx, |workspace, cx| {
18271                                    let pane = if split {
18272                                        workspace.adjacent_pane(window, cx)
18273                                    } else {
18274                                        workspace.active_pane().clone()
18275                                    };
18276
18277                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18278                                    let keep_old_preview = preview_tabs_settings
18279                                        .enable_keep_preview_on_code_navigation;
18280                                    let allow_new_preview = preview_tabs_settings
18281                                        .enable_preview_file_from_code_navigation;
18282
18283                                    let editor = workspace.open_project_item(
18284                                        pane.clone(),
18285                                        target_buffer.clone(),
18286                                        true,
18287                                        true,
18288                                        keep_old_preview,
18289                                        allow_new_preview,
18290                                        window,
18291                                        cx,
18292                                    );
18293                                    (editor, pane)
18294                                });
18295                            // We create our own nav history instead of using
18296                            // `target_editor.nav_history` because `nav_history`
18297                            // seems to be populated asynchronously when an item
18298                            // is added to a pane
18299                            let mut nav_history = target_pane
18300                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18301                            target_editor.update(cx, |target_editor, cx| {
18302                                // When selecting a definition in a different buffer, disable the nav history
18303                                // to avoid creating a history entry at the previous cursor location.
18304                                pane.update(cx, |pane, _| pane.disable_history());
18305                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18306
18307                                let nav_data = target_editor.navigation_data(
18308                                    target_editor.selections.newest_anchor().head(),
18309                                    cx,
18310                                );
18311                                let target =
18312                                    Some(nav_history.navigation_entry(Some(
18313                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18314                                    )));
18315                                nav_history.push_tag(origin, target);
18316                                pane.update(cx, |pane, _| pane.enable_history());
18317                            });
18318                        });
18319                    }
18320                    Navigated::Yes
18321                })
18322            }
18323        })
18324    }
18325
18326    fn compute_target_location(
18327        &self,
18328        lsp_location: lsp::Location,
18329        server_id: LanguageServerId,
18330        window: &mut Window,
18331        cx: &mut Context<Self>,
18332    ) -> Task<anyhow::Result<Option<Location>>> {
18333        let Some(project) = self.project.clone() else {
18334            return Task::ready(Ok(None));
18335        };
18336
18337        cx.spawn_in(window, async move |editor, cx| {
18338            let location_task = editor.update(cx, |_, cx| {
18339                project.update(cx, |project, cx| {
18340                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18341                })
18342            })?;
18343            let location = Some({
18344                let target_buffer_handle = location_task.await.context("open local buffer")?;
18345                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18346                    let target_start = target_buffer
18347                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18348                    let target_end = target_buffer
18349                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18350                    target_buffer.anchor_after(target_start)
18351                        ..target_buffer.anchor_before(target_end)
18352                });
18353                Location {
18354                    buffer: target_buffer_handle,
18355                    range,
18356                }
18357            });
18358            Ok(location)
18359        })
18360    }
18361
18362    fn go_to_next_reference(
18363        &mut self,
18364        _: &GoToNextReference,
18365        window: &mut Window,
18366        cx: &mut Context<Self>,
18367    ) {
18368        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18369        if let Some(task) = task {
18370            task.detach();
18371        };
18372    }
18373
18374    fn go_to_prev_reference(
18375        &mut self,
18376        _: &GoToPreviousReference,
18377        window: &mut Window,
18378        cx: &mut Context<Self>,
18379    ) {
18380        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18381        if let Some(task) = task {
18382            task.detach();
18383        };
18384    }
18385
18386    fn go_to_symbol_by_offset(
18387        &mut self,
18388        window: &mut Window,
18389        cx: &mut Context<Self>,
18390        offset: i8,
18391    ) -> Task<Result<()>> {
18392        let editor_snapshot = self.snapshot(window, cx);
18393
18394        // We don't care about multi-buffer symbols
18395        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18396            return Task::ready(Ok(()));
18397        };
18398
18399        let cursor_offset = self
18400            .selections
18401            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18402            .head();
18403
18404        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18405            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18406                let buffer = ed.buffer.read(cx).as_singleton()?;
18407                Some(buffer.read(cx).remote_id())
18408            }) else {
18409                return Ok(());
18410            };
18411
18412            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18413            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18414
18415            let multi_snapshot = editor_snapshot.buffer();
18416            let buffer_range = |range: &Range<_>| {
18417                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18418            };
18419
18420            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18421                let current_idx = outline_items
18422                    .iter()
18423                    .enumerate()
18424                    .filter_map(|(idx, item)| {
18425                        // Find the closest outline item by distance between outline text and cursor location
18426                        let source_range = buffer_range(&item.source_range_for_text);
18427                        let distance_to_closest_endpoint = cmp::min(
18428                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18429                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18430                        );
18431
18432                        let item_towards_offset =
18433                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18434                                == (offset as isize).signum();
18435
18436                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18437
18438                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18439                        // we should not already be within the outline's source range. We then pick the closest outline
18440                        // item.
18441                        (item_towards_offset && !source_range_contains_cursor)
18442                            .then_some((distance_to_closest_endpoint, idx))
18443                    })
18444                    .min()
18445                    .map(|(_, idx)| idx);
18446
18447                let Some(idx) = current_idx else {
18448                    return;
18449                };
18450
18451                let range = buffer_range(&outline_items[idx].source_range_for_text);
18452                let selection = [range.start..range.start];
18453
18454                let _ = editor
18455                    .update(acx, |editor, ecx| {
18456                        editor.change_selections(
18457                            SelectionEffects::scroll(Autoscroll::newest()),
18458                            window,
18459                            ecx,
18460                            |s| s.select_ranges(selection),
18461                        );
18462                    })
18463                    .ok();
18464            })?;
18465
18466            Ok(())
18467        })
18468    }
18469
18470    fn go_to_next_symbol(
18471        &mut self,
18472        _: &GoToNextSymbol,
18473        window: &mut Window,
18474        cx: &mut Context<Self>,
18475    ) {
18476        self.go_to_symbol_by_offset(window, cx, 1).detach();
18477    }
18478
18479    fn go_to_previous_symbol(
18480        &mut self,
18481        _: &GoToPreviousSymbol,
18482        window: &mut Window,
18483        cx: &mut Context<Self>,
18484    ) {
18485        self.go_to_symbol_by_offset(window, cx, -1).detach();
18486    }
18487
18488    pub fn go_to_reference_before_or_after_position(
18489        &mut self,
18490        direction: Direction,
18491        count: usize,
18492        window: &mut Window,
18493        cx: &mut Context<Self>,
18494    ) -> Option<Task<Result<()>>> {
18495        let selection = self.selections.newest_anchor();
18496        let head = selection.head();
18497
18498        let multi_buffer = self.buffer.read(cx);
18499
18500        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18501        let workspace = self.workspace()?;
18502        let project = workspace.read(cx).project().clone();
18503        let references =
18504            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18505        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18506            let Some(locations) = references.await? else {
18507                return Ok(());
18508            };
18509
18510            if locations.is_empty() {
18511                // totally normal - the cursor may be on something which is not
18512                // a symbol (e.g. a keyword)
18513                log::info!("no references found under cursor");
18514                return Ok(());
18515            }
18516
18517            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18518
18519            let (locations, current_location_index) =
18520                multi_buffer.update(cx, |multi_buffer, cx| {
18521                    let mut locations = locations
18522                        .into_iter()
18523                        .filter_map(|loc| {
18524                            let start = multi_buffer.buffer_anchor_to_anchor(
18525                                &loc.buffer,
18526                                loc.range.start,
18527                                cx,
18528                            )?;
18529                            let end = multi_buffer.buffer_anchor_to_anchor(
18530                                &loc.buffer,
18531                                loc.range.end,
18532                                cx,
18533                            )?;
18534                            Some(start..end)
18535                        })
18536                        .collect::<Vec<_>>();
18537
18538                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18539                    // There is an O(n) implementation, but given this list will be
18540                    // small (usually <100 items), the extra O(log(n)) factor isn't
18541                    // worth the (surprisingly large amount of) extra complexity.
18542                    locations
18543                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18544
18545                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18546
18547                    let current_location_index = locations.iter().position(|loc| {
18548                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18549                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18550                    });
18551
18552                    (locations, current_location_index)
18553                });
18554
18555            let Some(current_location_index) = current_location_index else {
18556                // This indicates something has gone wrong, because we already
18557                // handle the "no references" case above
18558                log::error!(
18559                    "failed to find current reference under cursor. Total references: {}",
18560                    locations.len()
18561                );
18562                return Ok(());
18563            };
18564
18565            let destination_location_index = match direction {
18566                Direction::Next => (current_location_index + count) % locations.len(),
18567                Direction::Prev => {
18568                    (current_location_index + locations.len() - count % locations.len())
18569                        % locations.len()
18570                }
18571            };
18572
18573            // TODO(cameron): is this needed?
18574            // the thinking is to avoid "jumping to the current location" (avoid
18575            // polluting "jumplist" in vim terms)
18576            if current_location_index == destination_location_index {
18577                return Ok(());
18578            }
18579
18580            let Range { start, end } = locations[destination_location_index];
18581
18582            editor.update_in(cx, |editor, window, cx| {
18583                let effects = SelectionEffects::default();
18584
18585                editor.unfold_ranges(&[start..end], false, false, cx);
18586                editor.change_selections(effects, window, cx, |s| {
18587                    s.select_ranges([start..start]);
18588                });
18589            })?;
18590
18591            Ok(())
18592        }))
18593    }
18594
18595    pub fn find_all_references(
18596        &mut self,
18597        action: &FindAllReferences,
18598        window: &mut Window,
18599        cx: &mut Context<Self>,
18600    ) -> Option<Task<Result<Navigated>>> {
18601        let always_open_multibuffer = action.always_open_multibuffer;
18602        let selection = self.selections.newest_anchor();
18603        let multi_buffer = self.buffer.read(cx);
18604        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18605        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18606        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18607        let head = selection_offset.head();
18608
18609        let head_anchor = multi_buffer_snapshot.anchor_at(
18610            head,
18611            if head < selection_offset.tail() {
18612                Bias::Right
18613            } else {
18614                Bias::Left
18615            },
18616        );
18617
18618        match self
18619            .find_all_references_task_sources
18620            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18621        {
18622            Ok(_) => {
18623                log::info!(
18624                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18625                );
18626                return None;
18627            }
18628            Err(i) => {
18629                self.find_all_references_task_sources.insert(i, head_anchor);
18630            }
18631        }
18632
18633        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18634        let workspace = self.workspace()?;
18635        let project = workspace.read(cx).project().clone();
18636        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18637        Some(cx.spawn_in(window, async move |editor, cx| {
18638            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18639                if let Ok(i) = editor
18640                    .find_all_references_task_sources
18641                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18642                {
18643                    editor.find_all_references_task_sources.remove(i);
18644                }
18645            });
18646
18647            let Some(locations) = references.await? else {
18648                return anyhow::Ok(Navigated::No);
18649            };
18650            let mut locations = cx.update(|_, cx| {
18651                locations
18652                    .into_iter()
18653                    .map(|location| {
18654                        let buffer = location.buffer.read(cx);
18655                        (location.buffer, location.range.to_point(buffer))
18656                    })
18657                    // if special-casing the single-match case, remove ranges
18658                    // that intersect current selection
18659                    .filter(|(location_buffer, location)| {
18660                        if always_open_multibuffer || &buffer != location_buffer {
18661                            return true;
18662                        }
18663
18664                        !location.contains_inclusive(&selection_point.range())
18665                    })
18666                    .into_group_map()
18667            })?;
18668            if locations.is_empty() {
18669                return anyhow::Ok(Navigated::No);
18670            }
18671            for ranges in locations.values_mut() {
18672                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18673                ranges.dedup();
18674            }
18675            let mut num_locations = 0;
18676            for ranges in locations.values_mut() {
18677                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18678                ranges.dedup();
18679                num_locations += ranges.len();
18680            }
18681
18682            if num_locations == 1 && !always_open_multibuffer {
18683                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18684                let target_range = target_ranges.first().unwrap().clone();
18685
18686                return editor.update_in(cx, |editor, window, cx| {
18687                    let range = target_range.to_point(target_buffer.read(cx));
18688                    let range = editor.range_for_match(&range);
18689                    let range = range.start..range.start;
18690
18691                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18692                        editor.go_to_singleton_buffer_range(range, window, cx);
18693                    } else {
18694                        let pane = workspace.read(cx).active_pane().clone();
18695                        window.defer(cx, move |window, cx| {
18696                            let target_editor: Entity<Self> =
18697                                workspace.update(cx, |workspace, cx| {
18698                                    let pane = workspace.active_pane().clone();
18699
18700                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18701                                    let keep_old_preview = preview_tabs_settings
18702                                        .enable_keep_preview_on_code_navigation;
18703                                    let allow_new_preview = preview_tabs_settings
18704                                        .enable_preview_file_from_code_navigation;
18705
18706                                    workspace.open_project_item(
18707                                        pane,
18708                                        target_buffer.clone(),
18709                                        true,
18710                                        true,
18711                                        keep_old_preview,
18712                                        allow_new_preview,
18713                                        window,
18714                                        cx,
18715                                    )
18716                                });
18717                            target_editor.update(cx, |target_editor, cx| {
18718                                // When selecting a definition in a different buffer, disable the nav history
18719                                // to avoid creating a history entry at the previous cursor location.
18720                                pane.update(cx, |pane, _| pane.disable_history());
18721                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18722                                pane.update(cx, |pane, _| pane.enable_history());
18723                            });
18724                        });
18725                    }
18726                    Navigated::No
18727                });
18728            }
18729
18730            workspace.update_in(cx, |workspace, window, cx| {
18731                let target = locations
18732                    .iter()
18733                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18734                    .map(|(buffer, location)| {
18735                        buffer
18736                            .read(cx)
18737                            .text_for_range(location.clone())
18738                            .collect::<String>()
18739                    })
18740                    .filter(|text| !text.contains('\n'))
18741                    .unique()
18742                    .take(3)
18743                    .join(", ");
18744                let title = if target.is_empty() {
18745                    "References".to_owned()
18746                } else {
18747                    format!("References to {target}")
18748                };
18749                let allow_preview = PreviewTabsSettings::get_global(cx)
18750                    .enable_preview_multibuffer_from_code_navigation;
18751                Self::open_locations_in_multibuffer(
18752                    workspace,
18753                    locations,
18754                    title,
18755                    false,
18756                    allow_preview,
18757                    MultibufferSelectionMode::First,
18758                    window,
18759                    cx,
18760                );
18761                Navigated::Yes
18762            })
18763        }))
18764    }
18765
18766    /// Opens a multibuffer with the given project locations in it.
18767    pub fn open_locations_in_multibuffer(
18768        workspace: &mut Workspace,
18769        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18770        title: String,
18771        split: bool,
18772        allow_preview: bool,
18773        multibuffer_selection_mode: MultibufferSelectionMode,
18774        window: &mut Window,
18775        cx: &mut Context<Workspace>,
18776    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18777        if locations.is_empty() {
18778            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18779            return None;
18780        }
18781
18782        let capability = workspace.project().read(cx).capability();
18783        let mut ranges = <Vec<Range<Anchor>>>::new();
18784
18785        // a key to find existing multibuffer editors with the same set of locations
18786        // to prevent us from opening more and more multibuffer tabs for searches and the like
18787        let mut key = (title.clone(), vec![]);
18788        let excerpt_buffer = cx.new(|cx| {
18789            let key = &mut key.1;
18790            let mut multibuffer = MultiBuffer::new(capability);
18791            for (buffer, mut ranges_for_buffer) in locations {
18792                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18793                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18794                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18795                    PathKey::for_buffer(&buffer, cx),
18796                    buffer.clone(),
18797                    ranges_for_buffer,
18798                    multibuffer_context_lines(cx),
18799                    cx,
18800                );
18801                ranges.extend(new_ranges)
18802            }
18803
18804            multibuffer.with_title(title)
18805        });
18806        let existing = workspace.active_pane().update(cx, |pane, cx| {
18807            pane.items()
18808                .filter_map(|item| item.downcast::<Editor>())
18809                .find(|editor| {
18810                    editor
18811                        .read(cx)
18812                        .lookup_key
18813                        .as_ref()
18814                        .and_then(|it| {
18815                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18816                        })
18817                        .is_some_and(|it| *it == key)
18818                })
18819        });
18820        let was_existing = existing.is_some();
18821        let editor = existing.unwrap_or_else(|| {
18822            cx.new(|cx| {
18823                let mut editor = Editor::for_multibuffer(
18824                    excerpt_buffer,
18825                    Some(workspace.project().clone()),
18826                    window,
18827                    cx,
18828                );
18829                editor.lookup_key = Some(Box::new(key));
18830                editor
18831            })
18832        });
18833        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18834            MultibufferSelectionMode::First => {
18835                if let Some(first_range) = ranges.first() {
18836                    editor.change_selections(
18837                        SelectionEffects::no_scroll(),
18838                        window,
18839                        cx,
18840                        |selections| {
18841                            selections.clear_disjoint();
18842                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18843                        },
18844                    );
18845                }
18846                editor.highlight_background(
18847                    HighlightKey::Editor,
18848                    &ranges,
18849                    |_, theme| theme.colors().editor_highlighted_line_background,
18850                    cx,
18851                );
18852            }
18853            MultibufferSelectionMode::All => {
18854                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18855                    selections.clear_disjoint();
18856                    selections.select_anchor_ranges(ranges);
18857                });
18858            }
18859        });
18860
18861        let item = Box::new(editor.clone());
18862
18863        let pane = if split {
18864            workspace.adjacent_pane(window, cx)
18865        } else {
18866            workspace.active_pane().clone()
18867        };
18868        let activate_pane = split;
18869
18870        let mut destination_index = None;
18871        pane.update(cx, |pane, cx| {
18872            if allow_preview && !was_existing {
18873                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18874            }
18875            if was_existing && !allow_preview {
18876                pane.unpreview_item_if_preview(item.item_id());
18877            }
18878            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18879        });
18880
18881        Some((editor, pane))
18882    }
18883
18884    pub fn rename(
18885        &mut self,
18886        _: &Rename,
18887        window: &mut Window,
18888        cx: &mut Context<Self>,
18889    ) -> Option<Task<Result<()>>> {
18890        use language::ToOffset as _;
18891
18892        let provider = self.semantics_provider.clone()?;
18893        let selection = self.selections.newest_anchor().clone();
18894        let (cursor_buffer, cursor_buffer_position) = self
18895            .buffer
18896            .read(cx)
18897            .text_anchor_for_position(selection.head(), cx)?;
18898        let (tail_buffer, cursor_buffer_position_end) = self
18899            .buffer
18900            .read(cx)
18901            .text_anchor_for_position(selection.tail(), cx)?;
18902        if tail_buffer != cursor_buffer {
18903            return None;
18904        }
18905
18906        let snapshot = cursor_buffer.read(cx).snapshot();
18907        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18908        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18909        let prepare_rename = provider
18910            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18911            .unwrap_or_else(|| Task::ready(Ok(None)));
18912        drop(snapshot);
18913
18914        Some(cx.spawn_in(window, async move |this, cx| {
18915            let rename_range = if let Some(range) = prepare_rename.await? {
18916                Some(range)
18917            } else {
18918                this.update(cx, |this, cx| {
18919                    let buffer = this.buffer.read(cx).snapshot(cx);
18920                    let mut buffer_highlights = this
18921                        .document_highlights_for_position(selection.head(), &buffer)
18922                        .filter(|highlight| {
18923                            highlight.start.excerpt_id == selection.head().excerpt_id
18924                                && highlight.end.excerpt_id == selection.head().excerpt_id
18925                        });
18926                    buffer_highlights
18927                        .next()
18928                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18929                })?
18930            };
18931            if let Some(rename_range) = rename_range {
18932                this.update_in(cx, |this, window, cx| {
18933                    let snapshot = cursor_buffer.read(cx).snapshot();
18934                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18935                    let cursor_offset_in_rename_range =
18936                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18937                    let cursor_offset_in_rename_range_end =
18938                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18939
18940                    this.take_rename(false, window, cx);
18941                    let buffer = this.buffer.read(cx).read(cx);
18942                    let cursor_offset = selection.head().to_offset(&buffer);
18943                    let rename_start =
18944                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18945                    let rename_end = rename_start + rename_buffer_range.len();
18946                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18947                    let mut old_highlight_id = None;
18948                    let old_name: Arc<str> = buffer
18949                        .chunks(rename_start..rename_end, true)
18950                        .map(|chunk| {
18951                            if old_highlight_id.is_none() {
18952                                old_highlight_id = chunk.syntax_highlight_id;
18953                            }
18954                            chunk.text
18955                        })
18956                        .collect::<String>()
18957                        .into();
18958
18959                    drop(buffer);
18960
18961                    // Position the selection in the rename editor so that it matches the current selection.
18962                    this.show_local_selections = false;
18963                    let rename_editor = cx.new(|cx| {
18964                        let mut editor = Editor::single_line(window, cx);
18965                        editor.buffer.update(cx, |buffer, cx| {
18966                            buffer.edit(
18967                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18968                                None,
18969                                cx,
18970                            )
18971                        });
18972                        let cursor_offset_in_rename_range =
18973                            MultiBufferOffset(cursor_offset_in_rename_range);
18974                        let cursor_offset_in_rename_range_end =
18975                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18976                        let rename_selection_range = match cursor_offset_in_rename_range
18977                            .cmp(&cursor_offset_in_rename_range_end)
18978                        {
18979                            Ordering::Equal => {
18980                                editor.select_all(&SelectAll, window, cx);
18981                                return editor;
18982                            }
18983                            Ordering::Less => {
18984                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18985                            }
18986                            Ordering::Greater => {
18987                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18988                            }
18989                        };
18990                        if rename_selection_range.end.0 > old_name.len() {
18991                            editor.select_all(&SelectAll, window, cx);
18992                        } else {
18993                            editor.change_selections(Default::default(), window, cx, |s| {
18994                                s.select_ranges([rename_selection_range]);
18995                            });
18996                        }
18997                        editor
18998                    });
18999                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19000                        if e == &EditorEvent::Focused {
19001                            cx.emit(EditorEvent::FocusedIn)
19002                        }
19003                    })
19004                    .detach();
19005
19006                    let write_highlights =
19007                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19008                    let read_highlights =
19009                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19010                    let ranges = write_highlights
19011                        .iter()
19012                        .flat_map(|(_, ranges)| ranges.iter())
19013                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19014                        .cloned()
19015                        .collect();
19016
19017                    this.highlight_text(
19018                        HighlightKey::Rename,
19019                        ranges,
19020                        HighlightStyle {
19021                            fade_out: Some(0.6),
19022                            ..Default::default()
19023                        },
19024                        cx,
19025                    );
19026                    let rename_focus_handle = rename_editor.focus_handle(cx);
19027                    window.focus(&rename_focus_handle, cx);
19028                    let block_id = this.insert_blocks(
19029                        [BlockProperties {
19030                            style: BlockStyle::Flex,
19031                            placement: BlockPlacement::Below(range.start),
19032                            height: Some(1),
19033                            render: Arc::new({
19034                                let rename_editor = rename_editor.clone();
19035                                move |cx: &mut BlockContext| {
19036                                    let mut text_style = cx.editor_style.text.clone();
19037                                    if let Some(highlight_style) = old_highlight_id
19038                                        .and_then(|h| h.style(&cx.editor_style.syntax))
19039                                    {
19040                                        text_style = text_style.highlight(highlight_style);
19041                                    }
19042                                    div()
19043                                        .block_mouse_except_scroll()
19044                                        .pl(cx.anchor_x)
19045                                        .child(EditorElement::new(
19046                                            &rename_editor,
19047                                            EditorStyle {
19048                                                background: cx.theme().system().transparent,
19049                                                local_player: cx.editor_style.local_player,
19050                                                text: text_style,
19051                                                scrollbar_width: cx.editor_style.scrollbar_width,
19052                                                syntax: cx.editor_style.syntax.clone(),
19053                                                status: cx.editor_style.status.clone(),
19054                                                inlay_hints_style: HighlightStyle {
19055                                                    font_weight: Some(FontWeight::BOLD),
19056                                                    ..make_inlay_hints_style(cx.app)
19057                                                },
19058                                                edit_prediction_styles: make_suggestion_styles(
19059                                                    cx.app,
19060                                                ),
19061                                                ..EditorStyle::default()
19062                                            },
19063                                        ))
19064                                        .into_any_element()
19065                                }
19066                            }),
19067                            priority: 0,
19068                        }],
19069                        Some(Autoscroll::fit()),
19070                        cx,
19071                    )[0];
19072                    this.pending_rename = Some(RenameState {
19073                        range,
19074                        old_name,
19075                        editor: rename_editor,
19076                        block_id,
19077                    });
19078                })?;
19079            }
19080
19081            Ok(())
19082        }))
19083    }
19084
19085    pub fn confirm_rename(
19086        &mut self,
19087        _: &ConfirmRename,
19088        window: &mut Window,
19089        cx: &mut Context<Self>,
19090    ) -> Option<Task<Result<()>>> {
19091        let rename = self.take_rename(false, window, cx)?;
19092        let workspace = self.workspace()?.downgrade();
19093        let (buffer, start) = self
19094            .buffer
19095            .read(cx)
19096            .text_anchor_for_position(rename.range.start, cx)?;
19097        let (end_buffer, _) = self
19098            .buffer
19099            .read(cx)
19100            .text_anchor_for_position(rename.range.end, cx)?;
19101        if buffer != end_buffer {
19102            return None;
19103        }
19104
19105        let old_name = rename.old_name;
19106        let new_name = rename.editor.read(cx).text(cx);
19107
19108        let rename = self.semantics_provider.as_ref()?.perform_rename(
19109            &buffer,
19110            start,
19111            new_name.clone(),
19112            cx,
19113        )?;
19114
19115        Some(cx.spawn_in(window, async move |editor, cx| {
19116            let project_transaction = rename.await?;
19117            Self::open_project_transaction(
19118                &editor,
19119                workspace,
19120                project_transaction,
19121                format!("Rename: {}{}", old_name, new_name),
19122                cx,
19123            )
19124            .await?;
19125
19126            editor.update(cx, |editor, cx| {
19127                editor.refresh_document_highlights(cx);
19128            })?;
19129            Ok(())
19130        }))
19131    }
19132
19133    fn take_rename(
19134        &mut self,
19135        moving_cursor: bool,
19136        window: &mut Window,
19137        cx: &mut Context<Self>,
19138    ) -> Option<RenameState> {
19139        let rename = self.pending_rename.take()?;
19140        if rename.editor.focus_handle(cx).is_focused(window) {
19141            window.focus(&self.focus_handle, cx);
19142        }
19143
19144        self.remove_blocks(
19145            [rename.block_id].into_iter().collect(),
19146            Some(Autoscroll::fit()),
19147            cx,
19148        );
19149        self.clear_highlights(HighlightKey::Rename, cx);
19150        self.show_local_selections = true;
19151
19152        if moving_cursor {
19153            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19154                editor
19155                    .selections
19156                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19157                    .head()
19158            });
19159
19160            // Update the selection to match the position of the selection inside
19161            // the rename editor.
19162            let snapshot = self.buffer.read(cx).read(cx);
19163            let rename_range = rename.range.to_offset(&snapshot);
19164            let cursor_in_editor = snapshot
19165                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19166                .min(rename_range.end);
19167            drop(snapshot);
19168
19169            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19170                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19171            });
19172        } else {
19173            self.refresh_document_highlights(cx);
19174        }
19175
19176        Some(rename)
19177    }
19178
19179    pub fn pending_rename(&self) -> Option<&RenameState> {
19180        self.pending_rename.as_ref()
19181    }
19182
19183    fn format(
19184        &mut self,
19185        _: &Format,
19186        window: &mut Window,
19187        cx: &mut Context<Self>,
19188    ) -> Option<Task<Result<()>>> {
19189        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19190
19191        let project = match &self.project {
19192            Some(project) => project.clone(),
19193            None => return None,
19194        };
19195
19196        Some(self.perform_format(
19197            project,
19198            FormatTrigger::Manual,
19199            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19200            window,
19201            cx,
19202        ))
19203    }
19204
19205    fn format_selections(
19206        &mut self,
19207        _: &FormatSelections,
19208        window: &mut Window,
19209        cx: &mut Context<Self>,
19210    ) -> Option<Task<Result<()>>> {
19211        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19212
19213        let project = match &self.project {
19214            Some(project) => project.clone(),
19215            None => return None,
19216        };
19217
19218        let ranges = self
19219            .selections
19220            .all_adjusted(&self.display_snapshot(cx))
19221            .into_iter()
19222            .map(|selection| selection.range())
19223            .collect_vec();
19224
19225        Some(self.perform_format(
19226            project,
19227            FormatTrigger::Manual,
19228            FormatTarget::Ranges(ranges),
19229            window,
19230            cx,
19231        ))
19232    }
19233
19234    fn perform_format(
19235        &mut self,
19236        project: Entity<Project>,
19237        trigger: FormatTrigger,
19238        target: FormatTarget,
19239        window: &mut Window,
19240        cx: &mut Context<Self>,
19241    ) -> Task<Result<()>> {
19242        let buffer = self.buffer.clone();
19243        let (buffers, target) = match target {
19244            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19245            FormatTarget::Ranges(selection_ranges) => {
19246                let multi_buffer = buffer.read(cx);
19247                let snapshot = multi_buffer.read(cx);
19248                let mut buffers = HashSet::default();
19249                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19250                    BTreeMap::new();
19251                for selection_range in selection_ranges {
19252                    for (buffer, buffer_range, _) in
19253                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19254                    {
19255                        let buffer_id = buffer.remote_id();
19256                        let start = buffer.anchor_before(buffer_range.start);
19257                        let end = buffer.anchor_after(buffer_range.end);
19258                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19259                        buffer_id_to_ranges
19260                            .entry(buffer_id)
19261                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19262                            .or_insert_with(|| vec![start..end]);
19263                    }
19264                }
19265                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19266            }
19267        };
19268
19269        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19270        let selections_prev = transaction_id_prev
19271            .and_then(|transaction_id_prev| {
19272                // default to selections as they were after the last edit, if we have them,
19273                // instead of how they are now.
19274                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19275                // will take you back to where you made the last edit, instead of staying where you scrolled
19276                self.selection_history
19277                    .transaction(transaction_id_prev)
19278                    .map(|t| t.0.clone())
19279            })
19280            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19281
19282        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19283        let format = project.update(cx, |project, cx| {
19284            project.format(buffers, target, true, trigger, cx)
19285        });
19286
19287        cx.spawn_in(window, async move |editor, cx| {
19288            let transaction = futures::select_biased! {
19289                transaction = format.log_err().fuse() => transaction,
19290                () = timeout => {
19291                    log::warn!("timed out waiting for formatting");
19292                    None
19293                }
19294            };
19295
19296            buffer.update(cx, |buffer, cx| {
19297                if let Some(transaction) = transaction
19298                    && !buffer.is_singleton()
19299                {
19300                    buffer.push_transaction(&transaction.0, cx);
19301                }
19302                cx.notify();
19303            });
19304
19305            if let Some(transaction_id_now) =
19306                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19307            {
19308                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19309                if has_new_transaction {
19310                    editor
19311                        .update(cx, |editor, _| {
19312                            editor
19313                                .selection_history
19314                                .insert_transaction(transaction_id_now, selections_prev);
19315                        })
19316                        .ok();
19317                }
19318            }
19319
19320            Ok(())
19321        })
19322    }
19323
19324    fn organize_imports(
19325        &mut self,
19326        _: &OrganizeImports,
19327        window: &mut Window,
19328        cx: &mut Context<Self>,
19329    ) -> Option<Task<Result<()>>> {
19330        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19331        let project = match &self.project {
19332            Some(project) => project.clone(),
19333            None => return None,
19334        };
19335        Some(self.perform_code_action_kind(
19336            project,
19337            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19338            window,
19339            cx,
19340        ))
19341    }
19342
19343    fn perform_code_action_kind(
19344        &mut self,
19345        project: Entity<Project>,
19346        kind: CodeActionKind,
19347        window: &mut Window,
19348        cx: &mut Context<Self>,
19349    ) -> Task<Result<()>> {
19350        let buffer = self.buffer.clone();
19351        let buffers = buffer.read(cx).all_buffers();
19352        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19353        let apply_action = project.update(cx, |project, cx| {
19354            project.apply_code_action_kind(buffers, kind, true, cx)
19355        });
19356        cx.spawn_in(window, async move |_, cx| {
19357            let transaction = futures::select_biased! {
19358                () = timeout => {
19359                    log::warn!("timed out waiting for executing code action");
19360                    None
19361                }
19362                transaction = apply_action.log_err().fuse() => transaction,
19363            };
19364            buffer.update(cx, |buffer, cx| {
19365                // check if we need this
19366                if let Some(transaction) = transaction
19367                    && !buffer.is_singleton()
19368                {
19369                    buffer.push_transaction(&transaction.0, cx);
19370                }
19371                cx.notify();
19372            });
19373            Ok(())
19374        })
19375    }
19376
19377    pub fn restart_language_server(
19378        &mut self,
19379        _: &RestartLanguageServer,
19380        _: &mut Window,
19381        cx: &mut Context<Self>,
19382    ) {
19383        if let Some(project) = self.project.clone() {
19384            self.buffer.update(cx, |multi_buffer, cx| {
19385                project.update(cx, |project, cx| {
19386                    project.restart_language_servers_for_buffers(
19387                        multi_buffer.all_buffers().into_iter().collect(),
19388                        HashSet::default(),
19389                        cx,
19390                    );
19391                });
19392            })
19393        }
19394    }
19395
19396    pub fn stop_language_server(
19397        &mut self,
19398        _: &StopLanguageServer,
19399        _: &mut Window,
19400        cx: &mut Context<Self>,
19401    ) {
19402        if let Some(project) = self.project.clone() {
19403            self.buffer.update(cx, |multi_buffer, cx| {
19404                project.update(cx, |project, cx| {
19405                    project.stop_language_servers_for_buffers(
19406                        multi_buffer.all_buffers().into_iter().collect(),
19407                        HashSet::default(),
19408                        cx,
19409                    );
19410                });
19411            });
19412        }
19413    }
19414
19415    fn cancel_language_server_work(
19416        workspace: &mut Workspace,
19417        _: &actions::CancelLanguageServerWork,
19418        _: &mut Window,
19419        cx: &mut Context<Workspace>,
19420    ) {
19421        let project = workspace.project();
19422        let buffers = workspace
19423            .active_item(cx)
19424            .and_then(|item| item.act_as::<Editor>(cx))
19425            .map_or(HashSet::default(), |editor| {
19426                editor.read(cx).buffer.read(cx).all_buffers()
19427            });
19428        project.update(cx, |project, cx| {
19429            project.cancel_language_server_work_for_buffers(buffers, cx);
19430        });
19431    }
19432
19433    fn show_character_palette(
19434        &mut self,
19435        _: &ShowCharacterPalette,
19436        window: &mut Window,
19437        _: &mut Context<Self>,
19438    ) {
19439        window.show_character_palette();
19440    }
19441
19442    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19443        if !self.diagnostics_enabled() {
19444            return;
19445        }
19446
19447        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19448            let buffer = self.buffer.read(cx).snapshot(cx);
19449            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19450            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19451            let is_valid = buffer
19452                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19453                .any(|entry| {
19454                    entry.diagnostic.is_primary
19455                        && !entry.range.is_empty()
19456                        && entry.range.start == primary_range_start
19457                        && entry.diagnostic.message == active_diagnostics.active_message
19458                });
19459
19460            if !is_valid {
19461                self.dismiss_diagnostics(cx);
19462            }
19463        }
19464    }
19465
19466    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19467        match &self.active_diagnostics {
19468            ActiveDiagnostic::Group(group) => Some(group),
19469            _ => None,
19470        }
19471    }
19472
19473    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19474        if !self.diagnostics_enabled() {
19475            return;
19476        }
19477        self.dismiss_diagnostics(cx);
19478        self.active_diagnostics = ActiveDiagnostic::All;
19479    }
19480
19481    fn activate_diagnostics(
19482        &mut self,
19483        buffer_id: BufferId,
19484        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19485        window: &mut Window,
19486        cx: &mut Context<Self>,
19487    ) {
19488        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19489            return;
19490        }
19491        self.dismiss_diagnostics(cx);
19492        let snapshot = self.snapshot(window, cx);
19493        let buffer = self.buffer.read(cx).snapshot(cx);
19494        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19495            return;
19496        };
19497
19498        let diagnostic_group = buffer
19499            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19500            .collect::<Vec<_>>();
19501
19502        let language_registry = self
19503            .project()
19504            .map(|project| project.read(cx).languages().clone());
19505
19506        let blocks = renderer.render_group(
19507            diagnostic_group,
19508            buffer_id,
19509            snapshot,
19510            cx.weak_entity(),
19511            language_registry,
19512            cx,
19513        );
19514
19515        let blocks = self.display_map.update(cx, |display_map, cx| {
19516            display_map.insert_blocks(blocks, cx).into_iter().collect()
19517        });
19518        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19519            active_range: buffer.anchor_before(diagnostic.range.start)
19520                ..buffer.anchor_after(diagnostic.range.end),
19521            active_message: diagnostic.diagnostic.message.clone(),
19522            group_id: diagnostic.diagnostic.group_id,
19523            blocks,
19524        });
19525        cx.notify();
19526    }
19527
19528    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19529        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19530            return;
19531        };
19532
19533        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19534        if let ActiveDiagnostic::Group(group) = prev {
19535            self.display_map.update(cx, |display_map, cx| {
19536                display_map.remove_blocks(group.blocks, cx);
19537            });
19538            cx.notify();
19539        }
19540    }
19541
19542    /// Disable inline diagnostics rendering for this editor.
19543    pub fn disable_inline_diagnostics(&mut self) {
19544        self.inline_diagnostics_enabled = false;
19545        self.inline_diagnostics_update = Task::ready(());
19546        self.inline_diagnostics.clear();
19547    }
19548
19549    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19550        self.diagnostics_enabled = false;
19551        self.dismiss_diagnostics(cx);
19552        self.inline_diagnostics_update = Task::ready(());
19553        self.inline_diagnostics.clear();
19554    }
19555
19556    pub fn disable_word_completions(&mut self) {
19557        self.word_completions_enabled = false;
19558    }
19559
19560    pub fn diagnostics_enabled(&self) -> bool {
19561        self.diagnostics_enabled && self.lsp_data_enabled()
19562    }
19563
19564    pub fn inline_diagnostics_enabled(&self) -> bool {
19565        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19566    }
19567
19568    pub fn show_inline_diagnostics(&self) -> bool {
19569        self.show_inline_diagnostics
19570    }
19571
19572    pub fn toggle_inline_diagnostics(
19573        &mut self,
19574        _: &ToggleInlineDiagnostics,
19575        window: &mut Window,
19576        cx: &mut Context<Editor>,
19577    ) {
19578        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19579        self.refresh_inline_diagnostics(false, window, cx);
19580    }
19581
19582    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19583        self.diagnostics_max_severity = severity;
19584        self.display_map.update(cx, |display_map, _| {
19585            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19586        });
19587    }
19588
19589    pub fn toggle_diagnostics(
19590        &mut self,
19591        _: &ToggleDiagnostics,
19592        window: &mut Window,
19593        cx: &mut Context<Editor>,
19594    ) {
19595        if !self.diagnostics_enabled() {
19596            return;
19597        }
19598
19599        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19600            EditorSettings::get_global(cx)
19601                .diagnostics_max_severity
19602                .filter(|severity| severity != &DiagnosticSeverity::Off)
19603                .unwrap_or(DiagnosticSeverity::Hint)
19604        } else {
19605            DiagnosticSeverity::Off
19606        };
19607        self.set_max_diagnostics_severity(new_severity, cx);
19608        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19609            self.active_diagnostics = ActiveDiagnostic::None;
19610            self.inline_diagnostics_update = Task::ready(());
19611            self.inline_diagnostics.clear();
19612        } else {
19613            self.refresh_inline_diagnostics(false, window, cx);
19614        }
19615
19616        cx.notify();
19617    }
19618
19619    pub fn toggle_minimap(
19620        &mut self,
19621        _: &ToggleMinimap,
19622        window: &mut Window,
19623        cx: &mut Context<Editor>,
19624    ) {
19625        if self.supports_minimap(cx) {
19626            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19627        }
19628    }
19629
19630    fn refresh_inline_diagnostics(
19631        &mut self,
19632        debounce: bool,
19633        window: &mut Window,
19634        cx: &mut Context<Self>,
19635    ) {
19636        let max_severity = ProjectSettings::get_global(cx)
19637            .diagnostics
19638            .inline
19639            .max_severity
19640            .unwrap_or(self.diagnostics_max_severity);
19641
19642        if !self.inline_diagnostics_enabled()
19643            || !self.diagnostics_enabled()
19644            || !self.show_inline_diagnostics
19645            || max_severity == DiagnosticSeverity::Off
19646        {
19647            self.inline_diagnostics_update = Task::ready(());
19648            self.inline_diagnostics.clear();
19649            return;
19650        }
19651
19652        let debounce_ms = ProjectSettings::get_global(cx)
19653            .diagnostics
19654            .inline
19655            .update_debounce_ms;
19656        let debounce = if debounce && debounce_ms > 0 {
19657            Some(Duration::from_millis(debounce_ms))
19658        } else {
19659            None
19660        };
19661        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19662            if let Some(debounce) = debounce {
19663                cx.background_executor().timer(debounce).await;
19664            }
19665            let Some(snapshot) = editor.upgrade().map(|editor| {
19666                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19667            }) else {
19668                return;
19669            };
19670
19671            let new_inline_diagnostics = cx
19672                .background_spawn(async move {
19673                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19674                    for diagnostic_entry in
19675                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19676                    {
19677                        let message = diagnostic_entry
19678                            .diagnostic
19679                            .message
19680                            .split_once('\n')
19681                            .map(|(line, _)| line)
19682                            .map(SharedString::new)
19683                            .unwrap_or_else(|| {
19684                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19685                            });
19686                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19687                        let (Ok(i) | Err(i)) = inline_diagnostics
19688                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19689                        inline_diagnostics.insert(
19690                            i,
19691                            (
19692                                start_anchor,
19693                                InlineDiagnostic {
19694                                    message,
19695                                    group_id: diagnostic_entry.diagnostic.group_id,
19696                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19697                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19698                                    severity: diagnostic_entry.diagnostic.severity,
19699                                },
19700                            ),
19701                        );
19702                    }
19703                    inline_diagnostics
19704                })
19705                .await;
19706
19707            editor
19708                .update(cx, |editor, cx| {
19709                    editor.inline_diagnostics = new_inline_diagnostics;
19710                    cx.notify();
19711                })
19712                .ok();
19713        });
19714    }
19715
19716    fn pull_diagnostics(
19717        &mut self,
19718        buffer_id: BufferId,
19719        _window: &Window,
19720        cx: &mut Context<Self>,
19721    ) -> Option<()> {
19722        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19723        // skip any LSP updates for it.
19724
19725        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19726            return None;
19727        }
19728        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19729            .diagnostics
19730            .lsp_pull_diagnostics;
19731        if !pull_diagnostics_settings.enabled {
19732            return None;
19733        }
19734        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19735        let project = self.project()?.downgrade();
19736        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19737
19738        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19739            cx.background_executor().timer(debounce).await;
19740            if let Ok(task) = project.update(cx, |project, cx| {
19741                project.lsp_store().update(cx, |lsp_store, cx| {
19742                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19743                })
19744            }) {
19745                task.await.log_err();
19746            }
19747            project
19748                .update(cx, |project, cx| {
19749                    project.lsp_store().update(cx, |lsp_store, cx| {
19750                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19751                    })
19752                })
19753                .log_err();
19754        });
19755
19756        Some(())
19757    }
19758
19759    pub fn set_selections_from_remote(
19760        &mut self,
19761        selections: Vec<Selection<Anchor>>,
19762        pending_selection: Option<Selection<Anchor>>,
19763        window: &mut Window,
19764        cx: &mut Context<Self>,
19765    ) {
19766        let old_cursor_position = self.selections.newest_anchor().head();
19767        self.selections
19768            .change_with(&self.display_snapshot(cx), |s| {
19769                s.select_anchors(selections);
19770                if let Some(pending_selection) = pending_selection {
19771                    s.set_pending(pending_selection, SelectMode::Character);
19772                } else {
19773                    s.clear_pending();
19774                }
19775            });
19776        self.selections_did_change(
19777            false,
19778            &old_cursor_position,
19779            SelectionEffects::default(),
19780            window,
19781            cx,
19782        );
19783    }
19784
19785    pub fn transact(
19786        &mut self,
19787        window: &mut Window,
19788        cx: &mut Context<Self>,
19789        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19790    ) -> Option<TransactionId> {
19791        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19792            this.start_transaction_at(Instant::now(), window, cx);
19793            update(this, window, cx);
19794            this.end_transaction_at(Instant::now(), cx)
19795        })
19796    }
19797
19798    pub fn start_transaction_at(
19799        &mut self,
19800        now: Instant,
19801        window: &mut Window,
19802        cx: &mut Context<Self>,
19803    ) -> Option<TransactionId> {
19804        self.end_selection(window, cx);
19805        if let Some(tx_id) = self
19806            .buffer
19807            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19808        {
19809            self.selection_history
19810                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19811            cx.emit(EditorEvent::TransactionBegun {
19812                transaction_id: tx_id,
19813            });
19814            Some(tx_id)
19815        } else {
19816            None
19817        }
19818    }
19819
19820    pub fn end_transaction_at(
19821        &mut self,
19822        now: Instant,
19823        cx: &mut Context<Self>,
19824    ) -> Option<TransactionId> {
19825        if let Some(transaction_id) = self
19826            .buffer
19827            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19828        {
19829            if let Some((_, end_selections)) =
19830                self.selection_history.transaction_mut(transaction_id)
19831            {
19832                *end_selections = Some(self.selections.disjoint_anchors_arc());
19833            } else {
19834                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19835            }
19836
19837            cx.emit(EditorEvent::Edited { transaction_id });
19838            Some(transaction_id)
19839        } else {
19840            None
19841        }
19842    }
19843
19844    pub fn modify_transaction_selection_history(
19845        &mut self,
19846        transaction_id: TransactionId,
19847        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19848    ) -> bool {
19849        self.selection_history
19850            .transaction_mut(transaction_id)
19851            .map(modify)
19852            .is_some()
19853    }
19854
19855    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19856        if self.selection_mark_mode {
19857            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19858                s.move_with(&mut |_, sel| {
19859                    sel.collapse_to(sel.head(), SelectionGoal::None);
19860                });
19861            })
19862        }
19863        self.selection_mark_mode = true;
19864        cx.notify();
19865    }
19866
19867    pub fn swap_selection_ends(
19868        &mut self,
19869        _: &actions::SwapSelectionEnds,
19870        window: &mut Window,
19871        cx: &mut Context<Self>,
19872    ) {
19873        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19874            s.move_with(&mut |_, sel| {
19875                if sel.start != sel.end {
19876                    sel.reversed = !sel.reversed
19877                }
19878            });
19879        });
19880        self.request_autoscroll(Autoscroll::newest(), cx);
19881        cx.notify();
19882    }
19883
19884    pub fn toggle_focus(
19885        workspace: &mut Workspace,
19886        _: &actions::ToggleFocus,
19887        window: &mut Window,
19888        cx: &mut Context<Workspace>,
19889    ) {
19890        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19891            return;
19892        };
19893        workspace.activate_item(&item, true, true, window, cx);
19894    }
19895
19896    pub fn toggle_fold(
19897        &mut self,
19898        _: &actions::ToggleFold,
19899        window: &mut Window,
19900        cx: &mut Context<Self>,
19901    ) {
19902        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19903            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19904            let selection = self.selections.newest::<Point>(&display_map);
19905
19906            let range = if selection.is_empty() {
19907                let point = selection.head().to_display_point(&display_map);
19908                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19909                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19910                    .to_point(&display_map);
19911                start..end
19912            } else {
19913                selection.range()
19914            };
19915            if display_map.folds_in_range(range).next().is_some() {
19916                self.unfold_lines(&Default::default(), window, cx)
19917            } else {
19918                self.fold(&Default::default(), window, cx)
19919            }
19920        } else {
19921            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19922            let buffer_ids: HashSet<_> = self
19923                .selections
19924                .disjoint_anchor_ranges()
19925                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19926                .collect();
19927
19928            let should_unfold = buffer_ids
19929                .iter()
19930                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19931
19932            for buffer_id in buffer_ids {
19933                if should_unfold {
19934                    self.unfold_buffer(buffer_id, cx);
19935                } else {
19936                    self.fold_buffer(buffer_id, cx);
19937                }
19938            }
19939        }
19940    }
19941
19942    pub fn toggle_fold_recursive(
19943        &mut self,
19944        _: &actions::ToggleFoldRecursive,
19945        window: &mut Window,
19946        cx: &mut Context<Self>,
19947    ) {
19948        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19949
19950        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19951        let range = if selection.is_empty() {
19952            let point = selection.head().to_display_point(&display_map);
19953            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19954            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19955                .to_point(&display_map);
19956            start..end
19957        } else {
19958            selection.range()
19959        };
19960        if display_map.folds_in_range(range).next().is_some() {
19961            self.unfold_recursive(&Default::default(), window, cx)
19962        } else {
19963            self.fold_recursive(&Default::default(), window, cx)
19964        }
19965    }
19966
19967    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19968        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19969            let mut to_fold = Vec::new();
19970            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19971            let selections = self.selections.all_adjusted(&display_map);
19972
19973            for selection in selections {
19974                let range = selection.range().sorted();
19975                let buffer_start_row = range.start.row;
19976
19977                if range.start.row != range.end.row {
19978                    let mut found = false;
19979                    let mut row = range.start.row;
19980                    while row <= range.end.row {
19981                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19982                        {
19983                            found = true;
19984                            row = crease.range().end.row + 1;
19985                            to_fold.push(crease);
19986                        } else {
19987                            row += 1
19988                        }
19989                    }
19990                    if found {
19991                        continue;
19992                    }
19993                }
19994
19995                for row in (0..=range.start.row).rev() {
19996                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19997                        && crease.range().end.row >= buffer_start_row
19998                    {
19999                        to_fold.push(crease);
20000                        if row <= range.start.row {
20001                            break;
20002                        }
20003                    }
20004                }
20005            }
20006
20007            self.fold_creases(to_fold, true, window, cx);
20008        } else {
20009            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20010            let buffer_ids = self
20011                .selections
20012                .disjoint_anchor_ranges()
20013                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20014                .collect::<HashSet<_>>();
20015            for buffer_id in buffer_ids {
20016                self.fold_buffer(buffer_id, cx);
20017            }
20018        }
20019    }
20020
20021    pub fn toggle_fold_all(
20022        &mut self,
20023        _: &actions::ToggleFoldAll,
20024        window: &mut Window,
20025        cx: &mut Context<Self>,
20026    ) {
20027        let has_folds = if self.buffer.read(cx).is_singleton() {
20028            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20029            let has_folds = display_map
20030                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20031                .next()
20032                .is_some();
20033            has_folds
20034        } else {
20035            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20036            let has_folds = buffer_ids
20037                .iter()
20038                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20039            has_folds
20040        };
20041
20042        if has_folds {
20043            self.unfold_all(&actions::UnfoldAll, window, cx);
20044        } else {
20045            self.fold_all(&actions::FoldAll, window, cx);
20046        }
20047    }
20048
20049    fn fold_at_level(
20050        &mut self,
20051        fold_at: &FoldAtLevel,
20052        window: &mut Window,
20053        cx: &mut Context<Self>,
20054    ) {
20055        if !self.buffer.read(cx).is_singleton() {
20056            return;
20057        }
20058
20059        let fold_at_level = fold_at.0;
20060        let snapshot = self.buffer.read(cx).snapshot(cx);
20061        let mut to_fold = Vec::new();
20062        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20063
20064        let row_ranges_to_keep: Vec<Range<u32>> = self
20065            .selections
20066            .all::<Point>(&self.display_snapshot(cx))
20067            .into_iter()
20068            .map(|sel| sel.start.row..sel.end.row)
20069            .collect();
20070
20071        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20072            while start_row < end_row {
20073                match self
20074                    .snapshot(window, cx)
20075                    .crease_for_buffer_row(MultiBufferRow(start_row))
20076                {
20077                    Some(crease) => {
20078                        let nested_start_row = crease.range().start.row + 1;
20079                        let nested_end_row = crease.range().end.row;
20080
20081                        if current_level < fold_at_level {
20082                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20083                        } else if current_level == fold_at_level {
20084                            // Fold iff there is no selection completely contained within the fold region
20085                            if !row_ranges_to_keep.iter().any(|selection| {
20086                                selection.end >= nested_start_row
20087                                    && selection.start <= nested_end_row
20088                            }) {
20089                                to_fold.push(crease);
20090                            }
20091                        }
20092
20093                        start_row = nested_end_row + 1;
20094                    }
20095                    None => start_row += 1,
20096                }
20097            }
20098        }
20099
20100        self.fold_creases(to_fold, true, window, cx);
20101    }
20102
20103    pub fn fold_at_level_1(
20104        &mut self,
20105        _: &actions::FoldAtLevel1,
20106        window: &mut Window,
20107        cx: &mut Context<Self>,
20108    ) {
20109        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20110    }
20111
20112    pub fn fold_at_level_2(
20113        &mut self,
20114        _: &actions::FoldAtLevel2,
20115        window: &mut Window,
20116        cx: &mut Context<Self>,
20117    ) {
20118        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20119    }
20120
20121    pub fn fold_at_level_3(
20122        &mut self,
20123        _: &actions::FoldAtLevel3,
20124        window: &mut Window,
20125        cx: &mut Context<Self>,
20126    ) {
20127        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20128    }
20129
20130    pub fn fold_at_level_4(
20131        &mut self,
20132        _: &actions::FoldAtLevel4,
20133        window: &mut Window,
20134        cx: &mut Context<Self>,
20135    ) {
20136        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20137    }
20138
20139    pub fn fold_at_level_5(
20140        &mut self,
20141        _: &actions::FoldAtLevel5,
20142        window: &mut Window,
20143        cx: &mut Context<Self>,
20144    ) {
20145        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20146    }
20147
20148    pub fn fold_at_level_6(
20149        &mut self,
20150        _: &actions::FoldAtLevel6,
20151        window: &mut Window,
20152        cx: &mut Context<Self>,
20153    ) {
20154        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20155    }
20156
20157    pub fn fold_at_level_7(
20158        &mut self,
20159        _: &actions::FoldAtLevel7,
20160        window: &mut Window,
20161        cx: &mut Context<Self>,
20162    ) {
20163        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20164    }
20165
20166    pub fn fold_at_level_8(
20167        &mut self,
20168        _: &actions::FoldAtLevel8,
20169        window: &mut Window,
20170        cx: &mut Context<Self>,
20171    ) {
20172        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20173    }
20174
20175    pub fn fold_at_level_9(
20176        &mut self,
20177        _: &actions::FoldAtLevel9,
20178        window: &mut Window,
20179        cx: &mut Context<Self>,
20180    ) {
20181        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20182    }
20183
20184    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20185        if self.buffer.read(cx).is_singleton() {
20186            let mut fold_ranges = Vec::new();
20187            let snapshot = self.buffer.read(cx).snapshot(cx);
20188
20189            for row in 0..snapshot.max_row().0 {
20190                if let Some(foldable_range) = self
20191                    .snapshot(window, cx)
20192                    .crease_for_buffer_row(MultiBufferRow(row))
20193                {
20194                    fold_ranges.push(foldable_range);
20195                }
20196            }
20197
20198            self.fold_creases(fold_ranges, true, window, cx);
20199        } else {
20200            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20201                editor
20202                    .update_in(cx, |editor, _, cx| {
20203                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20204                            editor.fold_buffer(buffer_id, cx);
20205                        }
20206                    })
20207                    .ok();
20208            });
20209        }
20210    }
20211
20212    pub fn fold_function_bodies(
20213        &mut self,
20214        _: &actions::FoldFunctionBodies,
20215        window: &mut Window,
20216        cx: &mut Context<Self>,
20217    ) {
20218        let snapshot = self.buffer.read(cx).snapshot(cx);
20219
20220        let ranges = snapshot
20221            .text_object_ranges(
20222                MultiBufferOffset(0)..snapshot.len(),
20223                TreeSitterOptions::default(),
20224            )
20225            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20226            .collect::<Vec<_>>();
20227
20228        let creases = ranges
20229            .into_iter()
20230            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20231            .collect();
20232
20233        self.fold_creases(creases, true, window, cx);
20234    }
20235
20236    pub fn fold_recursive(
20237        &mut self,
20238        _: &actions::FoldRecursive,
20239        window: &mut Window,
20240        cx: &mut Context<Self>,
20241    ) {
20242        let mut to_fold = Vec::new();
20243        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20244        let selections = self.selections.all_adjusted(&display_map);
20245
20246        for selection in selections {
20247            let range = selection.range().sorted();
20248            let buffer_start_row = range.start.row;
20249
20250            if range.start.row != range.end.row {
20251                let mut found = false;
20252                for row in range.start.row..=range.end.row {
20253                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20254                        found = true;
20255                        to_fold.push(crease);
20256                    }
20257                }
20258                if found {
20259                    continue;
20260                }
20261            }
20262
20263            for row in (0..=range.start.row).rev() {
20264                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20265                    if crease.range().end.row >= buffer_start_row {
20266                        to_fold.push(crease);
20267                    } else {
20268                        break;
20269                    }
20270                }
20271            }
20272        }
20273
20274        self.fold_creases(to_fold, true, window, cx);
20275    }
20276
20277    pub fn fold_at(
20278        &mut self,
20279        buffer_row: MultiBufferRow,
20280        window: &mut Window,
20281        cx: &mut Context<Self>,
20282    ) {
20283        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20284
20285        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20286            let autoscroll = self
20287                .selections
20288                .all::<Point>(&display_map)
20289                .iter()
20290                .any(|selection| crease.range().overlaps(&selection.range()));
20291
20292            self.fold_creases(vec![crease], autoscroll, window, cx);
20293        }
20294    }
20295
20296    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20297        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20298            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20299            let buffer = display_map.buffer_snapshot();
20300            let selections = self.selections.all::<Point>(&display_map);
20301            let ranges = selections
20302                .iter()
20303                .map(|s| {
20304                    let range = s.display_range(&display_map).sorted();
20305                    let mut start = range.start.to_point(&display_map);
20306                    let mut end = range.end.to_point(&display_map);
20307                    start.column = 0;
20308                    end.column = buffer.line_len(MultiBufferRow(end.row));
20309                    start..end
20310                })
20311                .collect::<Vec<_>>();
20312
20313            self.unfold_ranges(&ranges, true, true, cx);
20314        } else {
20315            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20316            let buffer_ids = self
20317                .selections
20318                .disjoint_anchor_ranges()
20319                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20320                .collect::<HashSet<_>>();
20321            for buffer_id in buffer_ids {
20322                self.unfold_buffer(buffer_id, cx);
20323            }
20324        }
20325    }
20326
20327    pub fn unfold_recursive(
20328        &mut self,
20329        _: &UnfoldRecursive,
20330        _window: &mut Window,
20331        cx: &mut Context<Self>,
20332    ) {
20333        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20334        let selections = self.selections.all::<Point>(&display_map);
20335        let ranges = selections
20336            .iter()
20337            .map(|s| {
20338                let mut range = s.display_range(&display_map).sorted();
20339                *range.start.column_mut() = 0;
20340                *range.end.column_mut() = display_map.line_len(range.end.row());
20341                let start = range.start.to_point(&display_map);
20342                let end = range.end.to_point(&display_map);
20343                start..end
20344            })
20345            .collect::<Vec<_>>();
20346
20347        self.unfold_ranges(&ranges, true, true, cx);
20348    }
20349
20350    pub fn unfold_at(
20351        &mut self,
20352        buffer_row: MultiBufferRow,
20353        _window: &mut Window,
20354        cx: &mut Context<Self>,
20355    ) {
20356        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20357
20358        let intersection_range = Point::new(buffer_row.0, 0)
20359            ..Point::new(
20360                buffer_row.0,
20361                display_map.buffer_snapshot().line_len(buffer_row),
20362            );
20363
20364        let autoscroll = self
20365            .selections
20366            .all::<Point>(&display_map)
20367            .iter()
20368            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20369
20370        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20371    }
20372
20373    pub fn unfold_all(
20374        &mut self,
20375        _: &actions::UnfoldAll,
20376        _window: &mut Window,
20377        cx: &mut Context<Self>,
20378    ) {
20379        if self.buffer.read(cx).is_singleton() {
20380            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20381            self.unfold_ranges(
20382                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20383                true,
20384                true,
20385                cx,
20386            );
20387        } else {
20388            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20389                editor
20390                    .update(cx, |editor, cx| {
20391                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20392                            editor.unfold_buffer(buffer_id, cx);
20393                        }
20394                    })
20395                    .ok();
20396            });
20397        }
20398    }
20399
20400    pub fn fold_selected_ranges(
20401        &mut self,
20402        _: &FoldSelectedRanges,
20403        window: &mut Window,
20404        cx: &mut Context<Self>,
20405    ) {
20406        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20407        let selections = self.selections.all_adjusted(&display_map);
20408        let ranges = selections
20409            .into_iter()
20410            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20411            .collect::<Vec<_>>();
20412        self.fold_creases(ranges, true, window, cx);
20413    }
20414
20415    pub fn fold_ranges<T: ToOffset + Clone>(
20416        &mut self,
20417        ranges: Vec<Range<T>>,
20418        auto_scroll: bool,
20419        window: &mut Window,
20420        cx: &mut Context<Self>,
20421    ) {
20422        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20423        let ranges = ranges
20424            .into_iter()
20425            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20426            .collect::<Vec<_>>();
20427        self.fold_creases(ranges, auto_scroll, window, cx);
20428    }
20429
20430    pub fn fold_creases<T: ToOffset + Clone>(
20431        &mut self,
20432        creases: Vec<Crease<T>>,
20433        auto_scroll: bool,
20434        window: &mut Window,
20435        cx: &mut Context<Self>,
20436    ) {
20437        if creases.is_empty() {
20438            return;
20439        }
20440
20441        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20442
20443        if auto_scroll {
20444            self.request_autoscroll(Autoscroll::fit(), cx);
20445        }
20446
20447        cx.notify();
20448
20449        self.scrollbar_marker_state.dirty = true;
20450        self.update_data_on_scroll(window, cx);
20451        self.folds_did_change(cx);
20452    }
20453
20454    /// Removes any folds whose ranges intersect any of the given ranges.
20455    pub fn unfold_ranges<T: ToOffset + Clone>(
20456        &mut self,
20457        ranges: &[Range<T>],
20458        inclusive: bool,
20459        auto_scroll: bool,
20460        cx: &mut Context<Self>,
20461    ) {
20462        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20463            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20464        });
20465        self.folds_did_change(cx);
20466    }
20467
20468    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20469        self.fold_buffers([buffer_id], cx);
20470    }
20471
20472    pub fn fold_buffers(
20473        &mut self,
20474        buffer_ids: impl IntoIterator<Item = BufferId>,
20475        cx: &mut Context<Self>,
20476    ) {
20477        if self.buffer().read(cx).is_singleton() {
20478            return;
20479        }
20480
20481        let ids_to_fold: Vec<BufferId> = buffer_ids
20482            .into_iter()
20483            .filter(|id| !self.is_buffer_folded(*id, cx))
20484            .collect();
20485
20486        if ids_to_fold.is_empty() {
20487            return;
20488        }
20489
20490        let mut all_folded_excerpt_ids = Vec::new();
20491        for buffer_id in &ids_to_fold {
20492            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20493            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20494        }
20495
20496        self.display_map.update(cx, |display_map, cx| {
20497            display_map.fold_buffers(ids_to_fold.clone(), cx)
20498        });
20499
20500        let snapshot = self.display_snapshot(cx);
20501        self.selections.change_with(&snapshot, |selections| {
20502            for buffer_id in ids_to_fold {
20503                selections.remove_selections_from_buffer(buffer_id);
20504            }
20505        });
20506
20507        cx.emit(EditorEvent::BufferFoldToggled {
20508            ids: all_folded_excerpt_ids,
20509            folded: true,
20510        });
20511        cx.notify();
20512    }
20513
20514    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20515        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20516            return;
20517        }
20518        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20519        self.display_map.update(cx, |display_map, cx| {
20520            display_map.unfold_buffers([buffer_id], cx);
20521        });
20522        cx.emit(EditorEvent::BufferFoldToggled {
20523            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20524            folded: false,
20525        });
20526        cx.notify();
20527    }
20528
20529    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20530        self.display_map.read(cx).is_buffer_folded(buffer)
20531    }
20532
20533    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20534        if self.buffer().read(cx).is_singleton() {
20535            return false;
20536        }
20537        !self.folded_buffers(cx).is_empty()
20538    }
20539
20540    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20541        self.display_map.read(cx).folded_buffers()
20542    }
20543
20544    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20545        self.display_map.update(cx, |display_map, cx| {
20546            display_map.disable_header_for_buffer(buffer_id, cx);
20547        });
20548        cx.notify();
20549    }
20550
20551    /// Removes any folds with the given ranges.
20552    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20553        &mut self,
20554        ranges: &[Range<T>],
20555        type_id: TypeId,
20556        auto_scroll: bool,
20557        cx: &mut Context<Self>,
20558    ) {
20559        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20560            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20561        });
20562        self.folds_did_change(cx);
20563    }
20564
20565    fn remove_folds_with<T: ToOffset + Clone>(
20566        &mut self,
20567        ranges: &[Range<T>],
20568        auto_scroll: bool,
20569        cx: &mut Context<Self>,
20570        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20571    ) {
20572        if ranges.is_empty() {
20573            return;
20574        }
20575
20576        let mut buffers_affected = HashSet::default();
20577        let multi_buffer = self.buffer().read(cx);
20578        for range in ranges {
20579            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20580                buffers_affected.insert(buffer.read(cx).remote_id());
20581            };
20582        }
20583
20584        self.display_map.update(cx, update);
20585
20586        if auto_scroll {
20587            self.request_autoscroll(Autoscroll::fit(), cx);
20588        }
20589
20590        cx.notify();
20591        self.scrollbar_marker_state.dirty = true;
20592        self.active_indent_guides_state.dirty = true;
20593    }
20594
20595    pub fn update_renderer_widths(
20596        &mut self,
20597        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20598        cx: &mut Context<Self>,
20599    ) -> bool {
20600        self.display_map
20601            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20602    }
20603
20604    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20605        self.display_map.read(cx).fold_placeholder.clone()
20606    }
20607
20608    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20609        self.buffer.update(cx, |buffer, cx| {
20610            buffer.set_all_diff_hunks_expanded(cx);
20611        });
20612    }
20613
20614    pub fn expand_all_diff_hunks(
20615        &mut self,
20616        _: &ExpandAllDiffHunks,
20617        _window: &mut Window,
20618        cx: &mut Context<Self>,
20619    ) {
20620        self.buffer.update(cx, |buffer, cx| {
20621            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20622        });
20623    }
20624
20625    pub fn collapse_all_diff_hunks(
20626        &mut self,
20627        _: &CollapseAllDiffHunks,
20628        _window: &mut Window,
20629        cx: &mut Context<Self>,
20630    ) {
20631        self.buffer.update(cx, |buffer, cx| {
20632            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20633        });
20634    }
20635
20636    pub fn toggle_selected_diff_hunks(
20637        &mut self,
20638        _: &ToggleSelectedDiffHunks,
20639        _window: &mut Window,
20640        cx: &mut Context<Self>,
20641    ) {
20642        let ranges: Vec<_> = self
20643            .selections
20644            .disjoint_anchors()
20645            .iter()
20646            .map(|s| s.range())
20647            .collect();
20648        self.toggle_diff_hunks_in_ranges(ranges, cx);
20649    }
20650
20651    pub fn diff_hunks_in_ranges<'a>(
20652        &'a self,
20653        ranges: &'a [Range<Anchor>],
20654        buffer: &'a MultiBufferSnapshot,
20655    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20656        ranges.iter().flat_map(move |range| {
20657            let end_excerpt_id = range.end.excerpt_id;
20658            let range = range.to_point(buffer);
20659            let mut peek_end = range.end;
20660            if range.end.row < buffer.max_row().0 {
20661                peek_end = Point::new(range.end.row + 1, 0);
20662            }
20663            buffer
20664                .diff_hunks_in_range(range.start..peek_end)
20665                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20666        })
20667    }
20668
20669    pub fn has_stageable_diff_hunks_in_ranges(
20670        &self,
20671        ranges: &[Range<Anchor>],
20672        snapshot: &MultiBufferSnapshot,
20673    ) -> bool {
20674        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20675        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20676    }
20677
20678    pub fn toggle_staged_selected_diff_hunks(
20679        &mut self,
20680        _: &::git::ToggleStaged,
20681        _: &mut Window,
20682        cx: &mut Context<Self>,
20683    ) {
20684        let snapshot = self.buffer.read(cx).snapshot(cx);
20685        let ranges: Vec<_> = self
20686            .selections
20687            .disjoint_anchors()
20688            .iter()
20689            .map(|s| s.range())
20690            .collect();
20691        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20692        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20693    }
20694
20695    pub fn set_render_diff_hunk_controls(
20696        &mut self,
20697        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20698        cx: &mut Context<Self>,
20699    ) {
20700        self.render_diff_hunk_controls = render_diff_hunk_controls;
20701        cx.notify();
20702    }
20703
20704    pub fn stage_and_next(
20705        &mut self,
20706        _: &::git::StageAndNext,
20707        window: &mut Window,
20708        cx: &mut Context<Self>,
20709    ) {
20710        self.do_stage_or_unstage_and_next(true, window, cx);
20711    }
20712
20713    pub fn unstage_and_next(
20714        &mut self,
20715        _: &::git::UnstageAndNext,
20716        window: &mut Window,
20717        cx: &mut Context<Self>,
20718    ) {
20719        self.do_stage_or_unstage_and_next(false, window, cx);
20720    }
20721
20722    pub fn stage_or_unstage_diff_hunks(
20723        &mut self,
20724        stage: bool,
20725        ranges: Vec<Range<Anchor>>,
20726        cx: &mut Context<Self>,
20727    ) {
20728        if self.delegate_stage_and_restore {
20729            let snapshot = self.buffer.read(cx).snapshot(cx);
20730            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20731            if !hunks.is_empty() {
20732                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20733            }
20734            return;
20735        }
20736        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20737        cx.spawn(async move |this, cx| {
20738            task.await?;
20739            this.update(cx, |this, cx| {
20740                let snapshot = this.buffer.read(cx).snapshot(cx);
20741                let chunk_by = this
20742                    .diff_hunks_in_ranges(&ranges, &snapshot)
20743                    .chunk_by(|hunk| hunk.buffer_id);
20744                for (buffer_id, hunks) in &chunk_by {
20745                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20746                }
20747            })
20748        })
20749        .detach_and_log_err(cx);
20750    }
20751
20752    fn save_buffers_for_ranges_if_needed(
20753        &mut self,
20754        ranges: &[Range<Anchor>],
20755        cx: &mut Context<Editor>,
20756    ) -> Task<Result<()>> {
20757        let multibuffer = self.buffer.read(cx);
20758        let snapshot = multibuffer.read(cx);
20759        let buffer_ids: HashSet<_> = ranges
20760            .iter()
20761            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20762            .collect();
20763        drop(snapshot);
20764
20765        let mut buffers = HashSet::default();
20766        for buffer_id in buffer_ids {
20767            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20768                let buffer = buffer_entity.read(cx);
20769                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20770                {
20771                    buffers.insert(buffer_entity);
20772                }
20773            }
20774        }
20775
20776        if let Some(project) = &self.project {
20777            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20778        } else {
20779            Task::ready(Ok(()))
20780        }
20781    }
20782
20783    fn do_stage_or_unstage_and_next(
20784        &mut self,
20785        stage: bool,
20786        window: &mut Window,
20787        cx: &mut Context<Self>,
20788    ) {
20789        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20790
20791        if ranges.iter().any(|range| range.start != range.end) {
20792            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20793            return;
20794        }
20795
20796        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20797
20798        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20799        let wrap_around = !all_diff_hunks_expanded;
20800        let snapshot = self.snapshot(window, cx);
20801        let position = self
20802            .selections
20803            .newest::<Point>(&snapshot.display_snapshot)
20804            .head();
20805
20806        self.go_to_hunk_before_or_after_position(
20807            &snapshot,
20808            position,
20809            Direction::Next,
20810            wrap_around,
20811            window,
20812            cx,
20813        );
20814    }
20815
20816    pub(crate) fn do_stage_or_unstage(
20817        &self,
20818        stage: bool,
20819        buffer_id: BufferId,
20820        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20821        cx: &mut App,
20822    ) -> Option<()> {
20823        let project = self.project()?;
20824        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20825        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20826        let buffer_snapshot = buffer.read(cx).snapshot();
20827        let file_exists = buffer_snapshot
20828            .file()
20829            .is_some_and(|file| file.disk_state().exists());
20830        diff.update(cx, |diff, cx| {
20831            diff.stage_or_unstage_hunks(
20832                stage,
20833                &hunks
20834                    .map(|hunk| buffer_diff::DiffHunk {
20835                        buffer_range: hunk.buffer_range,
20836                        // We don't need to pass in word diffs here because they're only used for rendering and
20837                        // this function changes internal state
20838                        base_word_diffs: Vec::default(),
20839                        buffer_word_diffs: Vec::default(),
20840                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20841                            ..hunk.diff_base_byte_range.end.0,
20842                        secondary_status: hunk.status.secondary,
20843                        range: Point::zero()..Point::zero(), // unused
20844                    })
20845                    .collect::<Vec<_>>(),
20846                &buffer_snapshot,
20847                file_exists,
20848                cx,
20849            )
20850        });
20851        None
20852    }
20853
20854    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20855        let ranges: Vec<_> = self
20856            .selections
20857            .disjoint_anchors()
20858            .iter()
20859            .map(|s| s.range())
20860            .collect();
20861        self.buffer
20862            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20863    }
20864
20865    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20866        self.buffer.update(cx, |buffer, cx| {
20867            let ranges = vec![Anchor::min()..Anchor::max()];
20868            if !buffer.all_diff_hunks_expanded()
20869                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20870            {
20871                buffer.collapse_diff_hunks(ranges, cx);
20872                true
20873            } else {
20874                false
20875            }
20876        })
20877    }
20878
20879    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20880        if self.buffer.read(cx).all_diff_hunks_expanded() {
20881            return true;
20882        }
20883        let ranges = vec![Anchor::min()..Anchor::max()];
20884        self.buffer
20885            .read(cx)
20886            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20887    }
20888
20889    fn toggle_diff_hunks_in_ranges(
20890        &mut self,
20891        ranges: Vec<Range<Anchor>>,
20892        cx: &mut Context<Editor>,
20893    ) {
20894        self.buffer.update(cx, |buffer, cx| {
20895            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20896            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20897        })
20898    }
20899
20900    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20901        self.buffer.update(cx, |buffer, cx| {
20902            buffer.toggle_single_diff_hunk(range, cx);
20903        })
20904    }
20905
20906    pub(crate) fn apply_all_diff_hunks(
20907        &mut self,
20908        _: &ApplyAllDiffHunks,
20909        window: &mut Window,
20910        cx: &mut Context<Self>,
20911    ) {
20912        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20913
20914        let buffers = self.buffer.read(cx).all_buffers();
20915        for branch_buffer in buffers {
20916            branch_buffer.update(cx, |branch_buffer, cx| {
20917                branch_buffer.merge_into_base(Vec::new(), cx);
20918            });
20919        }
20920
20921        if let Some(project) = self.project.clone() {
20922            self.save(
20923                SaveOptions {
20924                    format: true,
20925                    autosave: false,
20926                },
20927                project,
20928                window,
20929                cx,
20930            )
20931            .detach_and_log_err(cx);
20932        }
20933    }
20934
20935    pub(crate) fn apply_selected_diff_hunks(
20936        &mut self,
20937        _: &ApplyDiffHunk,
20938        window: &mut Window,
20939        cx: &mut Context<Self>,
20940    ) {
20941        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20942        let snapshot = self.snapshot(window, cx);
20943        let hunks = snapshot.hunks_for_ranges(
20944            self.selections
20945                .all(&snapshot.display_snapshot)
20946                .into_iter()
20947                .map(|selection| selection.range()),
20948        );
20949        let mut ranges_by_buffer = HashMap::default();
20950        self.transact(window, cx, |editor, _window, cx| {
20951            for hunk in hunks {
20952                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20953                    ranges_by_buffer
20954                        .entry(buffer.clone())
20955                        .or_insert_with(Vec::new)
20956                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20957                }
20958            }
20959
20960            for (buffer, ranges) in ranges_by_buffer {
20961                buffer.update(cx, |buffer, cx| {
20962                    buffer.merge_into_base(ranges, cx);
20963                });
20964            }
20965        });
20966
20967        if let Some(project) = self.project.clone() {
20968            self.save(
20969                SaveOptions {
20970                    format: true,
20971                    autosave: false,
20972                },
20973                project,
20974                window,
20975                cx,
20976            )
20977            .detach_and_log_err(cx);
20978        }
20979    }
20980
20981    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20982        if hovered != self.gutter_hovered {
20983            self.gutter_hovered = hovered;
20984            cx.notify();
20985        }
20986    }
20987
20988    pub fn insert_blocks(
20989        &mut self,
20990        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20991        autoscroll: Option<Autoscroll>,
20992        cx: &mut Context<Self>,
20993    ) -> Vec<CustomBlockId> {
20994        let blocks = self
20995            .display_map
20996            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20997        if let Some(autoscroll) = autoscroll {
20998            self.request_autoscroll(autoscroll, cx);
20999        }
21000        cx.notify();
21001        blocks
21002    }
21003
21004    pub fn resize_blocks(
21005        &mut self,
21006        heights: HashMap<CustomBlockId, u32>,
21007        autoscroll: Option<Autoscroll>,
21008        cx: &mut Context<Self>,
21009    ) {
21010        self.display_map
21011            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21012        if let Some(autoscroll) = autoscroll {
21013            self.request_autoscroll(autoscroll, cx);
21014        }
21015        cx.notify();
21016    }
21017
21018    pub fn replace_blocks(
21019        &mut self,
21020        renderers: HashMap<CustomBlockId, RenderBlock>,
21021        autoscroll: Option<Autoscroll>,
21022        cx: &mut Context<Self>,
21023    ) {
21024        self.display_map
21025            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21026        if let Some(autoscroll) = autoscroll {
21027            self.request_autoscroll(autoscroll, cx);
21028        }
21029        cx.notify();
21030    }
21031
21032    pub fn remove_blocks(
21033        &mut self,
21034        block_ids: HashSet<CustomBlockId>,
21035        autoscroll: Option<Autoscroll>,
21036        cx: &mut Context<Self>,
21037    ) {
21038        self.display_map.update(cx, |display_map, cx| {
21039            display_map.remove_blocks(block_ids, cx)
21040        });
21041        if let Some(autoscroll) = autoscroll {
21042            self.request_autoscroll(autoscroll, cx);
21043        }
21044        cx.notify();
21045    }
21046
21047    pub fn row_for_block(
21048        &self,
21049        block_id: CustomBlockId,
21050        cx: &mut Context<Self>,
21051    ) -> Option<DisplayRow> {
21052        self.display_map
21053            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21054    }
21055
21056    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21057        self.focused_block = Some(focused_block);
21058    }
21059
21060    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21061        self.focused_block.take()
21062    }
21063
21064    pub fn insert_creases(
21065        &mut self,
21066        creases: impl IntoIterator<Item = Crease<Anchor>>,
21067        cx: &mut Context<Self>,
21068    ) -> Vec<CreaseId> {
21069        self.display_map
21070            .update(cx, |map, cx| map.insert_creases(creases, cx))
21071    }
21072
21073    pub fn remove_creases(
21074        &mut self,
21075        ids: impl IntoIterator<Item = CreaseId>,
21076        cx: &mut Context<Self>,
21077    ) -> Vec<(CreaseId, Range<Anchor>)> {
21078        self.display_map
21079            .update(cx, |map, cx| map.remove_creases(ids, cx))
21080    }
21081
21082    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21083        self.display_map
21084            .update(cx, |map, cx| map.snapshot(cx))
21085            .longest_row()
21086    }
21087
21088    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21089        self.display_map
21090            .update(cx, |map, cx| map.snapshot(cx))
21091            .max_point()
21092    }
21093
21094    pub fn text(&self, cx: &App) -> String {
21095        self.buffer.read(cx).read(cx).text()
21096    }
21097
21098    pub fn is_empty(&self, cx: &App) -> bool {
21099        self.buffer.read(cx).read(cx).is_empty()
21100    }
21101
21102    pub fn text_option(&self, cx: &App) -> Option<String> {
21103        let text = self.text(cx);
21104        let text = text.trim();
21105
21106        if text.is_empty() {
21107            return None;
21108        }
21109
21110        Some(text.to_string())
21111    }
21112
21113    pub fn set_text(
21114        &mut self,
21115        text: impl Into<Arc<str>>,
21116        window: &mut Window,
21117        cx: &mut Context<Self>,
21118    ) {
21119        self.transact(window, cx, |this, _, cx| {
21120            this.buffer
21121                .read(cx)
21122                .as_singleton()
21123                .expect("you can only call set_text on editors for singleton buffers")
21124                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21125        });
21126    }
21127
21128    pub fn display_text(&self, cx: &mut App) -> String {
21129        self.display_map
21130            .update(cx, |map, cx| map.snapshot(cx))
21131            .text()
21132    }
21133
21134    fn create_minimap(
21135        &self,
21136        minimap_settings: MinimapSettings,
21137        window: &mut Window,
21138        cx: &mut Context<Self>,
21139    ) -> Option<Entity<Self>> {
21140        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21141            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21142    }
21143
21144    fn initialize_new_minimap(
21145        &self,
21146        minimap_settings: MinimapSettings,
21147        window: &mut Window,
21148        cx: &mut Context<Self>,
21149    ) -> Entity<Self> {
21150        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21151        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21152
21153        let mut minimap = Editor::new_internal(
21154            EditorMode::Minimap {
21155                parent: cx.weak_entity(),
21156            },
21157            self.buffer.clone(),
21158            None,
21159            Some(self.display_map.clone()),
21160            window,
21161            cx,
21162        );
21163        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21164        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21165        minimap.scroll_manager.clone_state(
21166            &self.scroll_manager,
21167            &my_snapshot,
21168            &minimap_snapshot,
21169            cx,
21170        );
21171        minimap.set_text_style_refinement(TextStyleRefinement {
21172            font_size: Some(MINIMAP_FONT_SIZE),
21173            font_weight: Some(MINIMAP_FONT_WEIGHT),
21174            font_family: Some(MINIMAP_FONT_FAMILY),
21175            ..Default::default()
21176        });
21177        minimap.update_minimap_configuration(minimap_settings, cx);
21178        cx.new(|_| minimap)
21179    }
21180
21181    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21182        let current_line_highlight = minimap_settings
21183            .current_line_highlight
21184            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21185        self.set_current_line_highlight(Some(current_line_highlight));
21186    }
21187
21188    pub fn minimap(&self) -> Option<&Entity<Self>> {
21189        self.minimap
21190            .as_ref()
21191            .filter(|_| self.minimap_visibility.visible())
21192    }
21193
21194    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21195        let mut wrap_guides = smallvec![];
21196
21197        if self.show_wrap_guides == Some(false) {
21198            return wrap_guides;
21199        }
21200
21201        let settings = self.buffer.read(cx).language_settings(cx);
21202        if settings.show_wrap_guides {
21203            match self.soft_wrap_mode(cx) {
21204                SoftWrap::Column(soft_wrap) => {
21205                    wrap_guides.push((soft_wrap as usize, true));
21206                }
21207                SoftWrap::Bounded(soft_wrap) => {
21208                    wrap_guides.push((soft_wrap as usize, true));
21209                }
21210                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21211            }
21212            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21213        }
21214
21215        wrap_guides
21216    }
21217
21218    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21219        let settings = self.buffer.read(cx).language_settings(cx);
21220        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21221        match mode {
21222            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21223                SoftWrap::None
21224            }
21225            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21226            language_settings::SoftWrap::PreferredLineLength => {
21227                SoftWrap::Column(settings.preferred_line_length)
21228            }
21229            language_settings::SoftWrap::Bounded => {
21230                SoftWrap::Bounded(settings.preferred_line_length)
21231            }
21232        }
21233    }
21234
21235    pub fn set_soft_wrap_mode(
21236        &mut self,
21237        mode: language_settings::SoftWrap,
21238        cx: &mut Context<Self>,
21239    ) {
21240        self.soft_wrap_mode_override = Some(mode);
21241        cx.notify();
21242    }
21243
21244    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21245        self.hard_wrap = hard_wrap;
21246        cx.notify();
21247    }
21248
21249    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21250        self.text_style_refinement = Some(style);
21251    }
21252
21253    /// called by the Element so we know what style we were most recently rendered with.
21254    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21255        // We intentionally do not inform the display map about the minimap style
21256        // so that wrapping is not recalculated and stays consistent for the editor
21257        // and its linked minimap.
21258        if !self.mode.is_minimap() {
21259            let font = style.text.font();
21260            let font_size = style.text.font_size.to_pixels(window.rem_size());
21261            let display_map = self
21262                .placeholder_display_map
21263                .as_ref()
21264                .filter(|_| self.is_empty(cx))
21265                .unwrap_or(&self.display_map);
21266
21267            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21268        }
21269        self.style = Some(style);
21270    }
21271
21272    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21273        if self.style.is_none() {
21274            self.style = Some(self.create_style(cx));
21275        }
21276        self.style.as_ref().unwrap()
21277    }
21278
21279    // Called by the element. This method is not designed to be called outside of the editor
21280    // element's layout code because it does not notify when rewrapping is computed synchronously.
21281    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21282        if self.is_empty(cx) {
21283            self.placeholder_display_map
21284                .as_ref()
21285                .map_or(false, |display_map| {
21286                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21287                })
21288        } else {
21289            self.display_map
21290                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21291        }
21292    }
21293
21294    pub fn set_soft_wrap(&mut self) {
21295        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21296    }
21297
21298    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21299        if self.soft_wrap_mode_override.is_some() {
21300            self.soft_wrap_mode_override.take();
21301        } else {
21302            let soft_wrap = match self.soft_wrap_mode(cx) {
21303                SoftWrap::GitDiff => return,
21304                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21305                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21306                    language_settings::SoftWrap::None
21307                }
21308            };
21309            self.soft_wrap_mode_override = Some(soft_wrap);
21310        }
21311        cx.notify();
21312    }
21313
21314    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21315        let Some(workspace) = self.workspace() else {
21316            return;
21317        };
21318        let fs = workspace.read(cx).app_state().fs.clone();
21319        let current_show = TabBarSettings::get_global(cx).show;
21320        update_settings_file(fs, cx, move |setting, _| {
21321            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21322        });
21323    }
21324
21325    pub fn toggle_indent_guides(
21326        &mut self,
21327        _: &ToggleIndentGuides,
21328        _: &mut Window,
21329        cx: &mut Context<Self>,
21330    ) {
21331        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21332            self.buffer
21333                .read(cx)
21334                .language_settings(cx)
21335                .indent_guides
21336                .enabled
21337        });
21338        self.show_indent_guides = Some(!currently_enabled);
21339        cx.notify();
21340    }
21341
21342    fn should_show_indent_guides(&self) -> Option<bool> {
21343        self.show_indent_guides
21344    }
21345
21346    pub fn disable_indent_guides_for_buffer(
21347        &mut self,
21348        buffer_id: BufferId,
21349        cx: &mut Context<Self>,
21350    ) {
21351        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21352        cx.notify();
21353    }
21354
21355    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21356        self.buffers_with_disabled_indent_guides
21357            .contains(&buffer_id)
21358    }
21359
21360    pub fn toggle_line_numbers(
21361        &mut self,
21362        _: &ToggleLineNumbers,
21363        _: &mut Window,
21364        cx: &mut Context<Self>,
21365    ) {
21366        let mut editor_settings = EditorSettings::get_global(cx).clone();
21367        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21368        EditorSettings::override_global(editor_settings, cx);
21369    }
21370
21371    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21372        if let Some(show_line_numbers) = self.show_line_numbers {
21373            return show_line_numbers;
21374        }
21375        EditorSettings::get_global(cx).gutter.line_numbers
21376    }
21377
21378    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21379        match (
21380            self.use_relative_line_numbers,
21381            EditorSettings::get_global(cx).relative_line_numbers,
21382        ) {
21383            (None, setting) => setting,
21384            (Some(false), _) => RelativeLineNumbers::Disabled,
21385            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21386            (Some(true), _) => RelativeLineNumbers::Enabled,
21387        }
21388    }
21389
21390    pub fn toggle_relative_line_numbers(
21391        &mut self,
21392        _: &ToggleRelativeLineNumbers,
21393        _: &mut Window,
21394        cx: &mut Context<Self>,
21395    ) {
21396        let is_relative = self.relative_line_numbers(cx);
21397        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21398    }
21399
21400    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21401        self.use_relative_line_numbers = is_relative;
21402        cx.notify();
21403    }
21404
21405    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21406        self.show_gutter = show_gutter;
21407        cx.notify();
21408    }
21409
21410    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21411        self.show_scrollbars = ScrollbarAxes {
21412            horizontal: show,
21413            vertical: show,
21414        };
21415        cx.notify();
21416    }
21417
21418    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21419        self.show_scrollbars.vertical = show;
21420        cx.notify();
21421    }
21422
21423    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21424        self.show_scrollbars.horizontal = show;
21425        cx.notify();
21426    }
21427
21428    pub fn set_minimap_visibility(
21429        &mut self,
21430        minimap_visibility: MinimapVisibility,
21431        window: &mut Window,
21432        cx: &mut Context<Self>,
21433    ) {
21434        if self.minimap_visibility != minimap_visibility {
21435            if minimap_visibility.visible() && self.minimap.is_none() {
21436                let minimap_settings = EditorSettings::get_global(cx).minimap;
21437                self.minimap =
21438                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21439            }
21440            self.minimap_visibility = minimap_visibility;
21441            cx.notify();
21442        }
21443    }
21444
21445    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21446        self.set_show_scrollbars(false, cx);
21447        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21448    }
21449
21450    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21451        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21452    }
21453
21454    /// Normally the text in full mode and auto height editors is padded on the
21455    /// left side by roughly half a character width for improved hit testing.
21456    ///
21457    /// Use this method to disable this for cases where this is not wanted (e.g.
21458    /// if you want to align the editor text with some other text above or below)
21459    /// or if you want to add this padding to single-line editors.
21460    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21461        self.offset_content = offset_content;
21462        cx.notify();
21463    }
21464
21465    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21466        self.show_line_numbers = Some(show_line_numbers);
21467        cx.notify();
21468    }
21469
21470    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21471        self.disable_expand_excerpt_buttons = true;
21472        cx.notify();
21473    }
21474
21475    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21476        self.number_deleted_lines = number;
21477        cx.notify();
21478    }
21479
21480    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21481        self.delegate_expand_excerpts = delegate;
21482    }
21483
21484    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21485        self.delegate_stage_and_restore = delegate;
21486    }
21487
21488    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21489        self.delegate_open_excerpts = delegate;
21490    }
21491
21492    pub fn set_on_local_selections_changed(
21493        &mut self,
21494        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21495    ) {
21496        self.on_local_selections_changed = callback;
21497    }
21498
21499    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21500        self.suppress_selection_callback = suppress;
21501    }
21502
21503    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21504        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21505        cx.notify();
21506    }
21507
21508    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21509        self.show_code_actions = Some(show_code_actions);
21510        cx.notify();
21511    }
21512
21513    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21514        self.show_runnables = Some(show_runnables);
21515        cx.notify();
21516    }
21517
21518    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21519        self.show_breakpoints = Some(show_breakpoints);
21520        cx.notify();
21521    }
21522
21523    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21524        self.show_diff_review_button = show;
21525        cx.notify();
21526    }
21527
21528    pub fn show_diff_review_button(&self) -> bool {
21529        self.show_diff_review_button
21530    }
21531
21532    pub fn render_diff_review_button(
21533        &self,
21534        display_row: DisplayRow,
21535        width: Pixels,
21536        cx: &mut Context<Self>,
21537    ) -> impl IntoElement {
21538        let text_color = cx.theme().colors().text;
21539        let icon_color = cx.theme().colors().icon_accent;
21540
21541        h_flex()
21542            .id("diff_review_button")
21543            .cursor_pointer()
21544            .w(width - px(1.))
21545            .h(relative(0.9))
21546            .justify_center()
21547            .rounded_sm()
21548            .border_1()
21549            .border_color(text_color.opacity(0.1))
21550            .bg(text_color.opacity(0.15))
21551            .hover(|s| {
21552                s.bg(icon_color.opacity(0.4))
21553                    .border_color(icon_color.opacity(0.5))
21554            })
21555            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21556            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21557            .on_mouse_down(
21558                gpui::MouseButton::Left,
21559                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21560                    editor.start_diff_review_drag(display_row, window, cx);
21561                }),
21562            )
21563    }
21564
21565    pub fn start_diff_review_drag(
21566        &mut self,
21567        display_row: DisplayRow,
21568        window: &mut Window,
21569        cx: &mut Context<Self>,
21570    ) {
21571        let snapshot = self.snapshot(window, cx);
21572        let point = snapshot
21573            .display_snapshot
21574            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21575        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21576        self.diff_review_drag_state = Some(DiffReviewDragState {
21577            start_anchor: anchor,
21578            current_anchor: anchor,
21579        });
21580        cx.notify();
21581    }
21582
21583    pub fn update_diff_review_drag(
21584        &mut self,
21585        display_row: DisplayRow,
21586        window: &mut Window,
21587        cx: &mut Context<Self>,
21588    ) {
21589        if self.diff_review_drag_state.is_none() {
21590            return;
21591        }
21592        let snapshot = self.snapshot(window, cx);
21593        let point = snapshot
21594            .display_snapshot
21595            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21596        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21597        if let Some(drag_state) = &mut self.diff_review_drag_state {
21598            drag_state.current_anchor = anchor;
21599            cx.notify();
21600        }
21601    }
21602
21603    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21604        if let Some(drag_state) = self.diff_review_drag_state.take() {
21605            let snapshot = self.snapshot(window, cx);
21606            let range = drag_state.row_range(&snapshot.display_snapshot);
21607            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21608        }
21609        cx.notify();
21610    }
21611
21612    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21613        self.diff_review_drag_state = None;
21614        cx.notify();
21615    }
21616
21617    /// Calculates the appropriate block height for the diff review overlay.
21618    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21619    /// and 2 lines per comment when expanded.
21620    fn calculate_overlay_height(
21621        &self,
21622        hunk_key: &DiffHunkKey,
21623        comments_expanded: bool,
21624        snapshot: &MultiBufferSnapshot,
21625    ) -> u32 {
21626        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21627        let base_height: u32 = 2; // Input row with avatar and buttons
21628
21629        if comment_count == 0 {
21630            base_height
21631        } else if comments_expanded {
21632            // Header (1 line) + 2 lines per comment
21633            base_height + 1 + (comment_count as u32 * 2)
21634        } else {
21635            // Just header when collapsed
21636            base_height + 1
21637        }
21638    }
21639
21640    pub fn show_diff_review_overlay(
21641        &mut self,
21642        display_range: Range<DisplayRow>,
21643        window: &mut Window,
21644        cx: &mut Context<Self>,
21645    ) {
21646        let Range { start, end } = display_range.sorted();
21647
21648        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21649        let editor_snapshot = self.snapshot(window, cx);
21650
21651        // Convert display rows to multibuffer points
21652        let start_point = editor_snapshot
21653            .display_snapshot
21654            .display_point_to_point(start.as_display_point(), Bias::Left);
21655        let end_point = editor_snapshot
21656            .display_snapshot
21657            .display_point_to_point(end.as_display_point(), Bias::Left);
21658        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21659
21660        // Create anchor range for the selected lines (start of first line to end of last line)
21661        let line_end = Point::new(
21662            end_point.row,
21663            buffer_snapshot.line_len(end_multi_buffer_row),
21664        );
21665        let anchor_range =
21666            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21667
21668        // Compute the hunk key for this display row
21669        let file_path = buffer_snapshot
21670            .file_at(start_point)
21671            .map(|file: &Arc<dyn language::File>| file.path().clone())
21672            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21673        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21674        let new_hunk_key = DiffHunkKey {
21675            file_path,
21676            hunk_start_anchor,
21677        };
21678
21679        // Check if we already have an overlay for this hunk
21680        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21681            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21682        }) {
21683            // Just focus the existing overlay's prompt editor
21684            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21685            window.focus(&focus_handle, cx);
21686            return;
21687        }
21688
21689        // Dismiss overlays that have no comments for their hunks
21690        self.dismiss_overlays_without_comments(cx);
21691
21692        // Get the current user's avatar URI from the project's user_store
21693        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21694            let user_store = project.read(cx).user_store();
21695            user_store
21696                .read(cx)
21697                .current_user()
21698                .map(|user| user.avatar_uri.clone())
21699        });
21700
21701        // Create anchor at the end of the last row so the block appears immediately below it
21702        // Use multibuffer coordinates for anchor creation
21703        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21704        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21705
21706        // Use the hunk key we already computed
21707        let hunk_key = new_hunk_key;
21708
21709        // Create the prompt editor for the review input
21710        let prompt_editor = cx.new(|cx| {
21711            let mut editor = Editor::single_line(window, cx);
21712            editor.set_placeholder_text("Add a review comment...", window, cx);
21713            editor
21714        });
21715
21716        // Register the Newline action on the prompt editor to submit the review
21717        let parent_editor = cx.entity().downgrade();
21718        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21719            prompt_editor.register_action({
21720                let parent_editor = parent_editor.clone();
21721                move |_: &crate::actions::Newline, window, cx| {
21722                    if let Some(editor) = parent_editor.upgrade() {
21723                        editor.update(cx, |editor, cx| {
21724                            editor.submit_diff_review_comment(window, cx);
21725                        });
21726                    }
21727                }
21728            })
21729        });
21730
21731        // Calculate initial height based on existing comments for this hunk
21732        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21733
21734        // Create the overlay block
21735        let prompt_editor_for_render = prompt_editor.clone();
21736        let hunk_key_for_render = hunk_key.clone();
21737        let editor_handle = cx.entity().downgrade();
21738        let block = BlockProperties {
21739            style: BlockStyle::Sticky,
21740            placement: BlockPlacement::Below(anchor),
21741            height: Some(initial_height),
21742            render: Arc::new(move |cx| {
21743                Self::render_diff_review_overlay(
21744                    &prompt_editor_for_render,
21745                    &hunk_key_for_render,
21746                    &editor_handle,
21747                    cx,
21748                )
21749            }),
21750            priority: 0,
21751        };
21752
21753        let block_ids = self.insert_blocks([block], None, cx);
21754        let Some(block_id) = block_ids.into_iter().next() else {
21755            log::error!("Failed to insert diff review overlay block");
21756            return;
21757        };
21758
21759        self.diff_review_overlays.push(DiffReviewOverlay {
21760            anchor_range,
21761            block_id,
21762            prompt_editor: prompt_editor.clone(),
21763            hunk_key,
21764            comments_expanded: true,
21765            inline_edit_editors: HashMap::default(),
21766            inline_edit_subscriptions: HashMap::default(),
21767            user_avatar_uri,
21768            _subscription: subscription,
21769        });
21770
21771        // Focus the prompt editor
21772        let focus_handle = prompt_editor.focus_handle(cx);
21773        window.focus(&focus_handle, cx);
21774
21775        cx.notify();
21776    }
21777
21778    /// Dismisses all diff review overlays.
21779    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21780        if self.diff_review_overlays.is_empty() {
21781            return;
21782        }
21783        let block_ids: HashSet<_> = self
21784            .diff_review_overlays
21785            .drain(..)
21786            .map(|overlay| overlay.block_id)
21787            .collect();
21788        self.remove_blocks(block_ids, None, cx);
21789        cx.notify();
21790    }
21791
21792    /// Dismisses overlays that have no comments stored for their hunks.
21793    /// Keeps overlays that have at least one comment.
21794    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21795        let snapshot = self.buffer.read(cx).snapshot(cx);
21796
21797        // First, compute which overlays have comments (to avoid borrow issues with retain)
21798        let overlays_with_comments: Vec<bool> = self
21799            .diff_review_overlays
21800            .iter()
21801            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21802            .collect();
21803
21804        // Now collect block IDs to remove and retain overlays
21805        let mut block_ids_to_remove = HashSet::default();
21806        let mut index = 0;
21807        self.diff_review_overlays.retain(|overlay| {
21808            let has_comments = overlays_with_comments[index];
21809            index += 1;
21810            if !has_comments {
21811                block_ids_to_remove.insert(overlay.block_id);
21812            }
21813            has_comments
21814        });
21815
21816        if !block_ids_to_remove.is_empty() {
21817            self.remove_blocks(block_ids_to_remove, None, cx);
21818            cx.notify();
21819        }
21820    }
21821
21822    /// Refreshes the diff review overlay block to update its height and render function.
21823    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21824    fn refresh_diff_review_overlay_height(
21825        &mut self,
21826        hunk_key: &DiffHunkKey,
21827        _window: &mut Window,
21828        cx: &mut Context<Self>,
21829    ) {
21830        // Extract all needed data from overlay first to avoid borrow conflicts
21831        let snapshot = self.buffer.read(cx).snapshot(cx);
21832        let (comments_expanded, block_id, prompt_editor) = {
21833            let Some(overlay) = self
21834                .diff_review_overlays
21835                .iter()
21836                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21837            else {
21838                return;
21839            };
21840
21841            (
21842                overlay.comments_expanded,
21843                overlay.block_id,
21844                overlay.prompt_editor.clone(),
21845            )
21846        };
21847
21848        // Calculate new height
21849        let snapshot = self.buffer.read(cx).snapshot(cx);
21850        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21851
21852        // Update the block height using resize_blocks (avoids flicker)
21853        let mut heights = HashMap::default();
21854        heights.insert(block_id, new_height);
21855        self.resize_blocks(heights, None, cx);
21856
21857        // Update the render function using replace_blocks (avoids flicker)
21858        let hunk_key_for_render = hunk_key.clone();
21859        let editor_handle = cx.entity().downgrade();
21860        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21861            Arc::new(move |cx| {
21862                Self::render_diff_review_overlay(
21863                    &prompt_editor,
21864                    &hunk_key_for_render,
21865                    &editor_handle,
21866                    cx,
21867                )
21868            });
21869
21870        let mut renderers = HashMap::default();
21871        renderers.insert(block_id, render);
21872        self.replace_blocks(renderers, None, cx);
21873    }
21874
21875    /// Action handler for SubmitDiffReviewComment.
21876    pub fn submit_diff_review_comment_action(
21877        &mut self,
21878        _: &SubmitDiffReviewComment,
21879        window: &mut Window,
21880        cx: &mut Context<Self>,
21881    ) {
21882        self.submit_diff_review_comment(window, cx);
21883    }
21884
21885    /// Stores the diff review comment locally.
21886    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21887    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21888        // Find the overlay that currently has focus
21889        let overlay_index = self
21890            .diff_review_overlays
21891            .iter()
21892            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21893        let Some(overlay_index) = overlay_index else {
21894            return;
21895        };
21896        let overlay = &self.diff_review_overlays[overlay_index];
21897
21898        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21899        if comment_text.is_empty() {
21900            return;
21901        }
21902
21903        let anchor_range = overlay.anchor_range.clone();
21904        let hunk_key = overlay.hunk_key.clone();
21905
21906        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21907
21908        // Clear the prompt editor but keep the overlay open
21909        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21910            overlay.prompt_editor.update(cx, |editor, cx| {
21911                editor.clear(window, cx);
21912            });
21913        }
21914
21915        // Refresh the overlay to update the block height for the new comment
21916        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21917
21918        cx.notify();
21919    }
21920
21921    /// Returns the prompt editor for the diff review overlay, if one is active.
21922    /// This is primarily used for testing.
21923    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21924        self.diff_review_overlays
21925            .first()
21926            .map(|overlay| &overlay.prompt_editor)
21927    }
21928
21929    /// Returns the line range for the first diff review overlay, if one is active.
21930    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21931    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21932        let overlay = self.diff_review_overlays.first()?;
21933        let snapshot = self.buffer.read(cx).snapshot(cx);
21934        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21935        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21936        let start_row = snapshot
21937            .point_to_buffer_point(start_point)
21938            .map(|(_, p, _)| p.row)
21939            .unwrap_or(start_point.row);
21940        let end_row = snapshot
21941            .point_to_buffer_point(end_point)
21942            .map(|(_, p, _)| p.row)
21943            .unwrap_or(end_point.row);
21944        Some((start_row, end_row))
21945    }
21946
21947    /// Sets whether the comments section is expanded in the diff review overlay.
21948    /// This is primarily used for testing.
21949    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21950        for overlay in &mut self.diff_review_overlays {
21951            overlay.comments_expanded = expanded;
21952        }
21953        cx.notify();
21954    }
21955
21956    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21957    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21958        a.file_path == b.file_path
21959            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21960    }
21961
21962    /// Returns comments for a specific hunk, ordered by creation time.
21963    pub fn comments_for_hunk<'a>(
21964        &'a self,
21965        key: &DiffHunkKey,
21966        snapshot: &MultiBufferSnapshot,
21967    ) -> &'a [StoredReviewComment] {
21968        let key_point = key.hunk_start_anchor.to_point(snapshot);
21969        self.stored_review_comments
21970            .iter()
21971            .find(|(k, _)| {
21972                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21973            })
21974            .map(|(_, comments)| comments.as_slice())
21975            .unwrap_or(&[])
21976    }
21977
21978    /// Returns the total count of stored review comments across all hunks.
21979    pub fn total_review_comment_count(&self) -> usize {
21980        self.stored_review_comments
21981            .iter()
21982            .map(|(_, v)| v.len())
21983            .sum()
21984    }
21985
21986    /// Returns the count of comments for a specific hunk.
21987    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21988        let key_point = key.hunk_start_anchor.to_point(snapshot);
21989        self.stored_review_comments
21990            .iter()
21991            .find(|(k, _)| {
21992                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21993            })
21994            .map(|(_, v)| v.len())
21995            .unwrap_or(0)
21996    }
21997
21998    /// Adds a new review comment to a specific hunk.
21999    pub fn add_review_comment(
22000        &mut self,
22001        hunk_key: DiffHunkKey,
22002        comment: String,
22003        anchor_range: Range<Anchor>,
22004        cx: &mut Context<Self>,
22005    ) -> usize {
22006        let id = self.next_review_comment_id;
22007        self.next_review_comment_id += 1;
22008
22009        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22010
22011        let snapshot = self.buffer.read(cx).snapshot(cx);
22012        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22013
22014        // Find existing entry for this hunk or add a new one
22015        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22016            k.file_path == hunk_key.file_path
22017                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22018        }) {
22019            comments.push(stored_comment);
22020        } else {
22021            self.stored_review_comments
22022                .push((hunk_key, vec![stored_comment]));
22023        }
22024
22025        cx.emit(EditorEvent::ReviewCommentsChanged {
22026            total_count: self.total_review_comment_count(),
22027        });
22028        cx.notify();
22029        id
22030    }
22031
22032    /// Removes a review comment by ID from any hunk.
22033    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22034        for (_, comments) in self.stored_review_comments.iter_mut() {
22035            if let Some(index) = comments.iter().position(|c| c.id == id) {
22036                comments.remove(index);
22037                cx.emit(EditorEvent::ReviewCommentsChanged {
22038                    total_count: self.total_review_comment_count(),
22039                });
22040                cx.notify();
22041                return true;
22042            }
22043        }
22044        false
22045    }
22046
22047    /// Updates a review comment's text by ID.
22048    pub fn update_review_comment(
22049        &mut self,
22050        id: usize,
22051        new_comment: String,
22052        cx: &mut Context<Self>,
22053    ) -> bool {
22054        for (_, comments) in self.stored_review_comments.iter_mut() {
22055            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22056                comment.comment = new_comment;
22057                comment.is_editing = false;
22058                cx.emit(EditorEvent::ReviewCommentsChanged {
22059                    total_count: self.total_review_comment_count(),
22060                });
22061                cx.notify();
22062                return true;
22063            }
22064        }
22065        false
22066    }
22067
22068    /// Sets a comment's editing state.
22069    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22070        for (_, comments) in self.stored_review_comments.iter_mut() {
22071            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22072                comment.is_editing = is_editing;
22073                cx.notify();
22074                return;
22075            }
22076        }
22077    }
22078
22079    /// Takes all stored comments from all hunks, clearing the storage.
22080    /// Returns a Vec of (hunk_key, comments) pairs.
22081    pub fn take_all_review_comments(
22082        &mut self,
22083        cx: &mut Context<Self>,
22084    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22085        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22086        self.dismiss_all_diff_review_overlays(cx);
22087        let comments = std::mem::take(&mut self.stored_review_comments);
22088        // Reset the ID counter since all comments have been taken
22089        self.next_review_comment_id = 0;
22090        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22091        cx.notify();
22092        comments
22093    }
22094
22095    /// Removes review comments whose anchors are no longer valid or whose
22096    /// associated diff hunks no longer exist.
22097    ///
22098    /// This should be called when the buffer changes to prevent orphaned comments
22099    /// from accumulating.
22100    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22101        let snapshot = self.buffer.read(cx).snapshot(cx);
22102        let original_count = self.total_review_comment_count();
22103
22104        // Remove comments with invalid hunk anchors
22105        self.stored_review_comments
22106            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22107
22108        // Also clean up individual comments with invalid anchor ranges
22109        for (_, comments) in &mut self.stored_review_comments {
22110            comments.retain(|comment| {
22111                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22112            });
22113        }
22114
22115        // Remove empty hunk entries
22116        self.stored_review_comments
22117            .retain(|(_, comments)| !comments.is_empty());
22118
22119        let new_count = self.total_review_comment_count();
22120        if new_count != original_count {
22121            cx.emit(EditorEvent::ReviewCommentsChanged {
22122                total_count: new_count,
22123            });
22124            cx.notify();
22125        }
22126    }
22127
22128    /// Toggles the expanded state of the comments section in the overlay.
22129    pub fn toggle_review_comments_expanded(
22130        &mut self,
22131        _: &ToggleReviewCommentsExpanded,
22132        window: &mut Window,
22133        cx: &mut Context<Self>,
22134    ) {
22135        // Find the overlay that currently has focus, or use the first one
22136        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22137            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22138                overlay.comments_expanded = !overlay.comments_expanded;
22139                Some(overlay.hunk_key.clone())
22140            } else {
22141                None
22142            }
22143        });
22144
22145        // If no focused overlay found, toggle the first one
22146        let hunk_key = overlay_info.or_else(|| {
22147            self.diff_review_overlays.first_mut().map(|overlay| {
22148                overlay.comments_expanded = !overlay.comments_expanded;
22149                overlay.hunk_key.clone()
22150            })
22151        });
22152
22153        if let Some(hunk_key) = hunk_key {
22154            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22155            cx.notify();
22156        }
22157    }
22158
22159    /// Handles the EditReviewComment action - sets a comment into editing mode.
22160    pub fn edit_review_comment(
22161        &mut self,
22162        action: &EditReviewComment,
22163        window: &mut Window,
22164        cx: &mut Context<Self>,
22165    ) {
22166        let comment_id = action.id;
22167
22168        // Set the comment to editing mode
22169        self.set_comment_editing(comment_id, true, cx);
22170
22171        // Find the overlay that contains this comment and create an inline editor if needed
22172        // First, find which hunk this comment belongs to
22173        let hunk_key = self
22174            .stored_review_comments
22175            .iter()
22176            .find_map(|(key, comments)| {
22177                if comments.iter().any(|c| c.id == comment_id) {
22178                    Some(key.clone())
22179                } else {
22180                    None
22181                }
22182            });
22183
22184        let snapshot = self.buffer.read(cx).snapshot(cx);
22185        if let Some(hunk_key) = hunk_key {
22186            if let Some(overlay) = self
22187                .diff_review_overlays
22188                .iter_mut()
22189                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22190            {
22191                if let std::collections::hash_map::Entry::Vacant(entry) =
22192                    overlay.inline_edit_editors.entry(comment_id)
22193                {
22194                    // Find the comment text
22195                    let comment_text = self
22196                        .stored_review_comments
22197                        .iter()
22198                        .flat_map(|(_, comments)| comments)
22199                        .find(|c| c.id == comment_id)
22200                        .map(|c| c.comment.clone())
22201                        .unwrap_or_default();
22202
22203                    // Create inline editor
22204                    let parent_editor = cx.entity().downgrade();
22205                    let inline_editor = cx.new(|cx| {
22206                        let mut editor = Editor::single_line(window, cx);
22207                        editor.set_text(&*comment_text, window, cx);
22208                        // Select all text for easy replacement
22209                        editor.select_all(&crate::actions::SelectAll, window, cx);
22210                        editor
22211                    });
22212
22213                    // Register the Newline action to confirm the edit
22214                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22215                        inline_editor.register_action({
22216                            let parent_editor = parent_editor.clone();
22217                            move |_: &crate::actions::Newline, window, cx| {
22218                                if let Some(editor) = parent_editor.upgrade() {
22219                                    editor.update(cx, |editor, cx| {
22220                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22221                                    });
22222                                }
22223                            }
22224                        })
22225                    });
22226
22227                    // Store the subscription to keep the action handler alive
22228                    overlay
22229                        .inline_edit_subscriptions
22230                        .insert(comment_id, subscription);
22231
22232                    // Focus the inline editor
22233                    let focus_handle = inline_editor.focus_handle(cx);
22234                    window.focus(&focus_handle, cx);
22235
22236                    entry.insert(inline_editor);
22237                }
22238            }
22239        }
22240
22241        cx.notify();
22242    }
22243
22244    /// Confirms an inline edit of a review comment.
22245    pub fn confirm_edit_review_comment(
22246        &mut self,
22247        comment_id: usize,
22248        _window: &mut Window,
22249        cx: &mut Context<Self>,
22250    ) {
22251        // Get the new text from the inline editor
22252        // Find the overlay containing this comment's inline editor
22253        let snapshot = self.buffer.read(cx).snapshot(cx);
22254        let hunk_key = self
22255            .stored_review_comments
22256            .iter()
22257            .find_map(|(key, comments)| {
22258                if comments.iter().any(|c| c.id == comment_id) {
22259                    Some(key.clone())
22260                } else {
22261                    None
22262                }
22263            });
22264
22265        let new_text = hunk_key
22266            .as_ref()
22267            .and_then(|hunk_key| {
22268                self.diff_review_overlays
22269                    .iter()
22270                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22271            })
22272            .as_ref()
22273            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22274            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22275
22276        if let Some(new_text) = new_text {
22277            if !new_text.is_empty() {
22278                self.update_review_comment(comment_id, new_text, cx);
22279            }
22280        }
22281
22282        // Remove the inline editor and its subscription
22283        if let Some(hunk_key) = hunk_key {
22284            if let Some(overlay) = self
22285                .diff_review_overlays
22286                .iter_mut()
22287                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22288            {
22289                overlay.inline_edit_editors.remove(&comment_id);
22290                overlay.inline_edit_subscriptions.remove(&comment_id);
22291            }
22292        }
22293
22294        // Clear editing state
22295        self.set_comment_editing(comment_id, false, cx);
22296    }
22297
22298    /// Cancels an inline edit of a review comment.
22299    pub fn cancel_edit_review_comment(
22300        &mut self,
22301        comment_id: usize,
22302        _window: &mut Window,
22303        cx: &mut Context<Self>,
22304    ) {
22305        // Find which hunk this comment belongs to
22306        let hunk_key = self
22307            .stored_review_comments
22308            .iter()
22309            .find_map(|(key, comments)| {
22310                if comments.iter().any(|c| c.id == comment_id) {
22311                    Some(key.clone())
22312                } else {
22313                    None
22314                }
22315            });
22316
22317        // Remove the inline editor and its subscription
22318        if let Some(hunk_key) = hunk_key {
22319            let snapshot = self.buffer.read(cx).snapshot(cx);
22320            if let Some(overlay) = self
22321                .diff_review_overlays
22322                .iter_mut()
22323                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22324            {
22325                overlay.inline_edit_editors.remove(&comment_id);
22326                overlay.inline_edit_subscriptions.remove(&comment_id);
22327            }
22328        }
22329
22330        // Clear editing state
22331        self.set_comment_editing(comment_id, false, cx);
22332    }
22333
22334    /// Action handler for ConfirmEditReviewComment.
22335    pub fn confirm_edit_review_comment_action(
22336        &mut self,
22337        action: &ConfirmEditReviewComment,
22338        window: &mut Window,
22339        cx: &mut Context<Self>,
22340    ) {
22341        self.confirm_edit_review_comment(action.id, window, cx);
22342    }
22343
22344    /// Action handler for CancelEditReviewComment.
22345    pub fn cancel_edit_review_comment_action(
22346        &mut self,
22347        action: &CancelEditReviewComment,
22348        window: &mut Window,
22349        cx: &mut Context<Self>,
22350    ) {
22351        self.cancel_edit_review_comment(action.id, window, cx);
22352    }
22353
22354    /// Handles the DeleteReviewComment action - removes a comment.
22355    pub fn delete_review_comment(
22356        &mut self,
22357        action: &DeleteReviewComment,
22358        window: &mut Window,
22359        cx: &mut Context<Self>,
22360    ) {
22361        // Get the hunk key before removing the comment
22362        // Find the hunk key from the comment itself
22363        let comment_id = action.id;
22364        let hunk_key = self
22365            .stored_review_comments
22366            .iter()
22367            .find_map(|(key, comments)| {
22368                if comments.iter().any(|c| c.id == comment_id) {
22369                    Some(key.clone())
22370                } else {
22371                    None
22372                }
22373            });
22374
22375        // Also get it from the overlay for refresh purposes
22376        let overlay_hunk_key = self
22377            .diff_review_overlays
22378            .first()
22379            .map(|o| o.hunk_key.clone());
22380
22381        self.remove_review_comment(action.id, cx);
22382
22383        // Refresh the overlay height after removing a comment
22384        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22385            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22386        }
22387    }
22388
22389    fn render_diff_review_overlay(
22390        prompt_editor: &Entity<Editor>,
22391        hunk_key: &DiffHunkKey,
22392        editor_handle: &WeakEntity<Editor>,
22393        cx: &mut BlockContext,
22394    ) -> AnyElement {
22395        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22396            if ranges.is_empty() {
22397                return None;
22398            }
22399            let formatted: Vec<String> = ranges
22400                .iter()
22401                .map(|(start, end)| {
22402                    let start_line = start + 1;
22403                    let end_line = end + 1;
22404                    if start_line == end_line {
22405                        format!("Line {start_line}")
22406                    } else {
22407                        format!("Lines {start_line}-{end_line}")
22408                    }
22409                })
22410                .collect();
22411            // Don't show label for single line in single excerpt
22412            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22413                return None;
22414            }
22415            Some(formatted.join(""))
22416        }
22417
22418        let theme = cx.theme();
22419        let colors = theme.colors();
22420
22421        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22422            editor_handle
22423                .upgrade()
22424                .map(|editor| {
22425                    let editor = editor.read(cx);
22426                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22427                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22428                    let (expanded, editors, avatar_uri, line_ranges) = editor
22429                        .diff_review_overlays
22430                        .iter()
22431                        .find(|overlay| {
22432                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22433                        })
22434                        .map(|o| {
22435                            let start_point = o.anchor_range.start.to_point(&snapshot);
22436                            let end_point = o.anchor_range.end.to_point(&snapshot);
22437                            // Get line ranges per excerpt to detect discontinuities
22438                            let buffer_ranges =
22439                                snapshot.range_to_buffer_ranges(start_point..end_point);
22440                            let ranges: Vec<(u32, u32)> = buffer_ranges
22441                                .iter()
22442                                .map(|(buffer, range, _)| {
22443                                    let start = buffer.offset_to_point(range.start.0).row;
22444                                    let end = buffer.offset_to_point(range.end.0).row;
22445                                    (start, end)
22446                                })
22447                                .collect();
22448                            (
22449                                o.comments_expanded,
22450                                o.inline_edit_editors.clone(),
22451                                o.user_avatar_uri.clone(),
22452                                if ranges.is_empty() {
22453                                    None
22454                                } else {
22455                                    Some(ranges)
22456                                },
22457                            )
22458                        })
22459                        .unwrap_or((true, HashMap::default(), None, None));
22460                    (comments, expanded, editors, avatar_uri, line_ranges)
22461                })
22462                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22463
22464        let comment_count = comments.len();
22465        let avatar_size = px(20.);
22466        let action_icon_size = IconSize::XSmall;
22467
22468        v_flex()
22469            .w_full()
22470            .bg(colors.editor_background)
22471            .border_b_1()
22472            .border_color(colors.border)
22473            .px_2()
22474            .pb_2()
22475            .gap_2()
22476            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22477            .when_some(line_ranges, |el, ranges| {
22478                let label = format_line_ranges(&ranges);
22479                if let Some(label) = label {
22480                    el.child(
22481                        h_flex()
22482                            .w_full()
22483                            .px_2()
22484                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22485                    )
22486                } else {
22487                    el
22488                }
22489            })
22490            // Top row: editable input with user's avatar
22491            .child(
22492                h_flex()
22493                    .w_full()
22494                    .items_center()
22495                    .gap_2()
22496                    .px_2()
22497                    .py_1p5()
22498                    .rounded_md()
22499                    .bg(colors.surface_background)
22500                    .child(
22501                        div()
22502                            .size(avatar_size)
22503                            .flex_shrink_0()
22504                            .rounded_full()
22505                            .overflow_hidden()
22506                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22507                                Avatar::new(avatar_uri.clone())
22508                                    .size(avatar_size)
22509                                    .into_any_element()
22510                            } else {
22511                                Icon::new(IconName::Person)
22512                                    .size(IconSize::Small)
22513                                    .color(ui::Color::Muted)
22514                                    .into_any_element()
22515                            }),
22516                    )
22517                    .child(
22518                        div()
22519                            .flex_1()
22520                            .border_1()
22521                            .border_color(colors.border)
22522                            .rounded_md()
22523                            .bg(colors.editor_background)
22524                            .px_2()
22525                            .py_1()
22526                            .child(prompt_editor.clone()),
22527                    )
22528                    .child(
22529                        h_flex()
22530                            .flex_shrink_0()
22531                            .gap_1()
22532                            .child(
22533                                IconButton::new("diff-review-close", IconName::Close)
22534                                    .icon_color(ui::Color::Muted)
22535                                    .icon_size(action_icon_size)
22536                                    .tooltip(Tooltip::text("Close"))
22537                                    .on_click(|_, window, cx| {
22538                                        window
22539                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22540                                    }),
22541                            )
22542                            .child(
22543                                IconButton::new("diff-review-add", IconName::Return)
22544                                    .icon_color(ui::Color::Muted)
22545                                    .icon_size(action_icon_size)
22546                                    .tooltip(Tooltip::text("Add comment"))
22547                                    .on_click(|_, window, cx| {
22548                                        window.dispatch_action(
22549                                            Box::new(crate::actions::SubmitDiffReviewComment),
22550                                            cx,
22551                                        );
22552                                    }),
22553                            ),
22554                    ),
22555            )
22556            // Expandable comments section (only shown when there are comments)
22557            .when(comment_count > 0, |el| {
22558                el.child(Self::render_comments_section(
22559                    comments,
22560                    comments_expanded,
22561                    inline_editors,
22562                    user_avatar_uri,
22563                    avatar_size,
22564                    action_icon_size,
22565                    colors,
22566                ))
22567            })
22568            .into_any_element()
22569    }
22570
22571    fn render_comments_section(
22572        comments: Vec<StoredReviewComment>,
22573        expanded: bool,
22574        inline_editors: HashMap<usize, Entity<Editor>>,
22575        user_avatar_uri: Option<SharedUri>,
22576        avatar_size: Pixels,
22577        action_icon_size: IconSize,
22578        colors: &theme::ThemeColors,
22579    ) -> impl IntoElement {
22580        let comment_count = comments.len();
22581
22582        v_flex()
22583            .w_full()
22584            .gap_1()
22585            // Header with expand/collapse toggle
22586            .child(
22587                h_flex()
22588                    .id("review-comments-header")
22589                    .w_full()
22590                    .items_center()
22591                    .gap_1()
22592                    .px_2()
22593                    .py_1()
22594                    .cursor_pointer()
22595                    .rounded_md()
22596                    .hover(|style| style.bg(colors.ghost_element_hover))
22597                    .on_click(|_, window: &mut Window, cx| {
22598                        window.dispatch_action(
22599                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22600                            cx,
22601                        );
22602                    })
22603                    .child(
22604                        Icon::new(if expanded {
22605                            IconName::ChevronDown
22606                        } else {
22607                            IconName::ChevronRight
22608                        })
22609                        .size(IconSize::Small)
22610                        .color(ui::Color::Muted),
22611                    )
22612                    .child(
22613                        Label::new(format!(
22614                            "{} Comment{}",
22615                            comment_count,
22616                            if comment_count == 1 { "" } else { "s" }
22617                        ))
22618                        .size(LabelSize::Small)
22619                        .color(Color::Muted),
22620                    ),
22621            )
22622            // Comments list (when expanded)
22623            .when(expanded, |el| {
22624                el.children(comments.into_iter().map(|comment| {
22625                    let inline_editor = inline_editors.get(&comment.id).cloned();
22626                    Self::render_comment_row(
22627                        comment,
22628                        inline_editor,
22629                        user_avatar_uri.clone(),
22630                        avatar_size,
22631                        action_icon_size,
22632                        colors,
22633                    )
22634                }))
22635            })
22636    }
22637
22638    fn render_comment_row(
22639        comment: StoredReviewComment,
22640        inline_editor: Option<Entity<Editor>>,
22641        user_avatar_uri: Option<SharedUri>,
22642        avatar_size: Pixels,
22643        action_icon_size: IconSize,
22644        colors: &theme::ThemeColors,
22645    ) -> impl IntoElement {
22646        let comment_id = comment.id;
22647        let is_editing = inline_editor.is_some();
22648
22649        h_flex()
22650            .w_full()
22651            .items_center()
22652            .gap_2()
22653            .px_2()
22654            .py_1p5()
22655            .rounded_md()
22656            .bg(colors.surface_background)
22657            .child(
22658                div()
22659                    .size(avatar_size)
22660                    .flex_shrink_0()
22661                    .rounded_full()
22662                    .overflow_hidden()
22663                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22664                        Avatar::new(avatar_uri.clone())
22665                            .size(avatar_size)
22666                            .into_any_element()
22667                    } else {
22668                        Icon::new(IconName::Person)
22669                            .size(IconSize::Small)
22670                            .color(ui::Color::Muted)
22671                            .into_any_element()
22672                    }),
22673            )
22674            .child(if let Some(editor) = inline_editor {
22675                // Inline edit mode: show an editable text field
22676                div()
22677                    .flex_1()
22678                    .border_1()
22679                    .border_color(colors.border)
22680                    .rounded_md()
22681                    .bg(colors.editor_background)
22682                    .px_2()
22683                    .py_1()
22684                    .child(editor)
22685                    .into_any_element()
22686            } else {
22687                // Display mode: show the comment text
22688                div()
22689                    .flex_1()
22690                    .text_sm()
22691                    .text_color(colors.text)
22692                    .child(comment.comment)
22693                    .into_any_element()
22694            })
22695            .child(if is_editing {
22696                // Editing mode: show close and confirm buttons
22697                h_flex()
22698                    .gap_1()
22699                    .child(
22700                        IconButton::new(
22701                            format!("diff-review-cancel-edit-{comment_id}"),
22702                            IconName::Close,
22703                        )
22704                        .icon_color(ui::Color::Muted)
22705                        .icon_size(action_icon_size)
22706                        .tooltip(Tooltip::text("Cancel"))
22707                        .on_click(move |_, window, cx| {
22708                            window.dispatch_action(
22709                                Box::new(crate::actions::CancelEditReviewComment {
22710                                    id: comment_id,
22711                                }),
22712                                cx,
22713                            );
22714                        }),
22715                    )
22716                    .child(
22717                        IconButton::new(
22718                            format!("diff-review-confirm-edit-{comment_id}"),
22719                            IconName::Return,
22720                        )
22721                        .icon_color(ui::Color::Muted)
22722                        .icon_size(action_icon_size)
22723                        .tooltip(Tooltip::text("Confirm"))
22724                        .on_click(move |_, window, cx| {
22725                            window.dispatch_action(
22726                                Box::new(crate::actions::ConfirmEditReviewComment {
22727                                    id: comment_id,
22728                                }),
22729                                cx,
22730                            );
22731                        }),
22732                    )
22733                    .into_any_element()
22734            } else {
22735                // Display mode: no action buttons for now (edit/delete not yet implemented)
22736                gpui::Empty.into_any_element()
22737            })
22738    }
22739
22740    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22741        if self.display_map.read(cx).masked != masked {
22742            self.display_map.update(cx, |map, _| map.masked = masked);
22743        }
22744        cx.notify()
22745    }
22746
22747    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22748        self.show_wrap_guides = Some(show_wrap_guides);
22749        cx.notify();
22750    }
22751
22752    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22753        self.show_indent_guides = Some(show_indent_guides);
22754        cx.notify();
22755    }
22756
22757    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22758        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22759            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22760                && let Some(dir) = file.abs_path(cx).parent()
22761            {
22762                return Some(dir.to_owned());
22763            }
22764        }
22765
22766        None
22767    }
22768
22769    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22770        self.active_excerpt(cx)?
22771            .1
22772            .read(cx)
22773            .file()
22774            .and_then(|f| f.as_local())
22775    }
22776
22777    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22778        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22779            let buffer = buffer.read(cx);
22780            if let Some(project_path) = buffer.project_path(cx) {
22781                let project = self.project()?.read(cx);
22782                project.absolute_path(&project_path, cx)
22783            } else {
22784                buffer
22785                    .file()
22786                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22787            }
22788        })
22789    }
22790
22791    pub fn reveal_in_finder(
22792        &mut self,
22793        _: &RevealInFileManager,
22794        _window: &mut Window,
22795        cx: &mut Context<Self>,
22796    ) {
22797        if let Some(path) = self.target_file_abs_path(cx) {
22798            if let Some(project) = self.project() {
22799                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22800            } else {
22801                cx.reveal_path(&path);
22802            }
22803        }
22804    }
22805
22806    pub fn copy_path(
22807        &mut self,
22808        _: &zed_actions::workspace::CopyPath,
22809        _window: &mut Window,
22810        cx: &mut Context<Self>,
22811    ) {
22812        if let Some(path) = self.target_file_abs_path(cx)
22813            && let Some(path) = path.to_str()
22814        {
22815            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22816        } else {
22817            cx.propagate();
22818        }
22819    }
22820
22821    pub fn copy_relative_path(
22822        &mut self,
22823        _: &zed_actions::workspace::CopyRelativePath,
22824        _window: &mut Window,
22825        cx: &mut Context<Self>,
22826    ) {
22827        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22828            let project = self.project()?.read(cx);
22829            let path = buffer.read(cx).file()?.path();
22830            let path = path.display(project.path_style(cx));
22831            Some(path)
22832        }) {
22833            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22834        } else {
22835            cx.propagate();
22836        }
22837    }
22838
22839    /// Returns the project path for the editor's buffer, if any buffer is
22840    /// opened in the editor.
22841    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22842        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22843            buffer.read(cx).project_path(cx)
22844        } else {
22845            None
22846        }
22847    }
22848
22849    // Returns true if the editor handled a go-to-line request
22850    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22851        maybe!({
22852            let breakpoint_store = self.breakpoint_store.as_ref()?;
22853
22854            let (active_stack_frame, debug_line_pane_id) = {
22855                let store = breakpoint_store.read(cx);
22856                let active_stack_frame = store.active_position().cloned();
22857                let debug_line_pane_id = store.active_debug_line_pane_id();
22858                (active_stack_frame, debug_line_pane_id)
22859            };
22860
22861            let Some(active_stack_frame) = active_stack_frame else {
22862                self.clear_row_highlights::<ActiveDebugLine>();
22863                return None;
22864            };
22865
22866            if let Some(debug_line_pane_id) = debug_line_pane_id {
22867                if let Some(workspace) = self
22868                    .workspace
22869                    .as_ref()
22870                    .and_then(|(workspace, _)| workspace.upgrade())
22871                {
22872                    let editor_pane_id = workspace
22873                        .read(cx)
22874                        .pane_for_item_id(cx.entity_id())
22875                        .map(|pane| pane.entity_id());
22876
22877                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22878                        self.clear_row_highlights::<ActiveDebugLine>();
22879                        return None;
22880                    }
22881                }
22882            }
22883
22884            let position = active_stack_frame.position;
22885            let buffer_id = position.buffer_id?;
22886            let snapshot = self
22887                .project
22888                .as_ref()?
22889                .read(cx)
22890                .buffer_for_id(buffer_id, cx)?
22891                .read(cx)
22892                .snapshot();
22893
22894            let mut handled = false;
22895            for (id, _, ExcerptRange { context, .. }) in
22896                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22897            {
22898                if context.start.cmp(&position, &snapshot).is_ge()
22899                    || context.end.cmp(&position, &snapshot).is_lt()
22900                {
22901                    continue;
22902                }
22903                let snapshot = self.buffer.read(cx).snapshot(cx);
22904                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22905
22906                handled = true;
22907                self.clear_row_highlights::<ActiveDebugLine>();
22908
22909                self.go_to_line::<ActiveDebugLine>(
22910                    multibuffer_anchor,
22911                    Some(cx.theme().colors().editor_debugger_active_line_background),
22912                    window,
22913                    cx,
22914                );
22915
22916                cx.notify();
22917            }
22918
22919            handled.then_some(())
22920        })
22921        .is_some()
22922    }
22923
22924    pub fn copy_file_name_without_extension(
22925        &mut self,
22926        _: &CopyFileNameWithoutExtension,
22927        _: &mut Window,
22928        cx: &mut Context<Self>,
22929    ) {
22930        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22931            let file = buffer.read(cx).file()?;
22932            file.path().file_stem()
22933        }) {
22934            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22935        }
22936    }
22937
22938    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22939        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22940            let file = buffer.read(cx).file()?;
22941            Some(file.file_name(cx))
22942        }) {
22943            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22944        }
22945    }
22946
22947    pub fn toggle_git_blame(
22948        &mut self,
22949        _: &::git::Blame,
22950        window: &mut Window,
22951        cx: &mut Context<Self>,
22952    ) {
22953        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22954
22955        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22956            self.start_git_blame(true, window, cx);
22957        }
22958
22959        cx.notify();
22960    }
22961
22962    pub fn toggle_git_blame_inline(
22963        &mut self,
22964        _: &ToggleGitBlameInline,
22965        window: &mut Window,
22966        cx: &mut Context<Self>,
22967    ) {
22968        self.toggle_git_blame_inline_internal(true, window, cx);
22969        cx.notify();
22970    }
22971
22972    pub fn open_git_blame_commit(
22973        &mut self,
22974        _: &OpenGitBlameCommit,
22975        window: &mut Window,
22976        cx: &mut Context<Self>,
22977    ) {
22978        self.open_git_blame_commit_internal(window, cx);
22979    }
22980
22981    fn open_git_blame_commit_internal(
22982        &mut self,
22983        window: &mut Window,
22984        cx: &mut Context<Self>,
22985    ) -> Option<()> {
22986        let blame = self.blame.as_ref()?;
22987        let snapshot = self.snapshot(window, cx);
22988        let cursor = self
22989            .selections
22990            .newest::<Point>(&snapshot.display_snapshot)
22991            .head();
22992        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22993        let (_, blame_entry) = blame
22994            .update(cx, |blame, cx| {
22995                blame
22996                    .blame_for_rows(
22997                        &[RowInfo {
22998                            buffer_id: Some(buffer.remote_id()),
22999                            buffer_row: Some(point.row),
23000                            ..Default::default()
23001                        }],
23002                        cx,
23003                    )
23004                    .next()
23005            })
23006            .flatten()?;
23007        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23008        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23009        let workspace = self.workspace()?.downgrade();
23010        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23011        None
23012    }
23013
23014    pub fn git_blame_inline_enabled(&self) -> bool {
23015        self.git_blame_inline_enabled
23016    }
23017
23018    pub fn toggle_selection_menu(
23019        &mut self,
23020        _: &ToggleSelectionMenu,
23021        _: &mut Window,
23022        cx: &mut Context<Self>,
23023    ) {
23024        self.show_selection_menu = self
23025            .show_selection_menu
23026            .map(|show_selections_menu| !show_selections_menu)
23027            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23028
23029        cx.notify();
23030    }
23031
23032    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23033        self.show_selection_menu
23034            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23035    }
23036
23037    fn start_git_blame(
23038        &mut self,
23039        user_triggered: bool,
23040        window: &mut Window,
23041        cx: &mut Context<Self>,
23042    ) {
23043        if let Some(project) = self.project() {
23044            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23045                && buffer.read(cx).file().is_none()
23046            {
23047                return;
23048            }
23049
23050            let focused = self.focus_handle(cx).contains_focused(window, cx);
23051
23052            let project = project.clone();
23053            let blame = cx
23054                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23055            self.blame_subscription =
23056                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23057            self.blame = Some(blame);
23058        }
23059    }
23060
23061    fn toggle_git_blame_inline_internal(
23062        &mut self,
23063        user_triggered: bool,
23064        window: &mut Window,
23065        cx: &mut Context<Self>,
23066    ) {
23067        if self.git_blame_inline_enabled {
23068            self.git_blame_inline_enabled = false;
23069            self.show_git_blame_inline = false;
23070            self.show_git_blame_inline_delay_task.take();
23071        } else {
23072            self.git_blame_inline_enabled = true;
23073            self.start_git_blame_inline(user_triggered, window, cx);
23074        }
23075
23076        cx.notify();
23077    }
23078
23079    fn start_git_blame_inline(
23080        &mut self,
23081        user_triggered: bool,
23082        window: &mut Window,
23083        cx: &mut Context<Self>,
23084    ) {
23085        self.start_git_blame(user_triggered, window, cx);
23086
23087        if ProjectSettings::get_global(cx)
23088            .git
23089            .inline_blame_delay()
23090            .is_some()
23091        {
23092            self.start_inline_blame_timer(window, cx);
23093        } else {
23094            self.show_git_blame_inline = true
23095        }
23096    }
23097
23098    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23099        self.blame.as_ref()
23100    }
23101
23102    pub fn show_git_blame_gutter(&self) -> bool {
23103        self.show_git_blame_gutter
23104    }
23105
23106    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23107        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23108    }
23109
23110    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23111        self.show_git_blame_inline
23112            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23113            && !self.newest_selection_head_on_empty_line(cx)
23114            && self.has_blame_entries(cx)
23115    }
23116
23117    fn has_blame_entries(&self, cx: &App) -> bool {
23118        self.blame()
23119            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23120    }
23121
23122    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23123        let cursor_anchor = self.selections.newest_anchor().head();
23124
23125        let snapshot = self.buffer.read(cx).snapshot(cx);
23126        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23127
23128        snapshot.line_len(buffer_row) == 0
23129    }
23130
23131    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23132        let buffer_and_selection = maybe!({
23133            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23134            let selection_range = selection.range();
23135
23136            let multi_buffer = self.buffer().read(cx);
23137            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23138            let buffer_ranges = multi_buffer_snapshot
23139                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23140
23141            let (buffer, range, _) = if selection.reversed {
23142                buffer_ranges.first()
23143            } else {
23144                buffer_ranges.last()
23145            }?;
23146
23147            let buffer_range = range.to_point(buffer);
23148
23149            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23150                return Some((
23151                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23152                    buffer_range.start.row..buffer_range.end.row,
23153                ));
23154            };
23155
23156            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23157            let start =
23158                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23159            let end =
23160                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23161
23162            Some((
23163                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23164                start.row..end.row,
23165            ))
23166        });
23167
23168        let Some((buffer, selection)) = buffer_and_selection else {
23169            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23170        };
23171
23172        let Some(project) = self.project() else {
23173            return Task::ready(Err(anyhow!("editor does not have project")));
23174        };
23175
23176        project.update(cx, |project, cx| {
23177            project.get_permalink_to_line(&buffer, selection, cx)
23178        })
23179    }
23180
23181    pub fn copy_permalink_to_line(
23182        &mut self,
23183        _: &CopyPermalinkToLine,
23184        window: &mut Window,
23185        cx: &mut Context<Self>,
23186    ) {
23187        let permalink_task = self.get_permalink_to_line(cx);
23188        let workspace = self.workspace();
23189
23190        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23191            Ok(permalink) => {
23192                cx.update(|_, cx| {
23193                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23194                })
23195                .ok();
23196            }
23197            Err(err) => {
23198                let message = format!("Failed to copy permalink: {err}");
23199
23200                anyhow::Result::<()>::Err(err).log_err();
23201
23202                if let Some(workspace) = workspace {
23203                    workspace
23204                        .update_in(cx, |workspace, _, cx| {
23205                            struct CopyPermalinkToLine;
23206
23207                            workspace.show_toast(
23208                                Toast::new(
23209                                    NotificationId::unique::<CopyPermalinkToLine>(),
23210                                    message,
23211                                ),
23212                                cx,
23213                            )
23214                        })
23215                        .ok();
23216                }
23217            }
23218        })
23219        .detach();
23220    }
23221
23222    pub fn copy_file_location(
23223        &mut self,
23224        _: &CopyFileLocation,
23225        _: &mut Window,
23226        cx: &mut Context<Self>,
23227    ) {
23228        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23229
23230        let start_line = selection.start.row + 1;
23231        let end_line = selection.end.row + 1;
23232
23233        let end_line = if selection.end.column == 0 && end_line > start_line {
23234            end_line - 1
23235        } else {
23236            end_line
23237        };
23238
23239        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23240            let project = self.project()?.read(cx);
23241            let file = buffer.read(cx).file()?;
23242            let path = file.path().display(project.path_style(cx));
23243
23244            let location = if start_line == end_line {
23245                format!("{path}:{start_line}")
23246            } else {
23247                format!("{path}:{start_line}-{end_line}")
23248            };
23249            Some(location)
23250        }) {
23251            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23252        }
23253    }
23254
23255    pub fn open_permalink_to_line(
23256        &mut self,
23257        _: &OpenPermalinkToLine,
23258        window: &mut Window,
23259        cx: &mut Context<Self>,
23260    ) {
23261        let permalink_task = self.get_permalink_to_line(cx);
23262        let workspace = self.workspace();
23263
23264        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23265            Ok(permalink) => {
23266                cx.update(|_, cx| {
23267                    cx.open_url(permalink.as_ref());
23268                })
23269                .ok();
23270            }
23271            Err(err) => {
23272                let message = format!("Failed to open permalink: {err}");
23273
23274                anyhow::Result::<()>::Err(err).log_err();
23275
23276                if let Some(workspace) = workspace {
23277                    workspace.update(cx, |workspace, cx| {
23278                        struct OpenPermalinkToLine;
23279
23280                        workspace.show_toast(
23281                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23282                            cx,
23283                        )
23284                    });
23285                }
23286            }
23287        })
23288        .detach();
23289    }
23290
23291    pub fn insert_uuid_v4(
23292        &mut self,
23293        _: &InsertUuidV4,
23294        window: &mut Window,
23295        cx: &mut Context<Self>,
23296    ) {
23297        self.insert_uuid(UuidVersion::V4, window, cx);
23298    }
23299
23300    pub fn insert_uuid_v7(
23301        &mut self,
23302        _: &InsertUuidV7,
23303        window: &mut Window,
23304        cx: &mut Context<Self>,
23305    ) {
23306        self.insert_uuid(UuidVersion::V7, window, cx);
23307    }
23308
23309    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23310        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23311        self.transact(window, cx, |this, window, cx| {
23312            let edits = this
23313                .selections
23314                .all::<Point>(&this.display_snapshot(cx))
23315                .into_iter()
23316                .map(|selection| {
23317                    let uuid = match version {
23318                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23319                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23320                    };
23321
23322                    (selection.range(), uuid.to_string())
23323                });
23324            this.edit(edits, cx);
23325            this.refresh_edit_prediction(true, false, window, cx);
23326        });
23327    }
23328
23329    pub fn open_selections_in_multibuffer(
23330        &mut self,
23331        _: &OpenSelectionsInMultibuffer,
23332        window: &mut Window,
23333        cx: &mut Context<Self>,
23334    ) {
23335        let multibuffer = self.buffer.read(cx);
23336
23337        let Some(buffer) = multibuffer.as_singleton() else {
23338            return;
23339        };
23340
23341        let Some(workspace) = self.workspace() else {
23342            return;
23343        };
23344
23345        let title = multibuffer.title(cx).to_string();
23346
23347        let locations = self
23348            .selections
23349            .all_anchors(&self.display_snapshot(cx))
23350            .iter()
23351            .map(|selection| {
23352                (
23353                    buffer.clone(),
23354                    (selection.start.text_anchor..selection.end.text_anchor)
23355                        .to_point(buffer.read(cx)),
23356                )
23357            })
23358            .into_group_map();
23359
23360        cx.spawn_in(window, async move |_, cx| {
23361            workspace.update_in(cx, |workspace, window, cx| {
23362                Self::open_locations_in_multibuffer(
23363                    workspace,
23364                    locations,
23365                    format!("Selections for '{title}'"),
23366                    false,
23367                    false,
23368                    MultibufferSelectionMode::All,
23369                    window,
23370                    cx,
23371                );
23372            })
23373        })
23374        .detach();
23375    }
23376
23377    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23378    /// last highlight added will be used.
23379    ///
23380    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23381    pub fn highlight_rows<T: 'static>(
23382        &mut self,
23383        range: Range<Anchor>,
23384        color: Hsla,
23385        options: RowHighlightOptions,
23386        cx: &mut Context<Self>,
23387    ) {
23388        let snapshot = self.buffer().read(cx).snapshot(cx);
23389        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23390        let ix = row_highlights.binary_search_by(|highlight| {
23391            Ordering::Equal
23392                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23393                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23394        });
23395
23396        if let Err(mut ix) = ix {
23397            let index = post_inc(&mut self.highlight_order);
23398
23399            // If this range intersects with the preceding highlight, then merge it with
23400            // the preceding highlight. Otherwise insert a new highlight.
23401            let mut merged = false;
23402            if ix > 0 {
23403                let prev_highlight = &mut row_highlights[ix - 1];
23404                if prev_highlight
23405                    .range
23406                    .end
23407                    .cmp(&range.start, &snapshot)
23408                    .is_ge()
23409                {
23410                    ix -= 1;
23411                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23412                        prev_highlight.range.end = range.end;
23413                    }
23414                    merged = true;
23415                    prev_highlight.index = index;
23416                    prev_highlight.color = color;
23417                    prev_highlight.options = options;
23418                }
23419            }
23420
23421            if !merged {
23422                row_highlights.insert(
23423                    ix,
23424                    RowHighlight {
23425                        range,
23426                        index,
23427                        color,
23428                        options,
23429                        type_id: TypeId::of::<T>(),
23430                    },
23431                );
23432            }
23433
23434            // If any of the following highlights intersect with this one, merge them.
23435            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23436                let highlight = &row_highlights[ix];
23437                if next_highlight
23438                    .range
23439                    .start
23440                    .cmp(&highlight.range.end, &snapshot)
23441                    .is_le()
23442                {
23443                    if next_highlight
23444                        .range
23445                        .end
23446                        .cmp(&highlight.range.end, &snapshot)
23447                        .is_gt()
23448                    {
23449                        row_highlights[ix].range.end = next_highlight.range.end;
23450                    }
23451                    row_highlights.remove(ix + 1);
23452                } else {
23453                    break;
23454                }
23455            }
23456        }
23457    }
23458
23459    /// Remove any highlighted row ranges of the given type that intersect the
23460    /// given ranges.
23461    pub fn remove_highlighted_rows<T: 'static>(
23462        &mut self,
23463        ranges_to_remove: Vec<Range<Anchor>>,
23464        cx: &mut Context<Self>,
23465    ) {
23466        let snapshot = self.buffer().read(cx).snapshot(cx);
23467        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23468        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23469        row_highlights.retain(|highlight| {
23470            while let Some(range_to_remove) = ranges_to_remove.peek() {
23471                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23472                    Ordering::Less | Ordering::Equal => {
23473                        ranges_to_remove.next();
23474                    }
23475                    Ordering::Greater => {
23476                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23477                            Ordering::Less | Ordering::Equal => {
23478                                return false;
23479                            }
23480                            Ordering::Greater => break,
23481                        }
23482                    }
23483                }
23484            }
23485
23486            true
23487        })
23488    }
23489
23490    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23491    pub fn clear_row_highlights<T: 'static>(&mut self) {
23492        self.highlighted_rows.remove(&TypeId::of::<T>());
23493    }
23494
23495    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23496    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23497        self.highlighted_rows
23498            .get(&TypeId::of::<T>())
23499            .map_or(&[] as &[_], |vec| vec.as_slice())
23500            .iter()
23501            .map(|highlight| (highlight.range.clone(), highlight.color))
23502    }
23503
23504    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23505    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23506    /// Allows to ignore certain kinds of highlights.
23507    pub fn highlighted_display_rows(
23508        &self,
23509        window: &mut Window,
23510        cx: &mut App,
23511    ) -> BTreeMap<DisplayRow, LineHighlight> {
23512        let snapshot = self.snapshot(window, cx);
23513        let mut used_highlight_orders = HashMap::default();
23514        self.highlighted_rows
23515            .iter()
23516            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23517            .fold(
23518                BTreeMap::<DisplayRow, LineHighlight>::new(),
23519                |mut unique_rows, highlight| {
23520                    let start = highlight.range.start.to_display_point(&snapshot);
23521                    let end = highlight.range.end.to_display_point(&snapshot);
23522                    let start_row = start.row().0;
23523                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23524                    {
23525                        end.row().0.saturating_sub(1)
23526                    } else {
23527                        end.row().0
23528                    };
23529                    for row in start_row..=end_row {
23530                        let used_index =
23531                            used_highlight_orders.entry(row).or_insert(highlight.index);
23532                        if highlight.index >= *used_index {
23533                            *used_index = highlight.index;
23534                            unique_rows.insert(
23535                                DisplayRow(row),
23536                                LineHighlight {
23537                                    include_gutter: highlight.options.include_gutter,
23538                                    border: None,
23539                                    background: highlight.color.into(),
23540                                    type_id: Some(highlight.type_id),
23541                                },
23542                            );
23543                        }
23544                    }
23545                    unique_rows
23546                },
23547            )
23548    }
23549
23550    pub fn highlighted_display_row_for_autoscroll(
23551        &self,
23552        snapshot: &DisplaySnapshot,
23553    ) -> Option<DisplayRow> {
23554        self.highlighted_rows
23555            .values()
23556            .flat_map(|highlighted_rows| highlighted_rows.iter())
23557            .filter_map(|highlight| {
23558                if highlight.options.autoscroll {
23559                    Some(highlight.range.start.to_display_point(snapshot).row())
23560                } else {
23561                    None
23562                }
23563            })
23564            .min()
23565    }
23566
23567    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23568        self.highlight_background(
23569            HighlightKey::SearchWithinRange,
23570            ranges,
23571            |_, colors| colors.colors().editor_document_highlight_read_background,
23572            cx,
23573        )
23574    }
23575
23576    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23577        self.breadcrumb_header = Some(new_header);
23578    }
23579
23580    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23581        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23582    }
23583
23584    pub fn highlight_background(
23585        &mut self,
23586        key: HighlightKey,
23587        ranges: &[Range<Anchor>],
23588        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23589        cx: &mut Context<Self>,
23590    ) {
23591        self.background_highlights
23592            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23593        self.scrollbar_marker_state.dirty = true;
23594        cx.notify();
23595    }
23596
23597    pub fn clear_background_highlights(
23598        &mut self,
23599        key: HighlightKey,
23600        cx: &mut Context<Self>,
23601    ) -> Option<BackgroundHighlight> {
23602        let text_highlights = self.background_highlights.remove(&key)?;
23603        if !text_highlights.1.is_empty() {
23604            self.scrollbar_marker_state.dirty = true;
23605            cx.notify();
23606        }
23607        Some(text_highlights)
23608    }
23609
23610    pub fn highlight_gutter<T: 'static>(
23611        &mut self,
23612        ranges: impl Into<Vec<Range<Anchor>>>,
23613        color_fetcher: fn(&App) -> Hsla,
23614        cx: &mut Context<Self>,
23615    ) {
23616        self.gutter_highlights
23617            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23618        cx.notify();
23619    }
23620
23621    pub fn clear_gutter_highlights<T: 'static>(
23622        &mut self,
23623        cx: &mut Context<Self>,
23624    ) -> Option<GutterHighlight> {
23625        cx.notify();
23626        self.gutter_highlights.remove(&TypeId::of::<T>())
23627    }
23628
23629    pub fn insert_gutter_highlight<T: 'static>(
23630        &mut self,
23631        range: Range<Anchor>,
23632        color_fetcher: fn(&App) -> Hsla,
23633        cx: &mut Context<Self>,
23634    ) {
23635        let snapshot = self.buffer().read(cx).snapshot(cx);
23636        let mut highlights = self
23637            .gutter_highlights
23638            .remove(&TypeId::of::<T>())
23639            .map(|(_, highlights)| highlights)
23640            .unwrap_or_default();
23641        let ix = highlights.binary_search_by(|highlight| {
23642            Ordering::Equal
23643                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23644                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23645        });
23646        if let Err(ix) = ix {
23647            highlights.insert(ix, range);
23648        }
23649        self.gutter_highlights
23650            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23651    }
23652
23653    pub fn remove_gutter_highlights<T: 'static>(
23654        &mut self,
23655        ranges_to_remove: Vec<Range<Anchor>>,
23656        cx: &mut Context<Self>,
23657    ) {
23658        let snapshot = self.buffer().read(cx).snapshot(cx);
23659        let Some((color_fetcher, mut gutter_highlights)) =
23660            self.gutter_highlights.remove(&TypeId::of::<T>())
23661        else {
23662            return;
23663        };
23664        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23665        gutter_highlights.retain(|highlight| {
23666            while let Some(range_to_remove) = ranges_to_remove.peek() {
23667                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23668                    Ordering::Less | Ordering::Equal => {
23669                        ranges_to_remove.next();
23670                    }
23671                    Ordering::Greater => {
23672                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23673                            Ordering::Less | Ordering::Equal => {
23674                                return false;
23675                            }
23676                            Ordering::Greater => break,
23677                        }
23678                    }
23679                }
23680            }
23681
23682            true
23683        });
23684        self.gutter_highlights
23685            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23686    }
23687
23688    #[cfg(any(test, feature = "test-support"))]
23689    pub fn all_text_highlights(
23690        &self,
23691        window: &mut Window,
23692        cx: &mut Context<Self>,
23693    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23694        let snapshot = self.snapshot(window, cx);
23695        self.display_map.update(cx, |display_map, _| {
23696            display_map
23697                .all_text_highlights()
23698                .map(|(_, highlight)| {
23699                    let (style, ranges) = highlight.as_ref();
23700                    (
23701                        *style,
23702                        ranges
23703                            .iter()
23704                            .map(|range| range.clone().to_display_points(&snapshot))
23705                            .collect(),
23706                    )
23707                })
23708                .collect()
23709        })
23710    }
23711
23712    #[cfg(any(test, feature = "test-support"))]
23713    pub fn all_text_background_highlights(
23714        &self,
23715        window: &mut Window,
23716        cx: &mut Context<Self>,
23717    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23718        let snapshot = self.snapshot(window, cx);
23719        let buffer = &snapshot.buffer_snapshot();
23720        let start = buffer.anchor_before(MultiBufferOffset(0));
23721        let end = buffer.anchor_after(buffer.len());
23722        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23723    }
23724
23725    #[cfg(any(test, feature = "test-support"))]
23726    pub fn sorted_background_highlights_in_range(
23727        &self,
23728        search_range: Range<Anchor>,
23729        display_snapshot: &DisplaySnapshot,
23730        theme: &Theme,
23731    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23732        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23733        res.sort_by(|a, b| {
23734            a.0.start
23735                .cmp(&b.0.start)
23736                .then_with(|| a.0.end.cmp(&b.0.end))
23737                .then_with(|| a.1.cmp(&b.1))
23738        });
23739        res
23740    }
23741
23742    #[cfg(any(test, feature = "test-support"))]
23743    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23744        let snapshot = self.buffer().read(cx).snapshot(cx);
23745
23746        let highlights = self
23747            .background_highlights
23748            .get(&HighlightKey::BufferSearchHighlights);
23749
23750        if let Some((_color, ranges)) = highlights {
23751            ranges
23752                .iter()
23753                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23754                .collect_vec()
23755        } else {
23756            vec![]
23757        }
23758    }
23759
23760    fn document_highlights_for_position<'a>(
23761        &'a self,
23762        position: Anchor,
23763        buffer: &'a MultiBufferSnapshot,
23764    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23765        let read_highlights = self
23766            .background_highlights
23767            .get(&HighlightKey::DocumentHighlightRead)
23768            .map(|h| &h.1);
23769        let write_highlights = self
23770            .background_highlights
23771            .get(&HighlightKey::DocumentHighlightWrite)
23772            .map(|h| &h.1);
23773        let left_position = position.bias_left(buffer);
23774        let right_position = position.bias_right(buffer);
23775        read_highlights
23776            .into_iter()
23777            .chain(write_highlights)
23778            .flat_map(move |ranges| {
23779                let start_ix = match ranges.binary_search_by(|probe| {
23780                    let cmp = probe.end.cmp(&left_position, buffer);
23781                    if cmp.is_ge() {
23782                        Ordering::Greater
23783                    } else {
23784                        Ordering::Less
23785                    }
23786                }) {
23787                    Ok(i) | Err(i) => i,
23788                };
23789
23790                ranges[start_ix..]
23791                    .iter()
23792                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23793            })
23794    }
23795
23796    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23797        self.background_highlights
23798            .get(&key)
23799            .is_some_and(|(_, highlights)| !highlights.is_empty())
23800    }
23801
23802    /// Returns all background highlights for a given range.
23803    ///
23804    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23805    pub fn background_highlights_in_range(
23806        &self,
23807        search_range: Range<Anchor>,
23808        display_snapshot: &DisplaySnapshot,
23809        theme: &Theme,
23810    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23811        let mut results = Vec::new();
23812        for (color_fetcher, ranges) in self.background_highlights.values() {
23813            let start_ix = match ranges.binary_search_by(|probe| {
23814                let cmp = probe
23815                    .end
23816                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23817                if cmp.is_gt() {
23818                    Ordering::Greater
23819                } else {
23820                    Ordering::Less
23821                }
23822            }) {
23823                Ok(i) | Err(i) => i,
23824            };
23825            for (index, range) in ranges[start_ix..].iter().enumerate() {
23826                if range
23827                    .start
23828                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23829                    .is_ge()
23830                {
23831                    break;
23832                }
23833
23834                let color = color_fetcher(&(start_ix + index), theme);
23835                let start = range.start.to_display_point(display_snapshot);
23836                let end = range.end.to_display_point(display_snapshot);
23837                results.push((start..end, color))
23838            }
23839        }
23840        results
23841    }
23842
23843    pub fn gutter_highlights_in_range(
23844        &self,
23845        search_range: Range<Anchor>,
23846        display_snapshot: &DisplaySnapshot,
23847        cx: &App,
23848    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23849        let mut results = Vec::new();
23850        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23851            let color = color_fetcher(cx);
23852            let start_ix = match ranges.binary_search_by(|probe| {
23853                let cmp = probe
23854                    .end
23855                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23856                if cmp.is_gt() {
23857                    Ordering::Greater
23858                } else {
23859                    Ordering::Less
23860                }
23861            }) {
23862                Ok(i) | Err(i) => i,
23863            };
23864            for range in &ranges[start_ix..] {
23865                if range
23866                    .start
23867                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23868                    .is_ge()
23869                {
23870                    break;
23871                }
23872
23873                let start = range.start.to_display_point(display_snapshot);
23874                let end = range.end.to_display_point(display_snapshot);
23875                results.push((start..end, color))
23876            }
23877        }
23878        results
23879    }
23880
23881    /// Get the text ranges corresponding to the redaction query
23882    pub fn redacted_ranges(
23883        &self,
23884        search_range: Range<Anchor>,
23885        display_snapshot: &DisplaySnapshot,
23886        cx: &App,
23887    ) -> Vec<Range<DisplayPoint>> {
23888        display_snapshot
23889            .buffer_snapshot()
23890            .redacted_ranges(search_range, |file| {
23891                if let Some(file) = file {
23892                    file.is_private()
23893                        && EditorSettings::get(
23894                            Some(SettingsLocation {
23895                                worktree_id: file.worktree_id(cx),
23896                                path: file.path().as_ref(),
23897                            }),
23898                            cx,
23899                        )
23900                        .redact_private_values
23901                } else {
23902                    false
23903                }
23904            })
23905            .map(|range| {
23906                range.start.to_display_point(display_snapshot)
23907                    ..range.end.to_display_point(display_snapshot)
23908            })
23909            .collect()
23910    }
23911
23912    pub fn highlight_text_key(
23913        &mut self,
23914        key: HighlightKey,
23915        ranges: Vec<Range<Anchor>>,
23916        style: HighlightStyle,
23917        merge: bool,
23918        cx: &mut Context<Self>,
23919    ) {
23920        self.display_map.update(cx, |map, cx| {
23921            map.highlight_text(key, ranges, style, merge, cx);
23922        });
23923        cx.notify();
23924    }
23925
23926    pub fn highlight_text(
23927        &mut self,
23928        key: HighlightKey,
23929        ranges: Vec<Range<Anchor>>,
23930        style: HighlightStyle,
23931        cx: &mut Context<Self>,
23932    ) {
23933        self.display_map.update(cx, |map, cx| {
23934            map.highlight_text(key, ranges, style, false, cx)
23935        });
23936        cx.notify();
23937    }
23938
23939    pub fn text_highlights<'a>(
23940        &'a self,
23941        key: HighlightKey,
23942        cx: &'a App,
23943    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23944        self.display_map.read(cx).text_highlights(key)
23945    }
23946
23947    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23948        let cleared = self
23949            .display_map
23950            .update(cx, |map, _| map.clear_highlights(key));
23951        if cleared {
23952            cx.notify();
23953        }
23954    }
23955
23956    pub fn clear_highlights_with(
23957        &mut self,
23958        f: &mut dyn FnMut(&HighlightKey) -> bool,
23959        cx: &mut Context<Self>,
23960    ) {
23961        let cleared = self
23962            .display_map
23963            .update(cx, |map, _| map.clear_highlights_with(f));
23964        if cleared {
23965            cx.notify();
23966        }
23967    }
23968
23969    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23970        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23971            && self.focus_handle.is_focused(window)
23972    }
23973
23974    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23975        self.show_cursor_when_unfocused = is_enabled;
23976        cx.notify();
23977    }
23978
23979    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23980        cx.notify();
23981    }
23982
23983    fn on_debug_session_event(
23984        &mut self,
23985        _session: Entity<Session>,
23986        event: &SessionEvent,
23987        cx: &mut Context<Self>,
23988    ) {
23989        if let SessionEvent::InvalidateInlineValue = event {
23990            self.refresh_inline_values(cx);
23991        }
23992    }
23993
23994    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23995        let Some(semantics) = self.semantics_provider.clone() else {
23996            return;
23997        };
23998
23999        if !self.inline_value_cache.enabled {
24000            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24001            self.splice_inlays(&inlays, Vec::new(), cx);
24002            return;
24003        }
24004
24005        let current_execution_position = self
24006            .highlighted_rows
24007            .get(&TypeId::of::<ActiveDebugLine>())
24008            .and_then(|lines| lines.last().map(|line| line.range.end));
24009
24010        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24011            let inline_values = editor
24012                .update(cx, |editor, cx| {
24013                    let Some(current_execution_position) = current_execution_position else {
24014                        return Some(Task::ready(Ok(Vec::new())));
24015                    };
24016
24017                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24018                        let snapshot = buffer.snapshot(cx);
24019
24020                        let excerpt = snapshot.excerpt_containing(
24021                            current_execution_position..current_execution_position,
24022                        )?;
24023
24024                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24025                    })?;
24026
24027                    if current_execution_position
24028                        .text_anchor
24029                        .buffer_id
24030                        .is_some_and(|id| id != buffer.read(cx).remote_id())
24031                    {
24032                        return Some(Task::ready(Ok(Vec::new())));
24033                    }
24034
24035                    let range =
24036                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24037
24038                    semantics.inline_values(buffer, range, cx)
24039                })
24040                .ok()
24041                .flatten()?
24042                .await
24043                .context("refreshing debugger inlays")
24044                .log_err()?;
24045
24046            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24047
24048            for (buffer_id, inline_value) in inline_values
24049                .into_iter()
24050                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24051            {
24052                buffer_inline_values
24053                    .entry(buffer_id)
24054                    .or_default()
24055                    .push(inline_value);
24056            }
24057
24058            editor
24059                .update(cx, |editor, cx| {
24060                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24061                    let mut new_inlays = Vec::default();
24062
24063                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24064                        let buffer_id = buffer_snapshot.remote_id();
24065                        buffer_inline_values
24066                            .get(&buffer_id)
24067                            .into_iter()
24068                            .flatten()
24069                            .for_each(|hint| {
24070                                let inlay = Inlay::debugger(
24071                                    post_inc(&mut editor.next_inlay_id),
24072                                    Anchor::in_buffer(excerpt_id, hint.position),
24073                                    hint.text(),
24074                                );
24075                                if !inlay.text().chars().contains(&'\n') {
24076                                    new_inlays.push(inlay);
24077                                }
24078                            });
24079                    }
24080
24081                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24082                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24083
24084                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24085                })
24086                .ok()?;
24087            Some(())
24088        });
24089    }
24090
24091    fn on_buffer_event(
24092        &mut self,
24093        multibuffer: &Entity<MultiBuffer>,
24094        event: &multi_buffer::Event,
24095        window: &mut Window,
24096        cx: &mut Context<Self>,
24097    ) {
24098        match event {
24099            multi_buffer::Event::Edited {
24100                edited_buffer,
24101                is_local,
24102            } => {
24103                self.scrollbar_marker_state.dirty = true;
24104                self.active_indent_guides_state.dirty = true;
24105                self.refresh_active_diagnostics(cx);
24106                self.refresh_code_actions(window, cx);
24107                self.refresh_single_line_folds(window, cx);
24108                let snapshot = self.snapshot(window, cx);
24109                self.refresh_matching_bracket_highlights(&snapshot, cx);
24110                self.refresh_outline_symbols_at_cursor(cx);
24111                self.refresh_sticky_headers(&snapshot, cx);
24112                if *is_local && self.has_active_edit_prediction() {
24113                    self.update_visible_edit_prediction(window, cx);
24114                }
24115
24116                // Clean up orphaned review comments after edits
24117                self.cleanup_orphaned_review_comments(cx);
24118
24119                if let Some(buffer) = edited_buffer {
24120                    if buffer.read(cx).file().is_none() {
24121                        cx.emit(EditorEvent::TitleChanged);
24122                    }
24123
24124                    if self.project.is_some() {
24125                        let buffer_id = buffer.read(cx).remote_id();
24126                        self.register_buffer(buffer_id, cx);
24127                        self.update_lsp_data(Some(buffer_id), window, cx);
24128                        self.refresh_inlay_hints(
24129                            InlayHintRefreshReason::BufferEdited(buffer_id),
24130                            cx,
24131                        );
24132                    }
24133                }
24134
24135                cx.emit(EditorEvent::BufferEdited);
24136                cx.emit(SearchEvent::MatchesInvalidated);
24137
24138                let Some(project) = &self.project else { return };
24139                let (telemetry, is_via_ssh) = {
24140                    let project = project.read(cx);
24141                    let telemetry = project.client().telemetry().clone();
24142                    let is_via_ssh = project.is_via_remote_server();
24143                    (telemetry, is_via_ssh)
24144                };
24145                telemetry.log_edit_event("editor", is_via_ssh);
24146            }
24147            multi_buffer::Event::ExcerptsAdded {
24148                buffer,
24149                predecessor,
24150                excerpts,
24151            } => {
24152                let buffer_id = buffer.read(cx).remote_id();
24153                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24154                    && let Some(project) = &self.project
24155                {
24156                    update_uncommitted_diff_for_buffer(
24157                        cx.entity(),
24158                        project,
24159                        [buffer.clone()],
24160                        self.buffer.clone(),
24161                        cx,
24162                    )
24163                    .detach();
24164                }
24165                self.semantic_token_state
24166                    .invalidate_buffer(&buffer.read(cx).remote_id());
24167                self.update_lsp_data(Some(buffer_id), window, cx);
24168                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24169                self.refresh_runnables(None, window, cx);
24170                self.colorize_brackets(false, cx);
24171                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24172                cx.emit(EditorEvent::ExcerptsAdded {
24173                    buffer: buffer.clone(),
24174                    predecessor: *predecessor,
24175                    excerpts: excerpts.clone(),
24176                });
24177            }
24178            multi_buffer::Event::ExcerptsRemoved {
24179                ids,
24180                removed_buffer_ids,
24181            } => {
24182                if let Some(inlay_hints) = &mut self.inlay_hints {
24183                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24184                }
24185                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24186                for buffer_id in removed_buffer_ids {
24187                    self.registered_buffers.remove(buffer_id);
24188                    self.clear_runnables(Some(*buffer_id));
24189                    self.semantic_token_state.invalidate_buffer(buffer_id);
24190                    self.display_map.update(cx, |display_map, cx| {
24191                        display_map.invalidate_semantic_highlights(*buffer_id);
24192                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24193                    });
24194                }
24195
24196                self.display_map.update(cx, |display_map, cx| {
24197                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24198                });
24199
24200                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24201                cx.emit(EditorEvent::ExcerptsRemoved {
24202                    ids: ids.clone(),
24203                    removed_buffer_ids: removed_buffer_ids.clone(),
24204                });
24205            }
24206            multi_buffer::Event::ExcerptsEdited {
24207                excerpt_ids,
24208                buffer_ids,
24209            } => {
24210                self.display_map.update(cx, |map, cx| {
24211                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24212                });
24213                cx.emit(EditorEvent::ExcerptsEdited {
24214                    ids: excerpt_ids.clone(),
24215                });
24216            }
24217            multi_buffer::Event::ExcerptsExpanded { ids } => {
24218                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24219                self.refresh_document_highlights(cx);
24220                let snapshot = multibuffer.read(cx).snapshot(cx);
24221                for id in ids {
24222                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24223                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24224                        self.semantic_token_state
24225                            .invalidate_buffer(&buffer.remote_id());
24226                    }
24227                }
24228                self.colorize_brackets(false, cx);
24229                self.update_lsp_data(None, window, cx);
24230                self.refresh_runnables(None, window, cx);
24231                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24232            }
24233            multi_buffer::Event::Reparsed(buffer_id) => {
24234                self.refresh_runnables(Some(*buffer_id), window, cx);
24235                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24236                self.colorize_brackets(true, cx);
24237                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24238
24239                cx.emit(EditorEvent::Reparsed(*buffer_id));
24240            }
24241            multi_buffer::Event::DiffHunksToggled => {
24242                self.refresh_runnables(None, window, cx);
24243            }
24244            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24245                if !is_fresh_language {
24246                    self.registered_buffers.remove(&buffer_id);
24247                }
24248                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24249                cx.emit(EditorEvent::Reparsed(*buffer_id));
24250                self.update_edit_prediction_settings(cx);
24251                cx.notify();
24252            }
24253            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24254            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24255            multi_buffer::Event::FileHandleChanged
24256            | multi_buffer::Event::Reloaded
24257            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24258            multi_buffer::Event::DiagnosticsUpdated => {
24259                self.update_diagnostics_state(window, cx);
24260            }
24261            _ => {}
24262        };
24263    }
24264
24265    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24266        if !self.diagnostics_enabled() {
24267            return;
24268        }
24269        self.refresh_active_diagnostics(cx);
24270        self.refresh_inline_diagnostics(true, window, cx);
24271        self.scrollbar_marker_state.dirty = true;
24272        cx.notify();
24273    }
24274
24275    pub fn start_temporary_diff_override(&mut self) {
24276        self.load_diff_task.take();
24277        self.temporary_diff_override = true;
24278    }
24279
24280    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24281        self.temporary_diff_override = false;
24282        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24283        self.buffer.update(cx, |buffer, cx| {
24284            buffer.set_all_diff_hunks_collapsed(cx);
24285        });
24286
24287        if let Some(project) = self.project.clone() {
24288            self.load_diff_task = Some(
24289                update_uncommitted_diff_for_buffer(
24290                    cx.entity(),
24291                    &project,
24292                    self.buffer.read(cx).all_buffers(),
24293                    self.buffer.clone(),
24294                    cx,
24295                )
24296                .shared(),
24297            );
24298        }
24299    }
24300
24301    fn on_display_map_changed(
24302        &mut self,
24303        _: Entity<DisplayMap>,
24304        _: &mut Window,
24305        cx: &mut Context<Self>,
24306    ) {
24307        cx.notify();
24308    }
24309
24310    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24311        if !self.mode.is_full() {
24312            return None;
24313        }
24314
24315        let theme_settings = theme::ThemeSettings::get_global(cx);
24316        let theme = cx.theme();
24317        let accent_colors = theme.accents().clone();
24318
24319        let accent_overrides = theme_settings
24320            .theme_overrides
24321            .get(theme.name.as_ref())
24322            .map(|theme_style| &theme_style.accents)
24323            .into_iter()
24324            .flatten()
24325            .chain(
24326                theme_settings
24327                    .experimental_theme_overrides
24328                    .as_ref()
24329                    .map(|overrides| &overrides.accents)
24330                    .into_iter()
24331                    .flatten(),
24332            )
24333            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24334            .collect();
24335
24336        Some(AccentData {
24337            colors: accent_colors,
24338            overrides: accent_overrides,
24339        })
24340    }
24341
24342    fn fetch_applicable_language_settings(
24343        &self,
24344        cx: &App,
24345    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24346        if !self.mode.is_full() {
24347            return HashMap::default();
24348        }
24349
24350        self.buffer().read(cx).all_buffers().into_iter().fold(
24351            HashMap::default(),
24352            |mut acc, buffer| {
24353                let buffer = buffer.read(cx);
24354                let language = buffer.language().map(|language| language.name());
24355                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24356                    let file = buffer.file();
24357                    v.insert(language_settings(language, file, cx).into_owned());
24358                }
24359                acc
24360            },
24361        )
24362    }
24363
24364    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24365        let new_language_settings = self.fetch_applicable_language_settings(cx);
24366        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24367        self.applicable_language_settings = new_language_settings;
24368
24369        let new_accents = self.fetch_accent_data(cx);
24370        let accents_changed = new_accents != self.accent_data;
24371        self.accent_data = new_accents;
24372
24373        if self.diagnostics_enabled() {
24374            let new_severity = EditorSettings::get_global(cx)
24375                .diagnostics_max_severity
24376                .unwrap_or(DiagnosticSeverity::Hint);
24377            self.set_max_diagnostics_severity(new_severity, cx);
24378        }
24379        self.refresh_runnables(None, window, cx);
24380        self.update_edit_prediction_settings(cx);
24381        self.refresh_edit_prediction(true, false, window, cx);
24382        self.refresh_inline_values(cx);
24383
24384        let old_cursor_shape = self.cursor_shape;
24385        let old_show_breadcrumbs = self.show_breadcrumbs;
24386
24387        {
24388            let editor_settings = EditorSettings::get_global(cx);
24389            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24390            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24391            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24392            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24393        }
24394
24395        if old_cursor_shape != self.cursor_shape {
24396            cx.emit(EditorEvent::CursorShapeChanged);
24397        }
24398
24399        if old_show_breadcrumbs != self.show_breadcrumbs {
24400            cx.emit(EditorEvent::BreadcrumbsChanged);
24401        }
24402
24403        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24404            let project_settings = ProjectSettings::get_global(cx);
24405            (
24406                project_settings.session.restore_unsaved_buffers,
24407                project_settings.diagnostics.inline.enabled,
24408                project_settings.git.inline_blame.enabled,
24409            )
24410        };
24411        self.buffer_serialization = self
24412            .should_serialize_buffer()
24413            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24414
24415        if self.mode.is_full() {
24416            if self.show_inline_diagnostics != show_inline_diagnostics {
24417                self.show_inline_diagnostics = show_inline_diagnostics;
24418                self.refresh_inline_diagnostics(false, window, cx);
24419            }
24420
24421            if self.git_blame_inline_enabled != inline_blame_enabled {
24422                self.toggle_git_blame_inline_internal(false, window, cx);
24423            }
24424
24425            let minimap_settings = EditorSettings::get_global(cx).minimap;
24426            if self.minimap_visibility != MinimapVisibility::Disabled {
24427                if self.minimap_visibility.settings_visibility()
24428                    != minimap_settings.minimap_enabled()
24429                {
24430                    self.set_minimap_visibility(
24431                        MinimapVisibility::for_mode(self.mode(), cx),
24432                        window,
24433                        cx,
24434                    );
24435                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24436                    minimap_entity.update(cx, |minimap_editor, cx| {
24437                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24438                    })
24439                }
24440            }
24441
24442            if language_settings_changed || accents_changed {
24443                self.colorize_brackets(true, cx);
24444            }
24445
24446            if language_settings_changed {
24447                self.clear_disabled_lsp_folding_ranges(window, cx);
24448                self.refresh_document_symbols(None, cx);
24449            }
24450
24451            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24452                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24453            }) {
24454                if !inlay_splice.is_empty() {
24455                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24456                }
24457                self.refresh_document_colors(None, window, cx);
24458            }
24459
24460            self.refresh_inlay_hints(
24461                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24462                    self.selections.newest_anchor().head(),
24463                    &self.buffer.read(cx).snapshot(cx),
24464                    cx,
24465                )),
24466                cx,
24467            );
24468
24469            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24470                .global_lsp_settings
24471                .semantic_token_rules
24472                .clone();
24473            let semantic_token_rules_changed = self
24474                .semantic_token_state
24475                .update_rules(new_semantic_token_rules);
24476            if language_settings_changed || semantic_token_rules_changed {
24477                self.invalidate_semantic_tokens(None);
24478                self.refresh_semantic_tokens(None, None, cx);
24479            }
24480        }
24481
24482        cx.notify();
24483    }
24484
24485    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24486        if !self.mode.is_full() {
24487            return;
24488        }
24489
24490        let new_accents = self.fetch_accent_data(cx);
24491        if new_accents != self.accent_data {
24492            self.accent_data = new_accents;
24493            self.colorize_brackets(true, cx);
24494        }
24495
24496        self.invalidate_semantic_tokens(None);
24497        self.refresh_semantic_tokens(None, None, cx);
24498    }
24499
24500    pub fn set_searchable(&mut self, searchable: bool) {
24501        self.searchable = searchable;
24502    }
24503
24504    pub fn searchable(&self) -> bool {
24505        self.searchable
24506    }
24507
24508    pub fn open_excerpts_in_split(
24509        &mut self,
24510        _: &OpenExcerptsSplit,
24511        window: &mut Window,
24512        cx: &mut Context<Self>,
24513    ) {
24514        self.open_excerpts_common(None, true, window, cx)
24515    }
24516
24517    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24518        self.open_excerpts_common(None, false, window, cx)
24519    }
24520
24521    pub(crate) fn open_excerpts_common(
24522        &mut self,
24523        jump_data: Option<JumpData>,
24524        split: bool,
24525        window: &mut Window,
24526        cx: &mut Context<Self>,
24527    ) {
24528        if self.buffer.read(cx).is_singleton() {
24529            cx.propagate();
24530            return;
24531        }
24532
24533        let mut new_selections_by_buffer = HashMap::default();
24534        match &jump_data {
24535            Some(JumpData::MultiBufferPoint {
24536                excerpt_id,
24537                position,
24538                anchor,
24539                line_offset_from_top,
24540            }) => {
24541                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24542                if let Some(buffer) = multi_buffer_snapshot
24543                    .buffer_id_for_excerpt(*excerpt_id)
24544                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24545                {
24546                    let buffer_snapshot = buffer.read(cx).snapshot();
24547                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24548                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24549                    } else {
24550                        buffer_snapshot.clip_point(*position, Bias::Left)
24551                    };
24552                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24553                    new_selections_by_buffer.insert(
24554                        buffer,
24555                        (
24556                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24557                            Some(*line_offset_from_top),
24558                        ),
24559                    );
24560                }
24561            }
24562            Some(JumpData::MultiBufferRow {
24563                row,
24564                line_offset_from_top,
24565            }) => {
24566                let point = MultiBufferPoint::new(row.0, 0);
24567                if let Some((buffer, buffer_point, _)) =
24568                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24569                {
24570                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24571                    new_selections_by_buffer
24572                        .entry(buffer)
24573                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24574                        .0
24575                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24576                }
24577            }
24578            None => {
24579                let selections = self
24580                    .selections
24581                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24582                let multi_buffer = self.buffer.read(cx);
24583                for selection in selections {
24584                    for (snapshot, range, _, anchor) in multi_buffer
24585                        .snapshot(cx)
24586                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24587                    {
24588                        if let Some(anchor) = anchor {
24589                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24590                            else {
24591                                continue;
24592                            };
24593                            let offset = text::ToOffset::to_offset(
24594                                &anchor.text_anchor,
24595                                &buffer_handle.read(cx).snapshot(),
24596                            );
24597                            let range = BufferOffset(offset)..BufferOffset(offset);
24598                            new_selections_by_buffer
24599                                .entry(buffer_handle)
24600                                .or_insert((Vec::new(), None))
24601                                .0
24602                                .push(range)
24603                        } else {
24604                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24605                            else {
24606                                continue;
24607                            };
24608                            new_selections_by_buffer
24609                                .entry(buffer_handle)
24610                                .or_insert((Vec::new(), None))
24611                                .0
24612                                .push(range)
24613                        }
24614                    }
24615                }
24616            }
24617        }
24618
24619        if self.delegate_open_excerpts {
24620            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24621                .into_iter()
24622                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24623                .collect();
24624            if !selections_by_buffer.is_empty() {
24625                cx.emit(EditorEvent::OpenExcerptsRequested {
24626                    selections_by_buffer,
24627                    split,
24628                });
24629            }
24630            return;
24631        }
24632
24633        let Some(workspace) = self.workspace() else {
24634            cx.propagate();
24635            return;
24636        };
24637
24638        new_selections_by_buffer
24639            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24640
24641        if new_selections_by_buffer.is_empty() {
24642            return;
24643        }
24644
24645        Self::open_buffers_in_workspace(
24646            workspace.downgrade(),
24647            new_selections_by_buffer,
24648            split,
24649            window,
24650            cx,
24651        );
24652    }
24653
24654    pub(crate) fn open_buffers_in_workspace(
24655        workspace: WeakEntity<Workspace>,
24656        new_selections_by_buffer: HashMap<
24657            Entity<language::Buffer>,
24658            (Vec<Range<BufferOffset>>, Option<u32>),
24659        >,
24660        split: bool,
24661        window: &mut Window,
24662        cx: &mut App,
24663    ) {
24664        // We defer the pane interaction because we ourselves are a workspace item
24665        // and activating a new item causes the pane to call a method on us reentrantly,
24666        // which panics if we're on the stack.
24667        window.defer(cx, move |window, cx| {
24668            workspace
24669                .update(cx, |workspace, cx| {
24670                    let pane = if split {
24671                        workspace.adjacent_pane(window, cx)
24672                    } else {
24673                        workspace.active_pane().clone()
24674                    };
24675
24676                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24677                        let buffer_read = buffer.read(cx);
24678                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24679                            (true, project::File::from_dyn(Some(file)).is_some())
24680                        } else {
24681                            (false, false)
24682                        };
24683
24684                        // If project file is none workspace.open_project_item will fail to open the excerpt
24685                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24686                        // so we check if there's a tab match in that case first
24687                        let editor = (!has_file || !is_project_file)
24688                            .then(|| {
24689                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24690                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24691                                // Instead, we try to activate the existing editor in the pane first.
24692                                let (editor, pane_item_index, pane_item_id) =
24693                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24694                                        let editor = item.downcast::<Editor>()?;
24695                                        let singleton_buffer =
24696                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24697                                        if singleton_buffer == buffer {
24698                                            Some((editor, i, item.item_id()))
24699                                        } else {
24700                                            None
24701                                        }
24702                                    })?;
24703                                pane.update(cx, |pane, cx| {
24704                                    pane.activate_item(pane_item_index, true, true, window, cx);
24705                                    if !PreviewTabsSettings::get_global(cx)
24706                                        .enable_preview_from_multibuffer
24707                                    {
24708                                        pane.unpreview_item_if_preview(pane_item_id);
24709                                    }
24710                                });
24711                                Some(editor)
24712                            })
24713                            .flatten()
24714                            .unwrap_or_else(|| {
24715                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24716                                    .enable_keep_preview_on_code_navigation;
24717                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24718                                    .enable_preview_from_multibuffer;
24719                                workspace.open_project_item::<Self>(
24720                                    pane.clone(),
24721                                    buffer,
24722                                    true,
24723                                    true,
24724                                    keep_old_preview,
24725                                    allow_new_preview,
24726                                    window,
24727                                    cx,
24728                                )
24729                            });
24730
24731                        editor.update(cx, |editor, cx| {
24732                            if has_file && !is_project_file {
24733                                editor.set_read_only(true);
24734                            }
24735                            let autoscroll = match scroll_offset {
24736                                Some(scroll_offset) => {
24737                                    Autoscroll::top_relative(scroll_offset as usize)
24738                                }
24739                                None => Autoscroll::newest(),
24740                            };
24741                            let nav_history = editor.nav_history.take();
24742                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24743                            let Some((excerpt_id, _, buffer_snapshot)) =
24744                                multibuffer_snapshot.as_singleton()
24745                            else {
24746                                return;
24747                            };
24748                            editor.change_selections(
24749                                SelectionEffects::scroll(autoscroll),
24750                                window,
24751                                cx,
24752                                |s| {
24753                                    s.select_ranges(ranges.into_iter().map(|range| {
24754                                        let range = buffer_snapshot.anchor_before(range.start)
24755                                            ..buffer_snapshot.anchor_after(range.end);
24756                                        multibuffer_snapshot
24757                                            .anchor_range_in_excerpt(excerpt_id, range)
24758                                            .unwrap()
24759                                    }));
24760                                },
24761                            );
24762                            editor.nav_history = nav_history;
24763                        });
24764                    }
24765                })
24766                .ok();
24767        });
24768    }
24769
24770    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24771        let snapshot = self.buffer.read(cx).read(cx);
24772        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24773        Some(
24774            ranges
24775                .iter()
24776                .map(move |range| {
24777                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24778                })
24779                .collect(),
24780        )
24781    }
24782
24783    fn selection_replacement_ranges(
24784        &self,
24785        range: Range<MultiBufferOffsetUtf16>,
24786        cx: &mut App,
24787    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24788        let selections = self
24789            .selections
24790            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24791        let newest_selection = selections
24792            .iter()
24793            .max_by_key(|selection| selection.id)
24794            .unwrap();
24795        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24796        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24797        let snapshot = self.buffer.read(cx).read(cx);
24798        selections
24799            .into_iter()
24800            .map(|mut selection| {
24801                selection.start.0.0 =
24802                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24803                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24804                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24805                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24806            })
24807            .collect()
24808    }
24809
24810    fn report_editor_event(
24811        &self,
24812        reported_event: ReportEditorEvent,
24813        file_extension: Option<String>,
24814        cx: &App,
24815    ) {
24816        if cfg!(any(test, feature = "test-support")) {
24817            return;
24818        }
24819
24820        let Some(project) = &self.project else { return };
24821
24822        // If None, we are in a file without an extension
24823        let file = self
24824            .buffer
24825            .read(cx)
24826            .as_singleton()
24827            .and_then(|b| b.read(cx).file());
24828        let file_extension = file_extension.or(file
24829            .as_ref()
24830            .and_then(|file| Path::new(file.file_name(cx)).extension())
24831            .and_then(|e| e.to_str())
24832            .map(|a| a.to_string()));
24833
24834        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24835            .map(|vim_mode| vim_mode.0)
24836            .unwrap_or(false);
24837
24838        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24839        let copilot_enabled = edit_predictions_provider
24840            == language::language_settings::EditPredictionProvider::Copilot;
24841        let copilot_enabled_for_language = self
24842            .buffer
24843            .read(cx)
24844            .language_settings(cx)
24845            .show_edit_predictions;
24846
24847        let project = project.read(cx);
24848        let event_type = reported_event.event_type();
24849
24850        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24851            telemetry::event!(
24852                event_type,
24853                type = if auto_saved {"autosave"} else {"manual"},
24854                file_extension,
24855                vim_mode,
24856                copilot_enabled,
24857                copilot_enabled_for_language,
24858                edit_predictions_provider,
24859                is_via_ssh = project.is_via_remote_server(),
24860            );
24861        } else {
24862            telemetry::event!(
24863                event_type,
24864                file_extension,
24865                vim_mode,
24866                copilot_enabled,
24867                copilot_enabled_for_language,
24868                edit_predictions_provider,
24869                is_via_ssh = project.is_via_remote_server(),
24870            );
24871        };
24872    }
24873
24874    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24875    /// with each line being an array of {text, highlight} objects.
24876    fn copy_highlight_json(
24877        &mut self,
24878        _: &CopyHighlightJson,
24879        window: &mut Window,
24880        cx: &mut Context<Self>,
24881    ) {
24882        #[derive(Serialize)]
24883        struct Chunk<'a> {
24884            text: String,
24885            highlight: Option<&'a str>,
24886        }
24887
24888        let snapshot = self.buffer.read(cx).snapshot(cx);
24889        let range = self
24890            .selected_text_range(false, window, cx)
24891            .and_then(|selection| {
24892                if selection.range.is_empty() {
24893                    None
24894                } else {
24895                    Some(
24896                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24897                            selection.range.start,
24898                        )))
24899                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24900                                selection.range.end,
24901                            ))),
24902                    )
24903                }
24904            })
24905            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24906
24907        let chunks = snapshot.chunks(range, true);
24908        let mut lines = Vec::new();
24909        let mut line: VecDeque<Chunk> = VecDeque::new();
24910
24911        let Some(style) = self.style.as_ref() else {
24912            return;
24913        };
24914
24915        for chunk in chunks {
24916            let highlight = chunk
24917                .syntax_highlight_id
24918                .and_then(|id| id.name(&style.syntax));
24919            let mut chunk_lines = chunk.text.split('\n').peekable();
24920            while let Some(text) = chunk_lines.next() {
24921                let mut merged_with_last_token = false;
24922                if let Some(last_token) = line.back_mut()
24923                    && last_token.highlight == highlight
24924                {
24925                    last_token.text.push_str(text);
24926                    merged_with_last_token = true;
24927                }
24928
24929                if !merged_with_last_token {
24930                    line.push_back(Chunk {
24931                        text: text.into(),
24932                        highlight,
24933                    });
24934                }
24935
24936                if chunk_lines.peek().is_some() {
24937                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24938                        line.pop_front();
24939                    }
24940                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24941                        line.pop_back();
24942                    }
24943
24944                    lines.push(mem::take(&mut line));
24945                }
24946            }
24947        }
24948
24949        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24950            return;
24951        };
24952        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24953    }
24954
24955    pub fn open_context_menu(
24956        &mut self,
24957        _: &OpenContextMenu,
24958        window: &mut Window,
24959        cx: &mut Context<Self>,
24960    ) {
24961        self.request_autoscroll(Autoscroll::newest(), cx);
24962        let position = self
24963            .selections
24964            .newest_display(&self.display_snapshot(cx))
24965            .start;
24966        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24967    }
24968
24969    pub fn replay_insert_event(
24970        &mut self,
24971        text: &str,
24972        relative_utf16_range: Option<Range<isize>>,
24973        window: &mut Window,
24974        cx: &mut Context<Self>,
24975    ) {
24976        if !self.input_enabled {
24977            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24978            return;
24979        }
24980        if let Some(relative_utf16_range) = relative_utf16_range {
24981            let selections = self
24982                .selections
24983                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24984            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24985                let new_ranges = selections.into_iter().map(|range| {
24986                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24987                        range
24988                            .head()
24989                            .0
24990                            .0
24991                            .saturating_add_signed(relative_utf16_range.start),
24992                    ));
24993                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24994                        range
24995                            .head()
24996                            .0
24997                            .0
24998                            .saturating_add_signed(relative_utf16_range.end),
24999                    ));
25000                    start..end
25001                });
25002                s.select_ranges(new_ranges);
25003            });
25004        }
25005
25006        self.handle_input(text, window, cx);
25007    }
25008
25009    pub fn is_focused(&self, window: &Window) -> bool {
25010        self.focus_handle.is_focused(window)
25011    }
25012
25013    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25014        cx.emit(EditorEvent::Focused);
25015
25016        if let Some(descendant) = self
25017            .last_focused_descendant
25018            .take()
25019            .and_then(|descendant| descendant.upgrade())
25020        {
25021            window.focus(&descendant, cx);
25022        } else {
25023            if let Some(blame) = self.blame.as_ref() {
25024                blame.update(cx, GitBlame::focus)
25025            }
25026
25027            self.blink_manager.update(cx, BlinkManager::enable);
25028            self.show_cursor_names(window, cx);
25029            self.buffer.update(cx, |buffer, cx| {
25030                buffer.finalize_last_transaction(cx);
25031                if self.leader_id.is_none() {
25032                    buffer.set_active_selections(
25033                        &self.selections.disjoint_anchors_arc(),
25034                        self.selections.line_mode(),
25035                        self.cursor_shape,
25036                        cx,
25037                    );
25038                }
25039            });
25040
25041            if let Some(position_map) = self.last_position_map.clone()
25042                && !self.mouse_cursor_hidden
25043            {
25044                EditorElement::mouse_moved(
25045                    self,
25046                    &MouseMoveEvent {
25047                        position: window.mouse_position(),
25048                        pressed_button: None,
25049                        modifiers: window.modifiers(),
25050                    },
25051                    &position_map,
25052                    None,
25053                    window,
25054                    cx,
25055                );
25056            }
25057        }
25058    }
25059
25060    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25061        cx.emit(EditorEvent::FocusedIn)
25062    }
25063
25064    fn handle_focus_out(
25065        &mut self,
25066        event: FocusOutEvent,
25067        _window: &mut Window,
25068        cx: &mut Context<Self>,
25069    ) {
25070        if event.blurred != self.focus_handle {
25071            self.last_focused_descendant = Some(event.blurred);
25072        }
25073        self.selection_drag_state = SelectionDragState::None;
25074        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25075    }
25076
25077    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25078        self.blink_manager.update(cx, BlinkManager::disable);
25079        self.buffer
25080            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25081
25082        if let Some(blame) = self.blame.as_ref() {
25083            blame.update(cx, GitBlame::blur)
25084        }
25085        if !self.hover_state.focused(window, cx) {
25086            hide_hover(self, cx);
25087        }
25088        if !self
25089            .context_menu
25090            .borrow()
25091            .as_ref()
25092            .is_some_and(|context_menu| context_menu.focused(window, cx))
25093        {
25094            self.hide_context_menu(window, cx);
25095        }
25096        self.take_active_edit_prediction(cx);
25097        cx.emit(EditorEvent::Blurred);
25098        cx.notify();
25099    }
25100
25101    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25102        let mut pending: String = window
25103            .pending_input_keystrokes()
25104            .into_iter()
25105            .flatten()
25106            .filter_map(|keystroke| keystroke.key_char.clone())
25107            .collect();
25108
25109        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25110            pending = "".to_string();
25111        }
25112
25113        let existing_pending = self
25114            .text_highlights(HighlightKey::PendingInput, cx)
25115            .map(|(_, ranges)| ranges.to_vec());
25116        if existing_pending.is_none() && pending.is_empty() {
25117            return;
25118        }
25119        let transaction =
25120            self.transact(window, cx, |this, window, cx| {
25121                let selections = this
25122                    .selections
25123                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25124                let edits = selections
25125                    .iter()
25126                    .map(|selection| (selection.end..selection.end, pending.clone()));
25127                this.edit(edits, cx);
25128                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25129                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25130                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25131                    }));
25132                });
25133                if let Some(existing_ranges) = existing_pending {
25134                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25135                    this.edit(edits, cx);
25136                }
25137            });
25138
25139        let snapshot = self.snapshot(window, cx);
25140        let ranges = self
25141            .selections
25142            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25143            .into_iter()
25144            .map(|selection| {
25145                snapshot.buffer_snapshot().anchor_after(selection.end)
25146                    ..snapshot
25147                        .buffer_snapshot()
25148                        .anchor_before(selection.end + pending.len())
25149            })
25150            .collect();
25151
25152        if pending.is_empty() {
25153            self.clear_highlights(HighlightKey::PendingInput, cx);
25154        } else {
25155            self.highlight_text(
25156                HighlightKey::PendingInput,
25157                ranges,
25158                HighlightStyle {
25159                    underline: Some(UnderlineStyle {
25160                        thickness: px(1.),
25161                        color: None,
25162                        wavy: false,
25163                    }),
25164                    ..Default::default()
25165                },
25166                cx,
25167            );
25168        }
25169
25170        self.ime_transaction = self.ime_transaction.or(transaction);
25171        if let Some(transaction) = self.ime_transaction {
25172            self.buffer.update(cx, |buffer, cx| {
25173                buffer.group_until_transaction(transaction, cx);
25174            });
25175        }
25176
25177        if self
25178            .text_highlights(HighlightKey::PendingInput, cx)
25179            .is_none()
25180        {
25181            self.ime_transaction.take();
25182        }
25183    }
25184
25185    pub fn register_action_renderer(
25186        &mut self,
25187        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25188    ) -> Subscription {
25189        let id = self.next_editor_action_id.post_inc();
25190        self.editor_actions
25191            .borrow_mut()
25192            .insert(id, Box::new(listener));
25193
25194        let editor_actions = self.editor_actions.clone();
25195        Subscription::new(move || {
25196            editor_actions.borrow_mut().remove(&id);
25197        })
25198    }
25199
25200    pub fn register_action<A: Action>(
25201        &mut self,
25202        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25203    ) -> Subscription {
25204        let id = self.next_editor_action_id.post_inc();
25205        let listener = Arc::new(listener);
25206        self.editor_actions.borrow_mut().insert(
25207            id,
25208            Box::new(move |_, window, _| {
25209                let listener = listener.clone();
25210                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25211                    let action = action.downcast_ref().unwrap();
25212                    if phase == DispatchPhase::Bubble {
25213                        listener(action, window, cx)
25214                    }
25215                })
25216            }),
25217        );
25218
25219        let editor_actions = self.editor_actions.clone();
25220        Subscription::new(move || {
25221            editor_actions.borrow_mut().remove(&id);
25222        })
25223    }
25224
25225    pub fn file_header_size(&self) -> u32 {
25226        FILE_HEADER_HEIGHT
25227    }
25228
25229    pub fn restore(
25230        &mut self,
25231        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25232        window: &mut Window,
25233        cx: &mut Context<Self>,
25234    ) {
25235        self.buffer().update(cx, |multi_buffer, cx| {
25236            for (buffer_id, changes) in revert_changes {
25237                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25238                    buffer.update(cx, |buffer, cx| {
25239                        buffer.edit(
25240                            changes
25241                                .into_iter()
25242                                .map(|(range, text)| (range, text.to_string())),
25243                            None,
25244                            cx,
25245                        );
25246                    });
25247                }
25248            }
25249        });
25250        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25251            selections.refresh()
25252        });
25253    }
25254
25255    pub fn to_pixel_point(
25256        &mut self,
25257        source: Anchor,
25258        editor_snapshot: &EditorSnapshot,
25259        window: &mut Window,
25260        cx: &mut App,
25261    ) -> Option<gpui::Point<Pixels>> {
25262        let source_point = source.to_display_point(editor_snapshot);
25263        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25264    }
25265
25266    pub fn display_to_pixel_point(
25267        &mut self,
25268        source: DisplayPoint,
25269        editor_snapshot: &EditorSnapshot,
25270        window: &mut Window,
25271        cx: &mut App,
25272    ) -> Option<gpui::Point<Pixels>> {
25273        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25274        let text_layout_details = self.text_layout_details(window, cx);
25275        let scroll_top = text_layout_details
25276            .scroll_anchor
25277            .scroll_position(editor_snapshot)
25278            .y;
25279
25280        if source.row().as_f64() < scroll_top.floor() {
25281            return None;
25282        }
25283        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25284        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25285        Some(gpui::Point::new(source_x, source_y))
25286    }
25287
25288    pub fn has_visible_completions_menu(&self) -> bool {
25289        !self.edit_prediction_preview_is_active()
25290            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25291                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25292            })
25293    }
25294
25295    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25296        if self.mode.is_minimap() {
25297            return;
25298        }
25299        self.addons
25300            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25301    }
25302
25303    pub fn unregister_addon<T: Addon>(&mut self) {
25304        self.addons.remove(&std::any::TypeId::of::<T>());
25305    }
25306
25307    pub fn addon<T: Addon>(&self) -> Option<&T> {
25308        let type_id = std::any::TypeId::of::<T>();
25309        self.addons
25310            .get(&type_id)
25311            .and_then(|item| item.to_any().downcast_ref::<T>())
25312    }
25313
25314    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25315        let type_id = std::any::TypeId::of::<T>();
25316        self.addons
25317            .get_mut(&type_id)
25318            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25319    }
25320
25321    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25322        let text_layout_details = self.text_layout_details(window, cx);
25323        let style = &text_layout_details.editor_style;
25324        let font_id = window.text_system().resolve_font(&style.text.font());
25325        let font_size = style.text.font_size.to_pixels(window.rem_size());
25326        let line_height = style.text.line_height_in_pixels(window.rem_size());
25327        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25328        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25329
25330        CharacterDimensions {
25331            em_width,
25332            em_advance,
25333            line_height,
25334        }
25335    }
25336
25337    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25338        self.load_diff_task.clone()
25339    }
25340
25341    fn read_metadata_from_db(
25342        &mut self,
25343        item_id: u64,
25344        workspace_id: WorkspaceId,
25345        window: &mut Window,
25346        cx: &mut Context<Editor>,
25347    ) {
25348        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25349            && !self.mode.is_minimap()
25350            && WorkspaceSettings::get(None, cx).restore_on_startup
25351                != RestoreOnStartupBehavior::EmptyTab
25352        {
25353            let buffer_snapshot = OnceCell::new();
25354
25355            // Get file path for path-based fold lookup
25356            let file_path: Option<Arc<Path>> =
25357                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25358                    project::File::from_dyn(buffer.read(cx).file())
25359                        .map(|file| Arc::from(file.abs_path(cx)))
25360                });
25361
25362            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25363            let db = EditorDb::global(cx);
25364            let (folds, needs_migration) = if let Some(ref path) = file_path {
25365                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
25366                    && !folds.is_empty()
25367                {
25368                    (Some(folds), false)
25369                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
25370                    && !folds.is_empty()
25371                {
25372                    // Found old editor_folds data, will migrate to file_folds
25373                    (Some(folds), true)
25374                } else {
25375                    (None, false)
25376                }
25377            } else {
25378                // No file path, try editor_folds as fallback
25379                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
25380                (folds.filter(|f| !f.is_empty()), false)
25381            };
25382
25383            if let Some(folds) = folds {
25384                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25385                let snapshot_len = snapshot.len().0;
25386
25387                // Helper: search for fingerprint in buffer, return offset if found
25388                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25389                    // Ensure we start at a character boundary (defensive)
25390                    let search_start = snapshot
25391                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25392                        .0;
25393                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25394
25395                    let mut byte_offset = search_start;
25396                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25397                        if byte_offset > search_end {
25398                            break;
25399                        }
25400                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25401                            return Some(byte_offset);
25402                        }
25403                        byte_offset += ch.len_utf8();
25404                    }
25405                    None
25406                };
25407
25408                // Track search position to handle duplicate fingerprints correctly.
25409                // Folds are stored in document order, so we advance after each match.
25410                let mut search_start = 0usize;
25411
25412                // Collect db_folds for migration (only folds with valid fingerprints)
25413                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25414
25415                let valid_folds: Vec<_> = folds
25416                    .into_iter()
25417                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25418                        // Skip folds without fingerprints (old data before migration)
25419                        let sfp = start_fp?;
25420                        let efp = end_fp?;
25421                        let efp_len = efp.len();
25422
25423                        // Fast path: check if fingerprints match at stored offsets
25424                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25425                        let start_matches = stored_start < snapshot_len
25426                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25427                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25428                        let end_matches = efp_check_pos >= stored_start
25429                            && stored_end <= snapshot_len
25430                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25431
25432                        let (new_start, new_end) = if start_matches && end_matches {
25433                            // Offsets unchanged, use stored values
25434                            (stored_start, stored_end)
25435                        } else if sfp == efp {
25436                            // Short fold: identical fingerprints can only match once per search
25437                            // Use stored fold length to compute new_end
25438                            let new_start = find_fingerprint(&sfp, search_start)?;
25439                            let fold_len = stored_end - stored_start;
25440                            let new_end = new_start + fold_len;
25441                            (new_start, new_end)
25442                        } else {
25443                            // Slow path: search for fingerprints in buffer
25444                            let new_start = find_fingerprint(&sfp, search_start)?;
25445                            // Search for end_fp after start, then add efp_len to get actual fold end
25446                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25447                            let new_end = efp_pos + efp_len;
25448                            (new_start, new_end)
25449                        };
25450
25451                        // Advance search position for next fold
25452                        search_start = new_end;
25453
25454                        // Validate fold makes sense (end must be after start)
25455                        if new_end <= new_start {
25456                            return None;
25457                        }
25458
25459                        // Collect for migration if needed
25460                        if needs_migration {
25461                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25462                        }
25463
25464                        Some(
25465                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25466                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25467                        )
25468                    })
25469                    .collect();
25470
25471                if !valid_folds.is_empty() {
25472                    self.fold_ranges(valid_folds, false, window, cx);
25473
25474                    // Migrate from editor_folds to file_folds if we loaded from old table
25475                    if needs_migration {
25476                        if let Some(ref path) = file_path {
25477                            let path = path.clone();
25478                            let db = EditorDb::global(cx);
25479                            cx.spawn(async move |_, _| {
25480                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
25481                                    .await
25482                                    .log_err();
25483                            })
25484                            .detach();
25485                        }
25486                    }
25487                }
25488            }
25489
25490            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
25491                && !selections.is_empty()
25492            {
25493                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25494                // skip adding the initial selection to selection history
25495                self.selection_history.mode = SelectionHistoryMode::Skipping;
25496                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25497                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25498                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25499                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25500                    }));
25501                });
25502                self.selection_history.mode = SelectionHistoryMode::Normal;
25503            };
25504        }
25505
25506        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25507    }
25508
25509    /// Load folds from the file_folds database table by file path.
25510    /// Used when manually opening a file that was previously closed.
25511    fn load_folds_from_db(
25512        &mut self,
25513        workspace_id: WorkspaceId,
25514        file_path: PathBuf,
25515        window: &mut Window,
25516        cx: &mut Context<Editor>,
25517    ) {
25518        if self.mode.is_minimap()
25519            || WorkspaceSettings::get(None, cx).restore_on_startup
25520                == RestoreOnStartupBehavior::EmptyTab
25521        {
25522            return;
25523        }
25524
25525        let Some(folds) = EditorDb::global(cx)
25526            .get_file_folds(workspace_id, &file_path)
25527            .log_err()
25528        else {
25529            return;
25530        };
25531        if folds.is_empty() {
25532            return;
25533        }
25534
25535        let snapshot = self.buffer.read(cx).snapshot(cx);
25536        let snapshot_len = snapshot.len().0;
25537
25538        // Helper: search for fingerprint in buffer, return offset if found
25539        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25540            let search_start = snapshot
25541                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25542                .0;
25543            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25544
25545            let mut byte_offset = search_start;
25546            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25547                if byte_offset > search_end {
25548                    break;
25549                }
25550                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25551                    return Some(byte_offset);
25552                }
25553                byte_offset += ch.len_utf8();
25554            }
25555            None
25556        };
25557
25558        let mut search_start = 0usize;
25559
25560        let valid_folds: Vec<_> = folds
25561            .into_iter()
25562            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25563                let sfp = start_fp?;
25564                let efp = end_fp?;
25565                let efp_len = efp.len();
25566
25567                let start_matches = stored_start < snapshot_len
25568                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25569                let efp_check_pos = stored_end.saturating_sub(efp_len);
25570                let end_matches = efp_check_pos >= stored_start
25571                    && stored_end <= snapshot_len
25572                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25573
25574                let (new_start, new_end) = if start_matches && end_matches {
25575                    (stored_start, stored_end)
25576                } else if sfp == efp {
25577                    let new_start = find_fingerprint(&sfp, search_start)?;
25578                    let fold_len = stored_end - stored_start;
25579                    let new_end = new_start + fold_len;
25580                    (new_start, new_end)
25581                } else {
25582                    let new_start = find_fingerprint(&sfp, search_start)?;
25583                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25584                    let new_end = efp_pos + efp_len;
25585                    (new_start, new_end)
25586                };
25587
25588                search_start = new_end;
25589
25590                if new_end <= new_start {
25591                    return None;
25592                }
25593
25594                Some(
25595                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25596                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25597                )
25598            })
25599            .collect();
25600
25601        if !valid_folds.is_empty() {
25602            self.fold_ranges(valid_folds, false, window, cx);
25603        }
25604    }
25605
25606    fn lsp_data_enabled(&self) -> bool {
25607        self.enable_lsp_data && self.mode().is_full()
25608    }
25609
25610    fn update_lsp_data(
25611        &mut self,
25612        for_buffer: Option<BufferId>,
25613        window: &mut Window,
25614        cx: &mut Context<'_, Self>,
25615    ) {
25616        if !self.lsp_data_enabled() {
25617            return;
25618        }
25619
25620        if let Some(buffer_id) = for_buffer {
25621            self.pull_diagnostics(buffer_id, window, cx);
25622        }
25623        self.refresh_semantic_tokens(for_buffer, None, cx);
25624        self.refresh_document_colors(for_buffer, window, cx);
25625        self.refresh_folding_ranges(for_buffer, window, cx);
25626        self.refresh_document_symbols(for_buffer, cx);
25627    }
25628
25629    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25630        if !self.lsp_data_enabled() {
25631            return;
25632        }
25633        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25634            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25635        }
25636    }
25637
25638    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25639        if !self.lsp_data_enabled() {
25640            return;
25641        }
25642
25643        if !self.registered_buffers.contains_key(&buffer_id)
25644            && let Some(project) = self.project.as_ref()
25645        {
25646            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25647                project.update(cx, |project, cx| {
25648                    self.registered_buffers.insert(
25649                        buffer_id,
25650                        project.register_buffer_with_language_servers(&buffer, cx),
25651                    );
25652                });
25653            } else {
25654                self.registered_buffers.remove(&buffer_id);
25655            }
25656        }
25657    }
25658
25659    fn create_style(&self, cx: &App) -> EditorStyle {
25660        let settings = ThemeSettings::get_global(cx);
25661
25662        let mut text_style = match self.mode {
25663            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25664                color: cx.theme().colors().editor_foreground,
25665                font_family: settings.ui_font.family.clone(),
25666                font_features: settings.ui_font.features.clone(),
25667                font_fallbacks: settings.ui_font.fallbacks.clone(),
25668                font_size: rems(0.875).into(),
25669                font_weight: settings.ui_font.weight,
25670                line_height: relative(settings.buffer_line_height.value()),
25671                ..Default::default()
25672            },
25673            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25674                color: cx.theme().colors().editor_foreground,
25675                font_family: settings.buffer_font.family.clone(),
25676                font_features: settings.buffer_font.features.clone(),
25677                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25678                font_size: settings.buffer_font_size(cx).into(),
25679                font_weight: settings.buffer_font.weight,
25680                line_height: relative(settings.buffer_line_height.value()),
25681                ..Default::default()
25682            },
25683        };
25684        if let Some(text_style_refinement) = &self.text_style_refinement {
25685            text_style.refine(text_style_refinement)
25686        }
25687
25688        let background = match self.mode {
25689            EditorMode::SingleLine => cx.theme().system().transparent,
25690            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25691            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25692            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25693        };
25694
25695        EditorStyle {
25696            background,
25697            border: cx.theme().colors().border,
25698            local_player: cx.theme().players().local(),
25699            text: text_style,
25700            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25701            syntax: cx.theme().syntax().clone(),
25702            status: cx.theme().status().clone(),
25703            inlay_hints_style: make_inlay_hints_style(cx),
25704            edit_prediction_styles: make_suggestion_styles(cx),
25705            unnecessary_code_fade: settings.unnecessary_code_fade,
25706            show_underlines: self.diagnostics_enabled(),
25707        }
25708    }
25709
25710    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25711        let multibuffer = self.buffer().read(cx);
25712        let is_singleton = multibuffer.is_singleton();
25713        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25714        let buffer = multibuffer.buffer(*buffer_id)?;
25715
25716        let buffer = buffer.read(cx);
25717        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25718        let mut breadcrumbs = if is_singleton {
25719            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25720                buffer
25721                    .snapshot()
25722                    .resolve_file_path(
25723                        self.project
25724                            .as_ref()
25725                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25726                            .unwrap_or_default(),
25727                        cx,
25728                    )
25729                    .unwrap_or_else(|| {
25730                        if multibuffer.is_singleton() {
25731                            multibuffer.title(cx).to_string()
25732                        } else {
25733                            "untitled".to_string()
25734                        }
25735                    })
25736            });
25737            vec![HighlightedText {
25738                text: text.into(),
25739                highlights: vec![],
25740            }]
25741        } else {
25742            vec![]
25743        };
25744
25745        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25746            text: symbol.text.clone().into(),
25747            highlights: symbol.highlight_ranges.clone(),
25748        }));
25749        Some(breadcrumbs)
25750    }
25751
25752    fn disable_lsp_data(&mut self) {
25753        self.enable_lsp_data = false;
25754    }
25755
25756    fn disable_runnables(&mut self) {
25757        self.enable_runnables = false;
25758    }
25759
25760    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25761        self.register_visible_buffers(cx);
25762        self.colorize_brackets(false, cx);
25763        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25764        if !self.buffer().read(cx).is_singleton() {
25765            self.update_lsp_data(None, window, cx);
25766            self.refresh_runnables(None, window, cx);
25767        }
25768    }
25769}
25770
25771fn edit_for_markdown_paste<'a>(
25772    buffer: &MultiBufferSnapshot,
25773    range: Range<MultiBufferOffset>,
25774    to_insert: &'a str,
25775    url: Option<url::Url>,
25776) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25777    if url.is_none() {
25778        return (range, Cow::Borrowed(to_insert));
25779    };
25780
25781    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25782
25783    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25784        Cow::Borrowed(to_insert)
25785    } else {
25786        Cow::Owned(format!("[{old_text}]({to_insert})"))
25787    };
25788    (range, new_text)
25789}
25790
25791fn process_completion_for_edit(
25792    completion: &Completion,
25793    intent: CompletionIntent,
25794    buffer: &Entity<Buffer>,
25795    cursor_position: &text::Anchor,
25796    cx: &mut Context<Editor>,
25797) -> CompletionEdit {
25798    let buffer = buffer.read(cx);
25799    let buffer_snapshot = buffer.snapshot();
25800    let (snippet, new_text) = if completion.is_snippet() {
25801        let mut snippet_source = completion.new_text.clone();
25802        // Workaround for typescript language server issues so that methods don't expand within
25803        // strings and functions with type expressions. The previous point is used because the query
25804        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25805        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25806        let previous_point = if previous_point.column > 0 {
25807            cursor_position.to_previous_offset(&buffer_snapshot)
25808        } else {
25809            cursor_position.to_offset(&buffer_snapshot)
25810        };
25811        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25812            && scope.prefers_label_for_snippet_in_completion()
25813            && let Some(label) = completion.label()
25814            && matches!(
25815                completion.kind(),
25816                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25817            )
25818        {
25819            snippet_source = label;
25820        }
25821        match Snippet::parse(&snippet_source).log_err() {
25822            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25823            None => (None, completion.new_text.clone()),
25824        }
25825    } else {
25826        (None, completion.new_text.clone())
25827    };
25828
25829    let mut range_to_replace = {
25830        let replace_range = &completion.replace_range;
25831        if let CompletionSource::Lsp {
25832            insert_range: Some(insert_range),
25833            ..
25834        } = &completion.source
25835        {
25836            debug_assert_eq!(
25837                insert_range.start, replace_range.start,
25838                "insert_range and replace_range should start at the same position"
25839            );
25840            debug_assert!(
25841                insert_range
25842                    .start
25843                    .cmp(cursor_position, &buffer_snapshot)
25844                    .is_le(),
25845                "insert_range should start before or at cursor position"
25846            );
25847            debug_assert!(
25848                replace_range
25849                    .start
25850                    .cmp(cursor_position, &buffer_snapshot)
25851                    .is_le(),
25852                "replace_range should start before or at cursor position"
25853            );
25854
25855            let should_replace = match intent {
25856                CompletionIntent::CompleteWithInsert => false,
25857                CompletionIntent::CompleteWithReplace => true,
25858                CompletionIntent::Complete | CompletionIntent::Compose => {
25859                    let insert_mode =
25860                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25861                            .completions
25862                            .lsp_insert_mode;
25863                    match insert_mode {
25864                        LspInsertMode::Insert => false,
25865                        LspInsertMode::Replace => true,
25866                        LspInsertMode::ReplaceSubsequence => {
25867                            let mut text_to_replace = buffer.chars_for_range(
25868                                buffer.anchor_before(replace_range.start)
25869                                    ..buffer.anchor_after(replace_range.end),
25870                            );
25871                            let mut current_needle = text_to_replace.next();
25872                            for haystack_ch in completion.label.text.chars() {
25873                                if let Some(needle_ch) = current_needle
25874                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25875                                {
25876                                    current_needle = text_to_replace.next();
25877                                }
25878                            }
25879                            current_needle.is_none()
25880                        }
25881                        LspInsertMode::ReplaceSuffix => {
25882                            if replace_range
25883                                .end
25884                                .cmp(cursor_position, &buffer_snapshot)
25885                                .is_gt()
25886                            {
25887                                let range_after_cursor = *cursor_position..replace_range.end;
25888                                let text_after_cursor = buffer
25889                                    .text_for_range(
25890                                        buffer.anchor_before(range_after_cursor.start)
25891                                            ..buffer.anchor_after(range_after_cursor.end),
25892                                    )
25893                                    .collect::<String>()
25894                                    .to_ascii_lowercase();
25895                                completion
25896                                    .label
25897                                    .text
25898                                    .to_ascii_lowercase()
25899                                    .ends_with(&text_after_cursor)
25900                            } else {
25901                                true
25902                            }
25903                        }
25904                    }
25905                }
25906            };
25907
25908            if should_replace {
25909                replace_range.clone()
25910            } else {
25911                insert_range.clone()
25912            }
25913        } else {
25914            replace_range.clone()
25915        }
25916    };
25917
25918    if range_to_replace
25919        .end
25920        .cmp(cursor_position, &buffer_snapshot)
25921        .is_lt()
25922    {
25923        range_to_replace.end = *cursor_position;
25924    }
25925
25926    let replace_range = range_to_replace.to_offset(buffer);
25927    CompletionEdit {
25928        new_text,
25929        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25930        snippet,
25931    }
25932}
25933
25934struct CompletionEdit {
25935    new_text: String,
25936    replace_range: Range<BufferOffset>,
25937    snippet: Option<Snippet>,
25938}
25939
25940fn comment_delimiter_for_newline(
25941    start_point: &Point,
25942    buffer: &MultiBufferSnapshot,
25943    language: &LanguageScope,
25944) -> Option<Arc<str>> {
25945    let delimiters = language.line_comment_prefixes();
25946    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25947    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25948
25949    let num_of_whitespaces = snapshot
25950        .chars_for_range(range.clone())
25951        .take_while(|c| c.is_whitespace())
25952        .count();
25953    let comment_candidate = snapshot
25954        .chars_for_range(range.clone())
25955        .skip(num_of_whitespaces)
25956        .take(max_len_of_delimiter + 2)
25957        .collect::<String>();
25958    let (delimiter, trimmed_len, is_repl) = delimiters
25959        .iter()
25960        .filter_map(|delimiter| {
25961            let prefix = delimiter.trim_end();
25962            if comment_candidate.starts_with(prefix) {
25963                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25964                {
25965                    stripped_comment.starts_with(" %%")
25966                } else {
25967                    false
25968                };
25969                Some((delimiter, prefix.len(), is_repl))
25970            } else {
25971                None
25972            }
25973        })
25974        .max_by_key(|(_, len, _)| *len)?;
25975
25976    if let Some(BlockCommentConfig {
25977        start: block_start, ..
25978    }) = language.block_comment()
25979    {
25980        let block_start_trimmed = block_start.trim_end();
25981        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25982            let line_content = snapshot
25983                .chars_for_range(range.clone())
25984                .skip(num_of_whitespaces)
25985                .take(block_start_trimmed.len())
25986                .collect::<String>();
25987
25988            if line_content.starts_with(block_start_trimmed) {
25989                return None;
25990            }
25991        }
25992    }
25993
25994    let cursor_is_placed_after_comment_marker =
25995        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25996    if cursor_is_placed_after_comment_marker {
25997        if !is_repl {
25998            return Some(delimiter.clone());
25999        }
26000
26001        let line_content_after_cursor: String = snapshot
26002            .chars_for_range(range)
26003            .skip(start_point.column as usize)
26004            .collect();
26005
26006        if line_content_after_cursor.trim().is_empty() {
26007            return None;
26008        } else {
26009            return Some(delimiter.clone());
26010        }
26011    } else {
26012        None
26013    }
26014}
26015
26016fn documentation_delimiter_for_newline(
26017    start_point: &Point,
26018    buffer: &MultiBufferSnapshot,
26019    language: &LanguageScope,
26020    newline_config: &mut NewlineConfig,
26021) -> Option<Arc<str>> {
26022    let BlockCommentConfig {
26023        start: start_tag,
26024        end: end_tag,
26025        prefix: delimiter,
26026        tab_size: len,
26027    } = language.documentation_comment()?;
26028    let is_within_block_comment = buffer
26029        .language_scope_at(*start_point)
26030        .is_some_and(|scope| scope.override_name() == Some("comment"));
26031    if !is_within_block_comment {
26032        return None;
26033    }
26034
26035    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26036
26037    let num_of_whitespaces = snapshot
26038        .chars_for_range(range.clone())
26039        .take_while(|c| c.is_whitespace())
26040        .count();
26041
26042    // 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.
26043    let column = start_point.column;
26044    let cursor_is_after_start_tag = {
26045        let start_tag_len = start_tag.len();
26046        let start_tag_line = snapshot
26047            .chars_for_range(range.clone())
26048            .skip(num_of_whitespaces)
26049            .take(start_tag_len)
26050            .collect::<String>();
26051        if start_tag_line.starts_with(start_tag.as_ref()) {
26052            num_of_whitespaces + start_tag_len <= column as usize
26053        } else {
26054            false
26055        }
26056    };
26057
26058    let cursor_is_after_delimiter = {
26059        let delimiter_trim = delimiter.trim_end();
26060        let delimiter_line = snapshot
26061            .chars_for_range(range.clone())
26062            .skip(num_of_whitespaces)
26063            .take(delimiter_trim.len())
26064            .collect::<String>();
26065        if delimiter_line.starts_with(delimiter_trim) {
26066            num_of_whitespaces + delimiter_trim.len() <= column as usize
26067        } else {
26068            false
26069        }
26070    };
26071
26072    let mut needs_extra_line = false;
26073    let mut extra_line_additional_indent = IndentSize::spaces(0);
26074
26075    let cursor_is_before_end_tag_if_exists = {
26076        let mut char_position = 0u32;
26077        let mut end_tag_offset = None;
26078
26079        'outer: for chunk in snapshot.text_for_range(range) {
26080            if let Some(byte_pos) = chunk.find(&**end_tag) {
26081                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26082                end_tag_offset = Some(char_position + chars_before_match);
26083                break 'outer;
26084            }
26085            char_position += chunk.chars().count() as u32;
26086        }
26087
26088        if let Some(end_tag_offset) = end_tag_offset {
26089            let cursor_is_before_end_tag = column <= end_tag_offset;
26090            if cursor_is_after_start_tag {
26091                if cursor_is_before_end_tag {
26092                    needs_extra_line = true;
26093                }
26094                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26095                if cursor_is_at_start_of_end_tag {
26096                    extra_line_additional_indent.len = *len;
26097                }
26098            }
26099            cursor_is_before_end_tag
26100        } else {
26101            true
26102        }
26103    };
26104
26105    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26106        && cursor_is_before_end_tag_if_exists
26107    {
26108        let additional_indent = if cursor_is_after_start_tag {
26109            IndentSize::spaces(*len)
26110        } else {
26111            IndentSize::spaces(0)
26112        };
26113
26114        *newline_config = NewlineConfig::Newline {
26115            additional_indent,
26116            extra_line_additional_indent: if needs_extra_line {
26117                Some(extra_line_additional_indent)
26118            } else {
26119                None
26120            },
26121            prevent_auto_indent: true,
26122        };
26123        Some(delimiter.clone())
26124    } else {
26125        None
26126    }
26127}
26128
26129const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26130
26131fn list_delimiter_for_newline(
26132    start_point: &Point,
26133    buffer: &MultiBufferSnapshot,
26134    language: &LanguageScope,
26135    newline_config: &mut NewlineConfig,
26136) -> Option<Arc<str>> {
26137    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26138
26139    let num_of_whitespaces = snapshot
26140        .chars_for_range(range.clone())
26141        .take_while(|c| c.is_whitespace())
26142        .count();
26143
26144    let task_list_entries: Vec<_> = language
26145        .task_list()
26146        .into_iter()
26147        .flat_map(|config| {
26148            config
26149                .prefixes
26150                .iter()
26151                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26152        })
26153        .collect();
26154    let unordered_list_entries: Vec<_> = language
26155        .unordered_list()
26156        .iter()
26157        .map(|marker| (marker.as_ref(), marker.as_ref()))
26158        .collect();
26159
26160    let all_entries: Vec<_> = task_list_entries
26161        .into_iter()
26162        .chain(unordered_list_entries)
26163        .collect();
26164
26165    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26166        let candidate: String = snapshot
26167            .chars_for_range(range.clone())
26168            .skip(num_of_whitespaces)
26169            .take(max_prefix_len)
26170            .collect();
26171
26172        if let Some((prefix, continuation)) = all_entries
26173            .iter()
26174            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26175            .max_by_key(|(prefix, _)| prefix.len())
26176        {
26177            let end_of_prefix = num_of_whitespaces + prefix.len();
26178            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26179            let has_content_after_marker = snapshot
26180                .chars_for_range(range)
26181                .skip(end_of_prefix)
26182                .any(|c| !c.is_whitespace());
26183
26184            if has_content_after_marker && cursor_is_after_prefix {
26185                return Some((*continuation).into());
26186            }
26187
26188            if start_point.column as usize == end_of_prefix {
26189                if num_of_whitespaces == 0 {
26190                    *newline_config = NewlineConfig::ClearCurrentLine;
26191                } else {
26192                    *newline_config = NewlineConfig::UnindentCurrentLine {
26193                        continuation: (*continuation).into(),
26194                    };
26195                }
26196            }
26197
26198            return None;
26199        }
26200    }
26201
26202    let candidate: String = snapshot
26203        .chars_for_range(range.clone())
26204        .skip(num_of_whitespaces)
26205        .take(ORDERED_LIST_MAX_MARKER_LEN)
26206        .collect();
26207
26208    for ordered_config in language.ordered_list() {
26209        let regex = match Regex::new(&ordered_config.pattern) {
26210            Ok(r) => r,
26211            Err(_) => continue,
26212        };
26213
26214        if let Some(captures) = regex.captures(&candidate) {
26215            let full_match = captures.get(0)?;
26216            let marker_len = full_match.len();
26217            let end_of_prefix = num_of_whitespaces + marker_len;
26218            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26219
26220            let has_content_after_marker = snapshot
26221                .chars_for_range(range)
26222                .skip(end_of_prefix)
26223                .any(|c| !c.is_whitespace());
26224
26225            if has_content_after_marker && cursor_is_after_prefix {
26226                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26227                let continuation = ordered_config
26228                    .format
26229                    .replace("{1}", &(number + 1).to_string());
26230                return Some(continuation.into());
26231            }
26232
26233            if start_point.column as usize == end_of_prefix {
26234                let continuation = ordered_config.format.replace("{1}", "1");
26235                if num_of_whitespaces == 0 {
26236                    *newline_config = NewlineConfig::ClearCurrentLine;
26237                } else {
26238                    *newline_config = NewlineConfig::UnindentCurrentLine {
26239                        continuation: continuation.into(),
26240                    };
26241                }
26242            }
26243
26244            return None;
26245        }
26246    }
26247
26248    None
26249}
26250
26251fn is_list_prefix_row(
26252    row: MultiBufferRow,
26253    buffer: &MultiBufferSnapshot,
26254    language: &LanguageScope,
26255) -> bool {
26256    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26257        return false;
26258    };
26259
26260    let num_of_whitespaces = snapshot
26261        .chars_for_range(range.clone())
26262        .take_while(|c| c.is_whitespace())
26263        .count();
26264
26265    let task_list_prefixes: Vec<_> = language
26266        .task_list()
26267        .into_iter()
26268        .flat_map(|config| {
26269            config
26270                .prefixes
26271                .iter()
26272                .map(|p| p.as_ref())
26273                .collect::<Vec<_>>()
26274        })
26275        .collect();
26276    let unordered_list_markers: Vec<_> = language
26277        .unordered_list()
26278        .iter()
26279        .map(|marker| marker.as_ref())
26280        .collect();
26281    let all_prefixes: Vec<_> = task_list_prefixes
26282        .into_iter()
26283        .chain(unordered_list_markers)
26284        .collect();
26285    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26286        let candidate: String = snapshot
26287            .chars_for_range(range.clone())
26288            .skip(num_of_whitespaces)
26289            .take(max_prefix_len)
26290            .collect();
26291        if all_prefixes
26292            .iter()
26293            .any(|prefix| candidate.starts_with(*prefix))
26294        {
26295            return true;
26296        }
26297    }
26298
26299    let ordered_list_candidate: String = snapshot
26300        .chars_for_range(range)
26301        .skip(num_of_whitespaces)
26302        .take(ORDERED_LIST_MAX_MARKER_LEN)
26303        .collect();
26304    for ordered_config in language.ordered_list() {
26305        let regex = match Regex::new(&ordered_config.pattern) {
26306            Ok(r) => r,
26307            Err(_) => continue,
26308        };
26309        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26310            return captures.get(0).is_some();
26311        }
26312    }
26313
26314    false
26315}
26316
26317#[derive(Debug)]
26318enum NewlineConfig {
26319    /// Insert newline with optional additional indent and optional extra blank line
26320    Newline {
26321        additional_indent: IndentSize,
26322        extra_line_additional_indent: Option<IndentSize>,
26323        prevent_auto_indent: bool,
26324    },
26325    /// Clear the current line
26326    ClearCurrentLine,
26327    /// Unindent the current line and add continuation
26328    UnindentCurrentLine { continuation: Arc<str> },
26329}
26330
26331impl NewlineConfig {
26332    fn has_extra_line(&self) -> bool {
26333        matches!(
26334            self,
26335            Self::Newline {
26336                extra_line_additional_indent: Some(_),
26337                ..
26338            }
26339        )
26340    }
26341
26342    fn insert_extra_newline_brackets(
26343        buffer: &MultiBufferSnapshot,
26344        range: Range<MultiBufferOffset>,
26345        language: &language::LanguageScope,
26346    ) -> bool {
26347        let leading_whitespace_len = buffer
26348            .reversed_chars_at(range.start)
26349            .take_while(|c| c.is_whitespace() && *c != '\n')
26350            .map(|c| c.len_utf8())
26351            .sum::<usize>();
26352        let trailing_whitespace_len = buffer
26353            .chars_at(range.end)
26354            .take_while(|c| c.is_whitespace() && *c != '\n')
26355            .map(|c| c.len_utf8())
26356            .sum::<usize>();
26357        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26358
26359        language.brackets().any(|(pair, enabled)| {
26360            let pair_start = pair.start.trim_end();
26361            let pair_end = pair.end.trim_start();
26362
26363            enabled
26364                && pair.newline
26365                && buffer.contains_str_at(range.end, pair_end)
26366                && buffer.contains_str_at(
26367                    range.start.saturating_sub_usize(pair_start.len()),
26368                    pair_start,
26369                )
26370        })
26371    }
26372
26373    fn insert_extra_newline_tree_sitter(
26374        buffer: &MultiBufferSnapshot,
26375        range: Range<MultiBufferOffset>,
26376    ) -> bool {
26377        let (buffer, range) = match buffer
26378            .range_to_buffer_ranges(range.start..=range.end)
26379            .as_slice()
26380        {
26381            [(buffer, range, _)] => (*buffer, range.clone()),
26382            _ => return false,
26383        };
26384        let pair = {
26385            let mut result: Option<BracketMatch<usize>> = None;
26386
26387            for pair in buffer
26388                .all_bracket_ranges(range.start.0..range.end.0)
26389                .filter(move |pair| {
26390                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26391                })
26392            {
26393                let len = pair.close_range.end - pair.open_range.start;
26394
26395                if let Some(existing) = &result {
26396                    let existing_len = existing.close_range.end - existing.open_range.start;
26397                    if len > existing_len {
26398                        continue;
26399                    }
26400                }
26401
26402                result = Some(pair);
26403            }
26404
26405            result
26406        };
26407        let Some(pair) = pair else {
26408            return false;
26409        };
26410        pair.newline_only
26411            && buffer
26412                .chars_for_range(pair.open_range.end..range.start.0)
26413                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26414                .all(|c| c.is_whitespace() && c != '\n')
26415    }
26416}
26417
26418fn update_uncommitted_diff_for_buffer(
26419    editor: Entity<Editor>,
26420    project: &Entity<Project>,
26421    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26422    buffer: Entity<MultiBuffer>,
26423    cx: &mut App,
26424) -> Task<()> {
26425    let mut tasks = Vec::new();
26426    project.update(cx, |project, cx| {
26427        for buffer in buffers {
26428            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26429                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26430            }
26431        }
26432    });
26433    cx.spawn(async move |cx| {
26434        let diffs = future::join_all(tasks).await;
26435        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26436            return;
26437        }
26438
26439        buffer.update(cx, |buffer, cx| {
26440            for diff in diffs.into_iter().flatten() {
26441                buffer.add_diff(diff, cx);
26442            }
26443        });
26444    })
26445}
26446
26447fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26448    let tab_size = tab_size.get() as usize;
26449    let mut width = offset;
26450
26451    for ch in text.chars() {
26452        width += if ch == '\t' {
26453            tab_size - (width % tab_size)
26454        } else {
26455            1
26456        };
26457    }
26458
26459    width - offset
26460}
26461
26462#[cfg(test)]
26463mod tests {
26464    use super::*;
26465
26466    #[test]
26467    fn test_string_size_with_expanded_tabs() {
26468        let nz = |val| NonZeroU32::new(val).unwrap();
26469        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26470        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26471        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26472        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26473        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26474        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26475        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26476        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26477    }
26478}
26479
26480/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26481struct WordBreakingTokenizer<'a> {
26482    input: &'a str,
26483}
26484
26485impl<'a> WordBreakingTokenizer<'a> {
26486    fn new(input: &'a str) -> Self {
26487        Self { input }
26488    }
26489}
26490
26491fn is_char_ideographic(ch: char) -> bool {
26492    use unicode_script::Script::*;
26493    use unicode_script::UnicodeScript;
26494    matches!(ch.script(), Han | Tangut | Yi)
26495}
26496
26497fn is_grapheme_ideographic(text: &str) -> bool {
26498    text.chars().any(is_char_ideographic)
26499}
26500
26501fn is_grapheme_whitespace(text: &str) -> bool {
26502    text.chars().any(|x| x.is_whitespace())
26503}
26504
26505fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26506    text.chars()
26507        .next()
26508        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26509}
26510
26511#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26512enum WordBreakToken<'a> {
26513    Word { token: &'a str, grapheme_len: usize },
26514    InlineWhitespace { token: &'a str, grapheme_len: usize },
26515    Newline,
26516}
26517
26518impl<'a> Iterator for WordBreakingTokenizer<'a> {
26519    /// Yields a span, the count of graphemes in the token, and whether it was
26520    /// whitespace. Note that it also breaks at word boundaries.
26521    type Item = WordBreakToken<'a>;
26522
26523    fn next(&mut self) -> Option<Self::Item> {
26524        use unicode_segmentation::UnicodeSegmentation;
26525        if self.input.is_empty() {
26526            return None;
26527        }
26528
26529        let mut iter = self.input.graphemes(true).peekable();
26530        let mut offset = 0;
26531        let mut grapheme_len = 0;
26532        if let Some(first_grapheme) = iter.next() {
26533            let is_newline = first_grapheme == "\n";
26534            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26535            offset += first_grapheme.len();
26536            grapheme_len += 1;
26537            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26538                if let Some(grapheme) = iter.peek().copied()
26539                    && should_stay_with_preceding_ideograph(grapheme)
26540                {
26541                    offset += grapheme.len();
26542                    grapheme_len += 1;
26543                }
26544            } else {
26545                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26546                let mut next_word_bound = words.peek().copied();
26547                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26548                    next_word_bound = words.next();
26549                }
26550                while let Some(grapheme) = iter.peek().copied() {
26551                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26552                        break;
26553                    };
26554                    if is_grapheme_whitespace(grapheme) != is_whitespace
26555                        || (grapheme == "\n") != is_newline
26556                    {
26557                        break;
26558                    };
26559                    offset += grapheme.len();
26560                    grapheme_len += 1;
26561                    iter.next();
26562                }
26563            }
26564            let token = &self.input[..offset];
26565            self.input = &self.input[offset..];
26566            if token == "\n" {
26567                Some(WordBreakToken::Newline)
26568            } else if is_whitespace {
26569                Some(WordBreakToken::InlineWhitespace {
26570                    token,
26571                    grapheme_len,
26572                })
26573            } else {
26574                Some(WordBreakToken::Word {
26575                    token,
26576                    grapheme_len,
26577                })
26578            }
26579        } else {
26580            None
26581        }
26582    }
26583}
26584
26585#[test]
26586fn test_word_breaking_tokenizer() {
26587    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26588        ("", &[]),
26589        ("  ", &[whitespace("  ", 2)]),
26590        ("Ʒ", &[word("Ʒ", 1)]),
26591        ("Ǽ", &[word("Ǽ", 1)]),
26592        ("", &[word("", 1)]),
26593        ("⋑⋑", &[word("⋑⋑", 2)]),
26594        (
26595            "原理,进而",
26596            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26597        ),
26598        (
26599            "hello world",
26600            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26601        ),
26602        (
26603            "hello, world",
26604            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26605        ),
26606        (
26607            "  hello world",
26608            &[
26609                whitespace("  ", 2),
26610                word("hello", 5),
26611                whitespace(" ", 1),
26612                word("world", 5),
26613            ],
26614        ),
26615        (
26616            "这是什么 \n 钢笔",
26617            &[
26618                word("", 1),
26619                word("", 1),
26620                word("", 1),
26621                word("", 1),
26622                whitespace(" ", 1),
26623                newline(),
26624                whitespace(" ", 1),
26625                word("", 1),
26626                word("", 1),
26627            ],
26628        ),
26629        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26630    ];
26631
26632    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26633        WordBreakToken::Word {
26634            token,
26635            grapheme_len,
26636        }
26637    }
26638
26639    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26640        WordBreakToken::InlineWhitespace {
26641            token,
26642            grapheme_len,
26643        }
26644    }
26645
26646    fn newline() -> WordBreakToken<'static> {
26647        WordBreakToken::Newline
26648    }
26649
26650    for (input, result) in tests {
26651        assert_eq!(
26652            WordBreakingTokenizer::new(input)
26653                .collect::<Vec<_>>()
26654                .as_slice(),
26655            *result,
26656        );
26657    }
26658}
26659
26660fn wrap_with_prefix(
26661    first_line_prefix: String,
26662    subsequent_lines_prefix: String,
26663    unwrapped_text: String,
26664    wrap_column: usize,
26665    tab_size: NonZeroU32,
26666    preserve_existing_whitespace: bool,
26667) -> String {
26668    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26669    let subsequent_lines_prefix_len =
26670        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26671    let mut wrapped_text = String::new();
26672    let mut current_line = first_line_prefix;
26673    let mut is_first_line = true;
26674
26675    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26676    let mut current_line_len = first_line_prefix_len;
26677    let mut in_whitespace = false;
26678    for token in tokenizer {
26679        let have_preceding_whitespace = in_whitespace;
26680        match token {
26681            WordBreakToken::Word {
26682                token,
26683                grapheme_len,
26684            } => {
26685                in_whitespace = false;
26686                let current_prefix_len = if is_first_line {
26687                    first_line_prefix_len
26688                } else {
26689                    subsequent_lines_prefix_len
26690                };
26691                if current_line_len + grapheme_len > wrap_column
26692                    && current_line_len != current_prefix_len
26693                {
26694                    wrapped_text.push_str(current_line.trim_end());
26695                    wrapped_text.push('\n');
26696                    is_first_line = false;
26697                    current_line = subsequent_lines_prefix.clone();
26698                    current_line_len = subsequent_lines_prefix_len;
26699                }
26700                current_line.push_str(token);
26701                current_line_len += grapheme_len;
26702            }
26703            WordBreakToken::InlineWhitespace {
26704                mut token,
26705                mut grapheme_len,
26706            } => {
26707                in_whitespace = true;
26708                if have_preceding_whitespace && !preserve_existing_whitespace {
26709                    continue;
26710                }
26711                if !preserve_existing_whitespace {
26712                    // Keep a single whitespace grapheme as-is
26713                    if let Some(first) =
26714                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26715                    {
26716                        token = first;
26717                    } else {
26718                        token = " ";
26719                    }
26720                    grapheme_len = 1;
26721                }
26722                let current_prefix_len = if is_first_line {
26723                    first_line_prefix_len
26724                } else {
26725                    subsequent_lines_prefix_len
26726                };
26727                if current_line_len + grapheme_len > wrap_column {
26728                    wrapped_text.push_str(current_line.trim_end());
26729                    wrapped_text.push('\n');
26730                    is_first_line = false;
26731                    current_line = subsequent_lines_prefix.clone();
26732                    current_line_len = subsequent_lines_prefix_len;
26733                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26734                    current_line.push_str(token);
26735                    current_line_len += grapheme_len;
26736                }
26737            }
26738            WordBreakToken::Newline => {
26739                in_whitespace = true;
26740                let current_prefix_len = if is_first_line {
26741                    first_line_prefix_len
26742                } else {
26743                    subsequent_lines_prefix_len
26744                };
26745                if preserve_existing_whitespace {
26746                    wrapped_text.push_str(current_line.trim_end());
26747                    wrapped_text.push('\n');
26748                    is_first_line = false;
26749                    current_line = subsequent_lines_prefix.clone();
26750                    current_line_len = subsequent_lines_prefix_len;
26751                } else if have_preceding_whitespace {
26752                    continue;
26753                } else if current_line_len + 1 > wrap_column
26754                    && current_line_len != current_prefix_len
26755                {
26756                    wrapped_text.push_str(current_line.trim_end());
26757                    wrapped_text.push('\n');
26758                    is_first_line = false;
26759                    current_line = subsequent_lines_prefix.clone();
26760                    current_line_len = subsequent_lines_prefix_len;
26761                } else if current_line_len != current_prefix_len {
26762                    current_line.push(' ');
26763                    current_line_len += 1;
26764                }
26765            }
26766        }
26767    }
26768
26769    if !current_line.is_empty() {
26770        wrapped_text.push_str(&current_line);
26771    }
26772    wrapped_text
26773}
26774
26775#[test]
26776fn test_wrap_with_prefix() {
26777    assert_eq!(
26778        wrap_with_prefix(
26779            "# ".to_string(),
26780            "# ".to_string(),
26781            "abcdefg".to_string(),
26782            4,
26783            NonZeroU32::new(4).unwrap(),
26784            false,
26785        ),
26786        "# abcdefg"
26787    );
26788    assert_eq!(
26789        wrap_with_prefix(
26790            "".to_string(),
26791            "".to_string(),
26792            "\thello world".to_string(),
26793            8,
26794            NonZeroU32::new(4).unwrap(),
26795            false,
26796        ),
26797        "hello\nworld"
26798    );
26799    assert_eq!(
26800        wrap_with_prefix(
26801            "// ".to_string(),
26802            "// ".to_string(),
26803            "xx \nyy zz aa bb cc".to_string(),
26804            12,
26805            NonZeroU32::new(4).unwrap(),
26806            false,
26807        ),
26808        "// xx yy zz\n// aa bb cc"
26809    );
26810    assert_eq!(
26811        wrap_with_prefix(
26812            String::new(),
26813            String::new(),
26814            "这是什么 \n 钢笔".to_string(),
26815            3,
26816            NonZeroU32::new(4).unwrap(),
26817            false,
26818        ),
26819        "这是什\n么 钢\n"
26820    );
26821    assert_eq!(
26822        wrap_with_prefix(
26823            String::new(),
26824            String::new(),
26825            format!("foo{}bar", '\u{2009}'), // thin space
26826            80,
26827            NonZeroU32::new(4).unwrap(),
26828            false,
26829        ),
26830        format!("foo{}bar", '\u{2009}')
26831    );
26832}
26833
26834pub trait CollaborationHub {
26835    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26836    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26837    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26838}
26839
26840impl CollaborationHub for Entity<Project> {
26841    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26842        self.read(cx).collaborators()
26843    }
26844
26845    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26846        self.read(cx).user_store().read(cx).participant_indices()
26847    }
26848
26849    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26850        let this = self.read(cx);
26851        let user_ids = this.collaborators().values().map(|c| c.user_id);
26852        this.user_store().read(cx).participant_names(user_ids, cx)
26853    }
26854}
26855
26856pub trait SemanticsProvider {
26857    fn hover(
26858        &self,
26859        buffer: &Entity<Buffer>,
26860        position: text::Anchor,
26861        cx: &mut App,
26862    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26863
26864    fn inline_values(
26865        &self,
26866        buffer_handle: Entity<Buffer>,
26867        range: Range<text::Anchor>,
26868        cx: &mut App,
26869    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26870
26871    fn applicable_inlay_chunks(
26872        &self,
26873        buffer: &Entity<Buffer>,
26874        ranges: &[Range<text::Anchor>],
26875        cx: &mut App,
26876    ) -> Vec<Range<BufferRow>>;
26877
26878    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26879
26880    fn inlay_hints(
26881        &self,
26882        invalidate: InvalidationStrategy,
26883        buffer: Entity<Buffer>,
26884        ranges: Vec<Range<text::Anchor>>,
26885        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26886        cx: &mut App,
26887    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26888
26889    fn semantic_tokens(
26890        &self,
26891        buffer: Entity<Buffer>,
26892        refresh: Option<RefreshForServer>,
26893        cx: &mut App,
26894    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
26895
26896    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26897
26898    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26899
26900    fn document_highlights(
26901        &self,
26902        buffer: &Entity<Buffer>,
26903        position: text::Anchor,
26904        cx: &mut App,
26905    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26906
26907    fn definitions(
26908        &self,
26909        buffer: &Entity<Buffer>,
26910        position: text::Anchor,
26911        kind: GotoDefinitionKind,
26912        cx: &mut App,
26913    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26914
26915    fn range_for_rename(
26916        &self,
26917        buffer: &Entity<Buffer>,
26918        position: text::Anchor,
26919        cx: &mut App,
26920    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26921
26922    fn perform_rename(
26923        &self,
26924        buffer: &Entity<Buffer>,
26925        position: text::Anchor,
26926        new_name: String,
26927        cx: &mut App,
26928    ) -> Option<Task<Result<ProjectTransaction>>>;
26929}
26930
26931pub trait CompletionProvider {
26932    fn completions(
26933        &self,
26934        excerpt_id: ExcerptId,
26935        buffer: &Entity<Buffer>,
26936        buffer_position: text::Anchor,
26937        trigger: CompletionContext,
26938        window: &mut Window,
26939        cx: &mut Context<Editor>,
26940    ) -> Task<Result<Vec<CompletionResponse>>>;
26941
26942    fn resolve_completions(
26943        &self,
26944        _buffer: Entity<Buffer>,
26945        _completion_indices: Vec<usize>,
26946        _completions: Rc<RefCell<Box<[Completion]>>>,
26947        _cx: &mut Context<Editor>,
26948    ) -> Task<Result<bool>> {
26949        Task::ready(Ok(false))
26950    }
26951
26952    fn apply_additional_edits_for_completion(
26953        &self,
26954        _buffer: Entity<Buffer>,
26955        _completions: Rc<RefCell<Box<[Completion]>>>,
26956        _completion_index: usize,
26957        _push_to_history: bool,
26958        _all_commit_ranges: Vec<Range<language::Anchor>>,
26959        _cx: &mut Context<Editor>,
26960    ) -> Task<Result<Option<language::Transaction>>> {
26961        Task::ready(Ok(None))
26962    }
26963
26964    fn is_completion_trigger(
26965        &self,
26966        buffer: &Entity<Buffer>,
26967        position: language::Anchor,
26968        text: &str,
26969        trigger_in_words: bool,
26970        cx: &mut Context<Editor>,
26971    ) -> bool;
26972
26973    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26974
26975    fn sort_completions(&self) -> bool {
26976        true
26977    }
26978
26979    fn filter_completions(&self) -> bool {
26980        true
26981    }
26982
26983    fn show_snippets(&self) -> bool {
26984        false
26985    }
26986}
26987
26988pub trait CodeActionProvider {
26989    fn id(&self) -> Arc<str>;
26990
26991    fn code_actions(
26992        &self,
26993        buffer: &Entity<Buffer>,
26994        range: Range<text::Anchor>,
26995        window: &mut Window,
26996        cx: &mut App,
26997    ) -> Task<Result<Vec<CodeAction>>>;
26998
26999    fn apply_code_action(
27000        &self,
27001        buffer_handle: Entity<Buffer>,
27002        action: CodeAction,
27003        excerpt_id: ExcerptId,
27004        push_to_history: bool,
27005        window: &mut Window,
27006        cx: &mut App,
27007    ) -> Task<Result<ProjectTransaction>>;
27008}
27009
27010impl CodeActionProvider for Entity<Project> {
27011    fn id(&self) -> Arc<str> {
27012        "project".into()
27013    }
27014
27015    fn code_actions(
27016        &self,
27017        buffer: &Entity<Buffer>,
27018        range: Range<text::Anchor>,
27019        _window: &mut Window,
27020        cx: &mut App,
27021    ) -> Task<Result<Vec<CodeAction>>> {
27022        self.update(cx, |project, cx| {
27023            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27024            let code_actions = project.code_actions(buffer, range, None, cx);
27025            cx.background_spawn(async move {
27026                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27027                Ok(code_lens_actions
27028                    .context("code lens fetch")?
27029                    .into_iter()
27030                    .flatten()
27031                    .chain(
27032                        code_actions
27033                            .context("code action fetch")?
27034                            .into_iter()
27035                            .flatten(),
27036                    )
27037                    .collect())
27038            })
27039        })
27040    }
27041
27042    fn apply_code_action(
27043        &self,
27044        buffer_handle: Entity<Buffer>,
27045        action: CodeAction,
27046        _excerpt_id: ExcerptId,
27047        push_to_history: bool,
27048        _window: &mut Window,
27049        cx: &mut App,
27050    ) -> Task<Result<ProjectTransaction>> {
27051        self.update(cx, |project, cx| {
27052            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27053        })
27054    }
27055}
27056
27057fn snippet_completions(
27058    project: &Project,
27059    buffer: &Entity<Buffer>,
27060    buffer_anchor: text::Anchor,
27061    classifier: CharClassifier,
27062    cx: &mut App,
27063) -> Task<Result<CompletionResponse>> {
27064    let languages = buffer.read(cx).languages_at(buffer_anchor);
27065    let snippet_store = project.snippets().read(cx);
27066
27067    let scopes: Vec<_> = languages
27068        .iter()
27069        .filter_map(|language| {
27070            let language_name = language.lsp_id();
27071            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27072
27073            if snippets.is_empty() {
27074                None
27075            } else {
27076                Some((language.default_scope(), snippets))
27077            }
27078        })
27079        .collect();
27080
27081    if scopes.is_empty() {
27082        return Task::ready(Ok(CompletionResponse {
27083            completions: vec![],
27084            display_options: CompletionDisplayOptions::default(),
27085            is_incomplete: false,
27086        }));
27087    }
27088
27089    let snapshot = buffer.read(cx).text_snapshot();
27090    let executor = cx.background_executor().clone();
27091
27092    cx.background_spawn(async move {
27093        let is_word_char = |c| classifier.is_word(c);
27094
27095        let mut is_incomplete = false;
27096        let mut completions: Vec<Completion> = Vec::new();
27097
27098        const MAX_PREFIX_LEN: usize = 128;
27099        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27100        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27101        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27102
27103        let max_buffer_window: String = snapshot
27104            .text_for_range(window_start..buffer_offset)
27105            .collect();
27106
27107        if max_buffer_window.is_empty() {
27108            return Ok(CompletionResponse {
27109                completions: vec![],
27110                display_options: CompletionDisplayOptions::default(),
27111                is_incomplete: true,
27112            });
27113        }
27114
27115        for (_scope, snippets) in scopes.into_iter() {
27116            // Sort snippets by word count to match longer snippet prefixes first.
27117            let mut sorted_snippet_candidates = snippets
27118                .iter()
27119                .enumerate()
27120                .flat_map(|(snippet_ix, snippet)| {
27121                    snippet
27122                        .prefix
27123                        .iter()
27124                        .enumerate()
27125                        .map(move |(prefix_ix, prefix)| {
27126                            let word_count =
27127                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27128                            ((snippet_ix, prefix_ix), prefix, word_count)
27129                        })
27130                })
27131                .collect_vec();
27132            sorted_snippet_candidates
27133                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27134
27135            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27136
27137            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27138                .take(
27139                    sorted_snippet_candidates
27140                        .first()
27141                        .map(|(_, _, word_count)| *word_count)
27142                        .unwrap_or_default(),
27143                )
27144                .collect_vec();
27145
27146            const MAX_RESULTS: usize = 100;
27147            // Each match also remembers how many characters from the buffer it consumed
27148            let mut matches: Vec<(StringMatch, usize)> = vec![];
27149
27150            let mut snippet_list_cutoff_index = 0;
27151            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27152                let word_count = buffer_index + 1;
27153                // Increase `snippet_list_cutoff_index` until we have all of the
27154                // snippets with sufficiently many words.
27155                while sorted_snippet_candidates
27156                    .get(snippet_list_cutoff_index)
27157                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27158                        *snippet_word_count >= word_count
27159                    })
27160                {
27161                    snippet_list_cutoff_index += 1;
27162                }
27163
27164                // Take only the candidates with at least `word_count` many words
27165                let snippet_candidates_at_word_len =
27166                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27167
27168                let candidates = snippet_candidates_at_word_len
27169                    .iter()
27170                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27171                    .enumerate() // index in `sorted_snippet_candidates`
27172                    // First char must match
27173                    .filter(|(_ix, prefix)| {
27174                        itertools::equal(
27175                            prefix
27176                                .chars()
27177                                .next()
27178                                .into_iter()
27179                                .flat_map(|c| c.to_lowercase()),
27180                            buffer_window
27181                                .chars()
27182                                .next()
27183                                .into_iter()
27184                                .flat_map(|c| c.to_lowercase()),
27185                        )
27186                    })
27187                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27188                    .collect::<Vec<StringMatchCandidate>>();
27189
27190                matches.extend(
27191                    fuzzy::match_strings(
27192                        &candidates,
27193                        &buffer_window,
27194                        buffer_window.chars().any(|c| c.is_uppercase()),
27195                        true,
27196                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27197                        &Default::default(),
27198                        executor.clone(),
27199                    )
27200                    .await
27201                    .into_iter()
27202                    .map(|string_match| (string_match, buffer_window.len())),
27203                );
27204
27205                if matches.len() >= MAX_RESULTS {
27206                    break;
27207                }
27208            }
27209
27210            let to_lsp = |point: &text::Anchor| {
27211                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27212                point_to_lsp(end)
27213            };
27214            let lsp_end = to_lsp(&buffer_anchor);
27215
27216            if matches.len() >= MAX_RESULTS {
27217                is_incomplete = true;
27218            }
27219
27220            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27221                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27222                    sorted_snippet_candidates[string_match.candidate_id];
27223                let snippet = &snippets[snippet_index];
27224                let start = buffer_offset - buffer_window_len;
27225                let start = snapshot.anchor_before(start);
27226                let range = start..buffer_anchor;
27227                let lsp_start = to_lsp(&start);
27228                let lsp_range = lsp::Range {
27229                    start: lsp_start,
27230                    end: lsp_end,
27231                };
27232                Completion {
27233                    replace_range: range,
27234                    new_text: snippet.body.clone(),
27235                    source: CompletionSource::Lsp {
27236                        insert_range: None,
27237                        server_id: LanguageServerId(usize::MAX),
27238                        resolved: true,
27239                        lsp_completion: Box::new(lsp::CompletionItem {
27240                            label: snippet.prefix.first().unwrap().clone(),
27241                            kind: Some(CompletionItemKind::SNIPPET),
27242                            label_details: snippet.description.as_ref().map(|description| {
27243                                lsp::CompletionItemLabelDetails {
27244                                    detail: Some(description.clone()),
27245                                    description: None,
27246                                }
27247                            }),
27248                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27249                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27250                                lsp::InsertReplaceEdit {
27251                                    new_text: snippet.body.clone(),
27252                                    insert: lsp_range,
27253                                    replace: lsp_range,
27254                                },
27255                            )),
27256                            filter_text: Some(snippet.body.clone()),
27257                            sort_text: Some(char::MAX.to_string()),
27258                            ..lsp::CompletionItem::default()
27259                        }),
27260                        lsp_defaults: None,
27261                    },
27262                    label: CodeLabel {
27263                        text: matching_prefix.clone(),
27264                        runs: Vec::new(),
27265                        filter_range: 0..matching_prefix.len(),
27266                    },
27267                    icon_path: None,
27268                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27269                        single_line: snippet.name.clone().into(),
27270                        plain_text: snippet
27271                            .description
27272                            .clone()
27273                            .map(|description| description.into()),
27274                    }),
27275                    insert_text_mode: None,
27276                    confirm: None,
27277                    match_start: Some(start),
27278                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27279                }
27280            }));
27281        }
27282
27283        Ok(CompletionResponse {
27284            completions,
27285            display_options: CompletionDisplayOptions::default(),
27286            is_incomplete,
27287        })
27288    })
27289}
27290
27291impl CompletionProvider for Entity<Project> {
27292    fn completions(
27293        &self,
27294        _excerpt_id: ExcerptId,
27295        buffer: &Entity<Buffer>,
27296        buffer_position: text::Anchor,
27297        options: CompletionContext,
27298        _window: &mut Window,
27299        cx: &mut Context<Editor>,
27300    ) -> Task<Result<Vec<CompletionResponse>>> {
27301        self.update(cx, |project, cx| {
27302            let task = project.completions(buffer, buffer_position, options, cx);
27303            cx.background_spawn(task)
27304        })
27305    }
27306
27307    fn resolve_completions(
27308        &self,
27309        buffer: Entity<Buffer>,
27310        completion_indices: Vec<usize>,
27311        completions: Rc<RefCell<Box<[Completion]>>>,
27312        cx: &mut Context<Editor>,
27313    ) -> Task<Result<bool>> {
27314        self.update(cx, |project, cx| {
27315            project.lsp_store().update(cx, |lsp_store, cx| {
27316                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27317            })
27318        })
27319    }
27320
27321    fn apply_additional_edits_for_completion(
27322        &self,
27323        buffer: Entity<Buffer>,
27324        completions: Rc<RefCell<Box<[Completion]>>>,
27325        completion_index: usize,
27326        push_to_history: bool,
27327        all_commit_ranges: Vec<Range<language::Anchor>>,
27328        cx: &mut Context<Editor>,
27329    ) -> Task<Result<Option<language::Transaction>>> {
27330        self.update(cx, |project, cx| {
27331            project.lsp_store().update(cx, |lsp_store, cx| {
27332                lsp_store.apply_additional_edits_for_completion(
27333                    buffer,
27334                    completions,
27335                    completion_index,
27336                    push_to_history,
27337                    all_commit_ranges,
27338                    cx,
27339                )
27340            })
27341        })
27342    }
27343
27344    fn is_completion_trigger(
27345        &self,
27346        buffer: &Entity<Buffer>,
27347        position: language::Anchor,
27348        text: &str,
27349        trigger_in_words: bool,
27350        cx: &mut Context<Editor>,
27351    ) -> bool {
27352        let mut chars = text.chars();
27353        let char = if let Some(char) = chars.next() {
27354            char
27355        } else {
27356            return false;
27357        };
27358        if chars.next().is_some() {
27359            return false;
27360        }
27361
27362        let buffer = buffer.read(cx);
27363        let snapshot = buffer.snapshot();
27364        let classifier = snapshot
27365            .char_classifier_at(position)
27366            .scope_context(Some(CharScopeContext::Completion));
27367        if trigger_in_words && classifier.is_word(char) {
27368            return true;
27369        }
27370
27371        buffer.completion_triggers().contains(text)
27372    }
27373
27374    fn show_snippets(&self) -> bool {
27375        true
27376    }
27377}
27378
27379impl SemanticsProvider for WeakEntity<Project> {
27380    fn hover(
27381        &self,
27382        buffer: &Entity<Buffer>,
27383        position: text::Anchor,
27384        cx: &mut App,
27385    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27386        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27387            .ok()
27388    }
27389
27390    fn document_highlights(
27391        &self,
27392        buffer: &Entity<Buffer>,
27393        position: text::Anchor,
27394        cx: &mut App,
27395    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27396        self.update(cx, |project, cx| {
27397            project.document_highlights(buffer, position, cx)
27398        })
27399        .ok()
27400    }
27401
27402    fn definitions(
27403        &self,
27404        buffer: &Entity<Buffer>,
27405        position: text::Anchor,
27406        kind: GotoDefinitionKind,
27407        cx: &mut App,
27408    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27409        self.update(cx, |project, cx| match kind {
27410            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27411            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27412            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27413            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27414        })
27415        .ok()
27416    }
27417
27418    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27419        self.update(cx, |project, cx| {
27420            if project
27421                .active_debug_session(cx)
27422                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27423            {
27424                return true;
27425            }
27426
27427            buffer.update(cx, |buffer, cx| {
27428                project.any_language_server_supports_inlay_hints(buffer, cx)
27429            })
27430        })
27431        .unwrap_or(false)
27432    }
27433
27434    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27435        self.update(cx, |project, cx| {
27436            buffer.update(cx, |buffer, cx| {
27437                project.any_language_server_supports_semantic_tokens(buffer, cx)
27438            })
27439        })
27440        .unwrap_or(false)
27441    }
27442
27443    fn inline_values(
27444        &self,
27445        buffer_handle: Entity<Buffer>,
27446        range: Range<text::Anchor>,
27447        cx: &mut App,
27448    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27449        self.update(cx, |project, cx| {
27450            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27451
27452            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27453        })
27454        .ok()
27455        .flatten()
27456    }
27457
27458    fn applicable_inlay_chunks(
27459        &self,
27460        buffer: &Entity<Buffer>,
27461        ranges: &[Range<text::Anchor>],
27462        cx: &mut App,
27463    ) -> Vec<Range<BufferRow>> {
27464        self.update(cx, |project, cx| {
27465            project.lsp_store().update(cx, |lsp_store, cx| {
27466                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27467            })
27468        })
27469        .unwrap_or_default()
27470    }
27471
27472    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27473        self.update(cx, |project, cx| {
27474            project.lsp_store().update(cx, |lsp_store, _| {
27475                lsp_store.invalidate_inlay_hints(for_buffers)
27476            })
27477        })
27478        .ok();
27479    }
27480
27481    fn inlay_hints(
27482        &self,
27483        invalidate: InvalidationStrategy,
27484        buffer: Entity<Buffer>,
27485        ranges: Vec<Range<text::Anchor>>,
27486        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27487        cx: &mut App,
27488    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27489        self.update(cx, |project, cx| {
27490            project.lsp_store().update(cx, |lsp_store, cx| {
27491                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27492            })
27493        })
27494        .ok()
27495    }
27496
27497    fn semantic_tokens(
27498        &self,
27499        buffer: Entity<Buffer>,
27500        refresh: Option<RefreshForServer>,
27501        cx: &mut App,
27502    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27503        self.update(cx, |this, cx| {
27504            this.lsp_store().update(cx, |lsp_store, cx| {
27505                lsp_store.semantic_tokens(buffer, refresh, cx)
27506            })
27507        })
27508        .ok()
27509    }
27510
27511    fn range_for_rename(
27512        &self,
27513        buffer: &Entity<Buffer>,
27514        position: text::Anchor,
27515        cx: &mut App,
27516    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27517        self.update(cx, |project, cx| {
27518            let buffer = buffer.clone();
27519            let task = project.prepare_rename(buffer.clone(), position, cx);
27520            cx.spawn(async move |_, cx| {
27521                Ok(match task.await? {
27522                    PrepareRenameResponse::Success(range) => Some(range),
27523                    PrepareRenameResponse::InvalidPosition => None,
27524                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27525                        // Fallback on using TreeSitter info to determine identifier range
27526                        buffer.read_with(cx, |buffer, _| {
27527                            let snapshot = buffer.snapshot();
27528                            let (range, kind) = snapshot.surrounding_word(position, None);
27529                            if kind != Some(CharKind::Word) {
27530                                return None;
27531                            }
27532                            Some(
27533                                snapshot.anchor_before(range.start)
27534                                    ..snapshot.anchor_after(range.end),
27535                            )
27536                        })
27537                    }
27538                })
27539            })
27540        })
27541        .ok()
27542    }
27543
27544    fn perform_rename(
27545        &self,
27546        buffer: &Entity<Buffer>,
27547        position: text::Anchor,
27548        new_name: String,
27549        cx: &mut App,
27550    ) -> Option<Task<Result<ProjectTransaction>>> {
27551        self.update(cx, |project, cx| {
27552            project.perform_rename(buffer.clone(), position, new_name, cx)
27553        })
27554        .ok()
27555    }
27556}
27557
27558fn consume_contiguous_rows(
27559    contiguous_row_selections: &mut Vec<Selection<Point>>,
27560    selection: &Selection<Point>,
27561    display_map: &DisplaySnapshot,
27562    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27563) -> (MultiBufferRow, MultiBufferRow) {
27564    contiguous_row_selections.push(selection.clone());
27565    let start_row = starting_row(selection, display_map);
27566    let mut end_row = ending_row(selection, display_map);
27567
27568    while let Some(next_selection) = selections.peek() {
27569        if next_selection.start.row <= end_row.0 {
27570            end_row = ending_row(next_selection, display_map);
27571            contiguous_row_selections.push(selections.next().unwrap().clone());
27572        } else {
27573            break;
27574        }
27575    }
27576    (start_row, end_row)
27577}
27578
27579fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27580    if selection.start.column > 0 {
27581        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27582    } else {
27583        MultiBufferRow(selection.start.row)
27584    }
27585}
27586
27587fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27588    if next_selection.end.column > 0 || next_selection.is_empty() {
27589        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27590    } else {
27591        MultiBufferRow(next_selection.end.row)
27592    }
27593}
27594
27595impl EditorSnapshot {
27596    pub fn remote_selections_in_range<'a>(
27597        &'a self,
27598        range: &'a Range<Anchor>,
27599        collaboration_hub: &dyn CollaborationHub,
27600        cx: &'a App,
27601    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27602        let participant_names = collaboration_hub.user_names(cx);
27603        let participant_indices = collaboration_hub.user_participant_indices(cx);
27604        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27605        let collaborators_by_replica_id = collaborators_by_peer_id
27606            .values()
27607            .map(|collaborator| (collaborator.replica_id, collaborator))
27608            .collect::<HashMap<_, _>>();
27609        self.buffer_snapshot()
27610            .selections_in_range(range, false)
27611            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27612                if replica_id == ReplicaId::AGENT {
27613                    Some(RemoteSelection {
27614                        replica_id,
27615                        selection,
27616                        cursor_shape,
27617                        line_mode,
27618                        collaborator_id: CollaboratorId::Agent,
27619                        user_name: Some("Agent".into()),
27620                        color: cx.theme().players().agent(),
27621                    })
27622                } else {
27623                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27624                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27625                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27626                    Some(RemoteSelection {
27627                        replica_id,
27628                        selection,
27629                        cursor_shape,
27630                        line_mode,
27631                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27632                        user_name,
27633                        color: if let Some(index) = participant_index {
27634                            cx.theme().players().color_for_participant(index.0)
27635                        } else {
27636                            cx.theme().players().absent()
27637                        },
27638                    })
27639                }
27640            })
27641    }
27642
27643    pub fn hunks_for_ranges(
27644        &self,
27645        ranges: impl IntoIterator<Item = Range<Point>>,
27646    ) -> Vec<MultiBufferDiffHunk> {
27647        let mut hunks = Vec::new();
27648        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27649            HashMap::default();
27650        for query_range in ranges {
27651            let query_rows =
27652                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27653            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27654                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27655            ) {
27656                // Include deleted hunks that are adjacent to the query range, because
27657                // otherwise they would be missed.
27658                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27659                if hunk.status().is_deleted() {
27660                    intersects_range |= hunk.row_range.start == query_rows.end;
27661                    intersects_range |= hunk.row_range.end == query_rows.start;
27662                }
27663                if intersects_range {
27664                    if !processed_buffer_rows
27665                        .entry(hunk.buffer_id)
27666                        .or_default()
27667                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27668                    {
27669                        continue;
27670                    }
27671                    hunks.push(hunk);
27672                }
27673            }
27674        }
27675
27676        hunks
27677    }
27678
27679    fn display_diff_hunks_for_rows<'a>(
27680        &'a self,
27681        display_rows: Range<DisplayRow>,
27682        folded_buffers: &'a HashSet<BufferId>,
27683    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27684        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27685        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27686
27687        self.buffer_snapshot()
27688            .diff_hunks_in_range(buffer_start..buffer_end)
27689            .filter_map(|hunk| {
27690                if folded_buffers.contains(&hunk.buffer_id)
27691                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27692                {
27693                    return None;
27694                }
27695
27696                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27697                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27698                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27699                    let line_len = self.buffer_snapshot().line_len(last_row);
27700                    Point::new(last_row.0, line_len)
27701                } else {
27702                    Point::new(hunk.row_range.end.0, 0)
27703                };
27704
27705                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27706                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27707
27708                let display_hunk = if hunk_display_start.column() != 0 {
27709                    DisplayDiffHunk::Folded {
27710                        display_row: hunk_display_start.row(),
27711                    }
27712                } else {
27713                    let mut end_row = hunk_display_end.row();
27714                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27715                        end_row.0 += 1;
27716                    }
27717                    let is_created_file = hunk.is_created_file();
27718
27719                    DisplayDiffHunk::Unfolded {
27720                        status: hunk.status(),
27721                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27722                            ..hunk.diff_base_byte_range.end.0,
27723                        word_diffs: hunk.word_diffs,
27724                        display_row_range: hunk_display_start.row()..end_row,
27725                        multi_buffer_range: Anchor::range_in_buffer(
27726                            hunk.excerpt_id,
27727                            hunk.buffer_range,
27728                        ),
27729                        is_created_file,
27730                    }
27731                };
27732
27733                Some(display_hunk)
27734            })
27735    }
27736
27737    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27738        self.display_snapshot
27739            .buffer_snapshot()
27740            .language_at(position)
27741    }
27742
27743    pub fn is_focused(&self) -> bool {
27744        self.is_focused
27745    }
27746
27747    pub fn placeholder_text(&self) -> Option<String> {
27748        self.placeholder_display_snapshot
27749            .as_ref()
27750            .map(|display_map| display_map.text())
27751    }
27752
27753    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27754        self.scroll_anchor.scroll_position(&self.display_snapshot)
27755    }
27756
27757    pub fn gutter_dimensions(
27758        &self,
27759        font_id: FontId,
27760        font_size: Pixels,
27761        style: &EditorStyle,
27762        window: &mut Window,
27763        cx: &App,
27764    ) -> GutterDimensions {
27765        if self.show_gutter
27766            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27767            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27768        {
27769            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27770                matches!(
27771                    ProjectSettings::get_global(cx).git.git_gutter,
27772                    GitGutterSetting::TrackedFiles
27773                )
27774            });
27775            let gutter_settings = EditorSettings::get_global(cx).gutter;
27776            let show_line_numbers = self
27777                .show_line_numbers
27778                .unwrap_or(gutter_settings.line_numbers);
27779            let line_gutter_width = if show_line_numbers {
27780                // Avoid flicker-like gutter resizes when the line number gains another digit by
27781                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27782                let min_width_for_number_on_gutter =
27783                    ch_advance * gutter_settings.min_line_number_digits as f32;
27784                self.max_line_number_width(style, window)
27785                    .max(min_width_for_number_on_gutter)
27786            } else {
27787                0.0.into()
27788            };
27789
27790            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27791            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27792
27793            let git_blame_entries_width =
27794                self.git_blame_gutter_max_author_length
27795                    .map(|max_author_length| {
27796                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27797                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27798
27799                        /// The number of characters to dedicate to gaps and margins.
27800                        const SPACING_WIDTH: usize = 4;
27801
27802                        let max_char_count = max_author_length.min(renderer.max_author_length())
27803                            + ::git::SHORT_SHA_LENGTH
27804                            + MAX_RELATIVE_TIMESTAMP.len()
27805                            + SPACING_WIDTH;
27806
27807                        ch_advance * max_char_count
27808                    });
27809
27810            let is_singleton = self.buffer_snapshot().is_singleton();
27811
27812            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27813            left_padding += if !is_singleton {
27814                ch_width * 4.0
27815            } else if show_runnables || show_breakpoints {
27816                ch_width * 3.0
27817            } else if show_git_gutter && show_line_numbers {
27818                ch_width * 2.0
27819            } else if show_git_gutter || show_line_numbers {
27820                ch_width
27821            } else {
27822                px(0.)
27823            };
27824
27825            let shows_folds = is_singleton && gutter_settings.folds;
27826
27827            let right_padding = if shows_folds && show_line_numbers {
27828                ch_width * 4.0
27829            } else if shows_folds || (!is_singleton && show_line_numbers) {
27830                ch_width * 3.0
27831            } else if show_line_numbers {
27832                ch_width
27833            } else {
27834                px(0.)
27835            };
27836
27837            GutterDimensions {
27838                left_padding,
27839                right_padding,
27840                width: line_gutter_width + left_padding + right_padding,
27841                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27842                git_blame_entries_width,
27843            }
27844        } else if self.offset_content {
27845            GutterDimensions::default_with_margin(font_id, font_size, cx)
27846        } else {
27847            GutterDimensions::default()
27848        }
27849    }
27850
27851    pub fn render_crease_toggle(
27852        &self,
27853        buffer_row: MultiBufferRow,
27854        row_contains_cursor: bool,
27855        editor: Entity<Editor>,
27856        window: &mut Window,
27857        cx: &mut App,
27858    ) -> Option<AnyElement> {
27859        let folded = self.is_line_folded(buffer_row);
27860        let mut is_foldable = false;
27861
27862        if let Some(crease) = self
27863            .crease_snapshot
27864            .query_row(buffer_row, self.buffer_snapshot())
27865        {
27866            is_foldable = true;
27867            match crease {
27868                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27869                    if let Some(render_toggle) = render_toggle {
27870                        let toggle_callback =
27871                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27872                                if folded {
27873                                    editor.update(cx, |editor, cx| {
27874                                        editor.fold_at(buffer_row, window, cx)
27875                                    });
27876                                } else {
27877                                    editor.update(cx, |editor, cx| {
27878                                        editor.unfold_at(buffer_row, window, cx)
27879                                    });
27880                                }
27881                            });
27882                        return Some((render_toggle)(
27883                            buffer_row,
27884                            folded,
27885                            toggle_callback,
27886                            window,
27887                            cx,
27888                        ));
27889                    }
27890                }
27891            }
27892        }
27893
27894        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27895
27896        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27897            Some(
27898                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27899                    .toggle_state(folded)
27900                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27901                        if folded {
27902                            this.unfold_at(buffer_row, window, cx);
27903                        } else {
27904                            this.fold_at(buffer_row, window, cx);
27905                        }
27906                    }))
27907                    .into_any_element(),
27908            )
27909        } else {
27910            None
27911        }
27912    }
27913
27914    pub fn render_crease_trailer(
27915        &self,
27916        buffer_row: MultiBufferRow,
27917        window: &mut Window,
27918        cx: &mut App,
27919    ) -> Option<AnyElement> {
27920        let folded = self.is_line_folded(buffer_row);
27921        if let Crease::Inline { render_trailer, .. } = self
27922            .crease_snapshot
27923            .query_row(buffer_row, self.buffer_snapshot())?
27924        {
27925            let render_trailer = render_trailer.as_ref()?;
27926            Some(render_trailer(buffer_row, folded, window, cx))
27927        } else {
27928            None
27929        }
27930    }
27931
27932    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27933        let digit_count = self.widest_line_number().ilog10() + 1;
27934        column_pixels(style, digit_count as usize, window)
27935    }
27936
27937    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27938    ///
27939    /// This is positive if `base` is before `line`.
27940    fn relative_line_delta(
27941        &self,
27942        current_selection_head: DisplayRow,
27943        first_visible_row: DisplayRow,
27944        consider_wrapped_lines: bool,
27945    ) -> i64 {
27946        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27947        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27948
27949        if consider_wrapped_lines {
27950            let wrap_snapshot = self.wrap_snapshot();
27951            let base_wrap_row = wrap_snapshot
27952                .make_wrap_point(current_selection_head, Bias::Left)
27953                .row();
27954            let wrap_row = wrap_snapshot
27955                .make_wrap_point(first_visible_row, Bias::Left)
27956                .row();
27957
27958            wrap_row.0 as i64 - base_wrap_row.0 as i64
27959        } else {
27960            let fold_snapshot = self.fold_snapshot();
27961            let base_fold_row = fold_snapshot
27962                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27963                .row();
27964            let fold_row = fold_snapshot
27965                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27966                .row();
27967
27968            fold_row as i64 - base_fold_row as i64
27969        }
27970    }
27971
27972    /// Returns the unsigned relative line number to display for each row in `rows`.
27973    ///
27974    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27975    pub fn calculate_relative_line_numbers(
27976        &self,
27977        rows: &Range<DisplayRow>,
27978        current_selection_head: DisplayRow,
27979        count_wrapped_lines: bool,
27980    ) -> HashMap<DisplayRow, u32> {
27981        let initial_offset =
27982            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27983
27984        self.row_infos(rows.start)
27985            .take(rows.len())
27986            .enumerate()
27987            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27988            .filter(|(_row, row_info)| {
27989                row_info.buffer_row.is_some()
27990                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27991            })
27992            .enumerate()
27993            .filter_map(|(i, (row, row_info))| {
27994                // We want to ensure here that the current line has absolute
27995                // numbering, even if we are in a soft-wrapped line. With the
27996                // exception that if we are in a deleted line, we should number this
27997                // relative with 0, as otherwise it would have no line number at all
27998                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27999
28000                (relative_line_number != 0
28001                    || row_info
28002                        .diff_status
28003                        .is_some_and(|status| status.is_deleted()))
28004                .then_some((row, relative_line_number))
28005            })
28006            .collect()
28007    }
28008}
28009
28010pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28011    let font_size = style.text.font_size.to_pixels(window.rem_size());
28012    let layout = window.text_system().shape_line(
28013        SharedString::from(" ".repeat(column)),
28014        font_size,
28015        &[TextRun {
28016            len: column,
28017            font: style.text.font(),
28018            color: Hsla::default(),
28019            ..Default::default()
28020        }],
28021        None,
28022    );
28023
28024    layout.width
28025}
28026
28027impl Deref for EditorSnapshot {
28028    type Target = DisplaySnapshot;
28029
28030    fn deref(&self) -> &Self::Target {
28031        &self.display_snapshot
28032    }
28033}
28034
28035#[derive(Clone, Debug, PartialEq, Eq)]
28036pub enum EditorEvent {
28037    /// Emitted when the stored review comments change (added, removed, or updated).
28038    ReviewCommentsChanged {
28039        /// The new total count of review comments.
28040        total_count: usize,
28041    },
28042    InputIgnored {
28043        text: Arc<str>,
28044    },
28045    InputHandled {
28046        utf16_range_to_replace: Option<Range<isize>>,
28047        text: Arc<str>,
28048    },
28049    ExcerptsAdded {
28050        buffer: Entity<Buffer>,
28051        predecessor: ExcerptId,
28052        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28053    },
28054    ExcerptsRemoved {
28055        ids: Vec<ExcerptId>,
28056        removed_buffer_ids: Vec<BufferId>,
28057    },
28058    BufferFoldToggled {
28059        ids: Vec<ExcerptId>,
28060        folded: bool,
28061    },
28062    ExcerptsEdited {
28063        ids: Vec<ExcerptId>,
28064    },
28065    ExcerptsExpanded {
28066        ids: Vec<ExcerptId>,
28067    },
28068    ExpandExcerptsRequested {
28069        excerpt_ids: Vec<ExcerptId>,
28070        lines: u32,
28071        direction: ExpandExcerptDirection,
28072    },
28073    StageOrUnstageRequested {
28074        stage: bool,
28075        hunks: Vec<MultiBufferDiffHunk>,
28076    },
28077    OpenExcerptsRequested {
28078        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28079        split: bool,
28080    },
28081    RestoreRequested {
28082        hunks: Vec<MultiBufferDiffHunk>,
28083    },
28084    BufferEdited,
28085    Edited {
28086        transaction_id: clock::Lamport,
28087    },
28088    Reparsed(BufferId),
28089    Focused,
28090    FocusedIn,
28091    Blurred,
28092    DirtyChanged,
28093    Saved,
28094    TitleChanged,
28095    SelectionsChanged {
28096        local: bool,
28097    },
28098    ScrollPositionChanged {
28099        local: bool,
28100        autoscroll: bool,
28101    },
28102    TransactionUndone {
28103        transaction_id: clock::Lamport,
28104    },
28105    TransactionBegun {
28106        transaction_id: clock::Lamport,
28107    },
28108    CursorShapeChanged,
28109    BreadcrumbsChanged,
28110    OutlineSymbolsChanged,
28111    PushedToNavHistory {
28112        anchor: Anchor,
28113        is_deactivate: bool,
28114    },
28115}
28116
28117impl EventEmitter<EditorEvent> for Editor {}
28118
28119impl Focusable for Editor {
28120    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28121        self.focus_handle.clone()
28122    }
28123}
28124
28125impl Render for Editor {
28126    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28127        EditorElement::new(&cx.entity(), self.create_style(cx))
28128    }
28129}
28130
28131impl EntityInputHandler for Editor {
28132    fn text_for_range(
28133        &mut self,
28134        range_utf16: Range<usize>,
28135        adjusted_range: &mut Option<Range<usize>>,
28136        _: &mut Window,
28137        cx: &mut Context<Self>,
28138    ) -> Option<String> {
28139        let snapshot = self.buffer.read(cx).read(cx);
28140        let start = snapshot.clip_offset_utf16(
28141            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28142            Bias::Left,
28143        );
28144        let end = snapshot.clip_offset_utf16(
28145            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28146            Bias::Right,
28147        );
28148        if (start.0.0..end.0.0) != range_utf16 {
28149            adjusted_range.replace(start.0.0..end.0.0);
28150        }
28151        Some(snapshot.text_for_range(start..end).collect())
28152    }
28153
28154    fn selected_text_range(
28155        &mut self,
28156        ignore_disabled_input: bool,
28157        _: &mut Window,
28158        cx: &mut Context<Self>,
28159    ) -> Option<UTF16Selection> {
28160        // Prevent the IME menu from appearing when holding down an alphabetic key
28161        // while input is disabled.
28162        if !ignore_disabled_input && !self.input_enabled {
28163            return None;
28164        }
28165
28166        let selection = self
28167            .selections
28168            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28169        let range = selection.range();
28170
28171        Some(UTF16Selection {
28172            range: range.start.0.0..range.end.0.0,
28173            reversed: selection.reversed,
28174        })
28175    }
28176
28177    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28178        let snapshot = self.buffer.read(cx).read(cx);
28179        let range = self
28180            .text_highlights(HighlightKey::InputComposition, cx)?
28181            .1
28182            .first()?;
28183        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28184    }
28185
28186    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28187        self.clear_highlights(HighlightKey::InputComposition, cx);
28188        self.ime_transaction.take();
28189    }
28190
28191    fn replace_text_in_range(
28192        &mut self,
28193        range_utf16: Option<Range<usize>>,
28194        text: &str,
28195        window: &mut Window,
28196        cx: &mut Context<Self>,
28197    ) {
28198        if !self.input_enabled {
28199            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28200            return;
28201        }
28202
28203        self.transact(window, cx, |this, window, cx| {
28204            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28205                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28206                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28207                Some(this.selection_replacement_ranges(range_utf16, cx))
28208            } else {
28209                this.marked_text_ranges(cx)
28210            };
28211
28212            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28213                let newest_selection_id = this.selections.newest_anchor().id;
28214                this.selections
28215                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28216                    .iter()
28217                    .zip(ranges_to_replace.iter())
28218                    .find_map(|(selection, range)| {
28219                        if selection.id == newest_selection_id {
28220                            Some(
28221                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28222                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28223                            )
28224                        } else {
28225                            None
28226                        }
28227                    })
28228            });
28229
28230            cx.emit(EditorEvent::InputHandled {
28231                utf16_range_to_replace: range_to_replace,
28232                text: text.into(),
28233            });
28234
28235            if let Some(new_selected_ranges) = new_selected_ranges {
28236                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28237                    selections.select_ranges(new_selected_ranges)
28238                });
28239                this.backspace(&Default::default(), window, cx);
28240            }
28241
28242            this.handle_input(text, window, cx);
28243        });
28244
28245        if let Some(transaction) = self.ime_transaction {
28246            self.buffer.update(cx, |buffer, cx| {
28247                buffer.group_until_transaction(transaction, cx);
28248            });
28249        }
28250
28251        self.unmark_text(window, cx);
28252    }
28253
28254    fn replace_and_mark_text_in_range(
28255        &mut self,
28256        range_utf16: Option<Range<usize>>,
28257        text: &str,
28258        new_selected_range_utf16: Option<Range<usize>>,
28259        window: &mut Window,
28260        cx: &mut Context<Self>,
28261    ) {
28262        if !self.input_enabled {
28263            return;
28264        }
28265
28266        let transaction = self.transact(window, cx, |this, window, cx| {
28267            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28268                let snapshot = this.buffer.read(cx).read(cx);
28269                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28270                    for marked_range in &mut marked_ranges {
28271                        marked_range.end = marked_range.start + relative_range_utf16.end;
28272                        marked_range.start += relative_range_utf16.start;
28273                        marked_range.start =
28274                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28275                        marked_range.end =
28276                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28277                    }
28278                }
28279                Some(marked_ranges)
28280            } else if let Some(range_utf16) = range_utf16 {
28281                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28282                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28283                Some(this.selection_replacement_ranges(range_utf16, cx))
28284            } else {
28285                None
28286            };
28287
28288            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28289                let newest_selection_id = this.selections.newest_anchor().id;
28290                this.selections
28291                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28292                    .iter()
28293                    .zip(ranges_to_replace.iter())
28294                    .find_map(|(selection, range)| {
28295                        if selection.id == newest_selection_id {
28296                            Some(
28297                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28298                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28299                            )
28300                        } else {
28301                            None
28302                        }
28303                    })
28304            });
28305
28306            cx.emit(EditorEvent::InputHandled {
28307                utf16_range_to_replace: range_to_replace,
28308                text: text.into(),
28309            });
28310
28311            if let Some(ranges) = ranges_to_replace {
28312                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28313                    s.select_ranges(ranges)
28314                });
28315            }
28316
28317            let marked_ranges = {
28318                let snapshot = this.buffer.read(cx).read(cx);
28319                this.selections
28320                    .disjoint_anchors_arc()
28321                    .iter()
28322                    .map(|selection| {
28323                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28324                    })
28325                    .collect::<Vec<_>>()
28326            };
28327
28328            if text.is_empty() {
28329                this.unmark_text(window, cx);
28330            } else {
28331                this.highlight_text(
28332                    HighlightKey::InputComposition,
28333                    marked_ranges.clone(),
28334                    HighlightStyle {
28335                        underline: Some(UnderlineStyle {
28336                            thickness: px(1.),
28337                            color: None,
28338                            wavy: false,
28339                        }),
28340                        ..Default::default()
28341                    },
28342                    cx,
28343                );
28344            }
28345
28346            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28347            let use_autoclose = this.use_autoclose;
28348            let use_auto_surround = this.use_auto_surround;
28349            this.set_use_autoclose(false);
28350            this.set_use_auto_surround(false);
28351            this.handle_input(text, window, cx);
28352            this.set_use_autoclose(use_autoclose);
28353            this.set_use_auto_surround(use_auto_surround);
28354
28355            if let Some(new_selected_range) = new_selected_range_utf16 {
28356                let snapshot = this.buffer.read(cx).read(cx);
28357                let new_selected_ranges = marked_ranges
28358                    .into_iter()
28359                    .map(|marked_range| {
28360                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28361                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28362                            insertion_start.0 + new_selected_range.start,
28363                        ));
28364                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28365                            insertion_start.0 + new_selected_range.end,
28366                        ));
28367                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28368                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28369                    })
28370                    .collect::<Vec<_>>();
28371
28372                drop(snapshot);
28373                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28374                    selections.select_ranges(new_selected_ranges)
28375                });
28376            }
28377        });
28378
28379        self.ime_transaction = self.ime_transaction.or(transaction);
28380        if let Some(transaction) = self.ime_transaction {
28381            self.buffer.update(cx, |buffer, cx| {
28382                buffer.group_until_transaction(transaction, cx);
28383            });
28384        }
28385
28386        if self
28387            .text_highlights(HighlightKey::InputComposition, cx)
28388            .is_none()
28389        {
28390            self.ime_transaction.take();
28391        }
28392    }
28393
28394    fn bounds_for_range(
28395        &mut self,
28396        range_utf16: Range<usize>,
28397        element_bounds: gpui::Bounds<Pixels>,
28398        window: &mut Window,
28399        cx: &mut Context<Self>,
28400    ) -> Option<gpui::Bounds<Pixels>> {
28401        let text_layout_details = self.text_layout_details(window, cx);
28402        let CharacterDimensions {
28403            em_width,
28404            em_advance,
28405            line_height,
28406        } = self.character_dimensions(window, cx);
28407
28408        let snapshot = self.snapshot(window, cx);
28409        let scroll_position = snapshot.scroll_position();
28410        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28411
28412        let start =
28413            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28414        let x = Pixels::from(
28415            ScrollOffset::from(
28416                snapshot.x_for_display_point(start, &text_layout_details)
28417                    + self.gutter_dimensions.full_width(),
28418            ) - scroll_left,
28419        );
28420        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28421
28422        Some(Bounds {
28423            origin: element_bounds.origin + point(x, y),
28424            size: size(em_width, line_height),
28425        })
28426    }
28427
28428    fn character_index_for_point(
28429        &mut self,
28430        point: gpui::Point<Pixels>,
28431        _window: &mut Window,
28432        _cx: &mut Context<Self>,
28433    ) -> Option<usize> {
28434        let position_map = self.last_position_map.as_ref()?;
28435        if !position_map.text_hitbox.contains(&point) {
28436            return None;
28437        }
28438        let display_point = position_map.point_for_position(point).previous_valid;
28439        let anchor = position_map
28440            .snapshot
28441            .display_point_to_anchor(display_point, Bias::Left);
28442        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28443        Some(utf16_offset.0.0)
28444    }
28445
28446    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28447        self.expects_character_input
28448    }
28449}
28450
28451trait SelectionExt {
28452    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28453    fn spanned_rows(
28454        &self,
28455        include_end_if_at_line_start: bool,
28456        map: &DisplaySnapshot,
28457    ) -> Range<MultiBufferRow>;
28458}
28459
28460impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28461    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28462        let start = self
28463            .start
28464            .to_point(map.buffer_snapshot())
28465            .to_display_point(map);
28466        let end = self
28467            .end
28468            .to_point(map.buffer_snapshot())
28469            .to_display_point(map);
28470        if self.reversed {
28471            end..start
28472        } else {
28473            start..end
28474        }
28475    }
28476
28477    fn spanned_rows(
28478        &self,
28479        include_end_if_at_line_start: bool,
28480        map: &DisplaySnapshot,
28481    ) -> Range<MultiBufferRow> {
28482        let start = self.start.to_point(map.buffer_snapshot());
28483        let mut end = self.end.to_point(map.buffer_snapshot());
28484        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28485            end.row -= 1;
28486        }
28487
28488        let buffer_start = map.prev_line_boundary(start).0;
28489        let buffer_end = map.next_line_boundary(end).0;
28490        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28491    }
28492}
28493
28494impl<T: InvalidationRegion> InvalidationStack<T> {
28495    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28496    where
28497        S: Clone + ToOffset,
28498    {
28499        while let Some(region) = self.last() {
28500            let all_selections_inside_invalidation_ranges =
28501                if selections.len() == region.ranges().len() {
28502                    selections
28503                        .iter()
28504                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28505                        .all(|(selection, invalidation_range)| {
28506                            let head = selection.head().to_offset(buffer);
28507                            invalidation_range.start <= head && invalidation_range.end >= head
28508                        })
28509                } else {
28510                    false
28511                };
28512
28513            if all_selections_inside_invalidation_ranges {
28514                break;
28515            } else {
28516                self.pop();
28517            }
28518        }
28519    }
28520}
28521
28522#[derive(Clone)]
28523struct ErasedEditorImpl(Entity<Editor>);
28524
28525impl ui_input::ErasedEditor for ErasedEditorImpl {
28526    fn text(&self, cx: &App) -> String {
28527        self.0.read(cx).text(cx)
28528    }
28529
28530    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28531        self.0.update(cx, |this, cx| {
28532            this.set_text(text, window, cx);
28533        })
28534    }
28535
28536    fn clear(&self, window: &mut Window, cx: &mut App) {
28537        self.0.update(cx, |this, cx| this.clear(window, cx));
28538    }
28539
28540    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28541        self.0.update(cx, |this, cx| {
28542            this.set_placeholder_text(text, window, cx);
28543        });
28544    }
28545
28546    fn focus_handle(&self, cx: &App) -> FocusHandle {
28547        self.0.read(cx).focus_handle(cx)
28548    }
28549
28550    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28551        let settings = ThemeSettings::get_global(cx);
28552        let theme_color = cx.theme().colors();
28553
28554        let text_style = TextStyle {
28555            font_family: settings.ui_font.family.clone(),
28556            font_features: settings.ui_font.features.clone(),
28557            font_size: rems(0.875).into(),
28558            font_weight: settings.ui_font.weight,
28559            font_style: FontStyle::Normal,
28560            line_height: relative(1.2),
28561            color: theme_color.text,
28562            ..Default::default()
28563        };
28564        let editor_style = EditorStyle {
28565            background: theme_color.ghost_element_background,
28566            local_player: cx.theme().players().local(),
28567            syntax: cx.theme().syntax().clone(),
28568            text: text_style,
28569            ..Default::default()
28570        };
28571        EditorElement::new(&self.0, editor_style).into_any()
28572    }
28573
28574    fn as_any(&self) -> &dyn Any {
28575        &self.0
28576    }
28577
28578    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28579        self.0.update(cx, |editor, cx| {
28580            let editor_offset = editor.buffer().read(cx).len(cx);
28581            editor.change_selections(
28582                SelectionEffects::scroll(Autoscroll::Next),
28583                window,
28584                cx,
28585                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28586            );
28587        });
28588    }
28589
28590    fn subscribe(
28591        &self,
28592        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28593        window: &mut Window,
28594        cx: &mut App,
28595    ) -> Subscription {
28596        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28597            let event = match event {
28598                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28599                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28600                _ => return,
28601            };
28602            (callback)(event, window, cx);
28603        })
28604    }
28605
28606    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28607        self.0.update(cx, |editor, cx| {
28608            editor.set_masked(masked, cx);
28609        });
28610    }
28611}
28612impl<T> Default for InvalidationStack<T> {
28613    fn default() -> Self {
28614        Self(Default::default())
28615    }
28616}
28617
28618impl<T> Deref for InvalidationStack<T> {
28619    type Target = Vec<T>;
28620
28621    fn deref(&self) -> &Self::Target {
28622        &self.0
28623    }
28624}
28625
28626impl<T> DerefMut for InvalidationStack<T> {
28627    fn deref_mut(&mut self) -> &mut Self::Target {
28628        &mut self.0
28629    }
28630}
28631
28632impl InvalidationRegion for SnippetState {
28633    fn ranges(&self) -> &[Range<Anchor>] {
28634        &self.ranges[self.active_index]
28635    }
28636}
28637
28638fn edit_prediction_edit_text(
28639    current_snapshot: &BufferSnapshot,
28640    edits: &[(Range<Anchor>, impl AsRef<str>)],
28641    edit_preview: &EditPreview,
28642    include_deletions: bool,
28643    cx: &App,
28644) -> HighlightedText {
28645    let edits = edits
28646        .iter()
28647        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28648        .collect::<Vec<_>>();
28649
28650    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28651}
28652
28653fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28654    // Fallback for providers that don't provide edit_preview (like Copilot)
28655    // Just show the raw edit text with basic styling
28656    let mut text = String::new();
28657    let mut highlights = Vec::new();
28658
28659    let insertion_highlight_style = HighlightStyle {
28660        color: Some(cx.theme().colors().text),
28661        ..Default::default()
28662    };
28663
28664    for (_, edit_text) in edits {
28665        let start_offset = text.len();
28666        text.push_str(edit_text);
28667        let end_offset = text.len();
28668
28669        if start_offset < end_offset {
28670            highlights.push((start_offset..end_offset, insertion_highlight_style));
28671        }
28672    }
28673
28674    HighlightedText {
28675        text: text.into(),
28676        highlights,
28677    }
28678}
28679
28680pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28681    match severity {
28682        lsp::DiagnosticSeverity::ERROR => colors.error,
28683        lsp::DiagnosticSeverity::WARNING => colors.warning,
28684        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28685        lsp::DiagnosticSeverity::HINT => colors.info,
28686        _ => colors.ignored,
28687    }
28688}
28689
28690pub fn styled_runs_for_code_label<'a>(
28691    label: &'a CodeLabel,
28692    syntax_theme: &'a theme::SyntaxTheme,
28693    local_player: &'a theme::PlayerColor,
28694) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28695    let fade_out = HighlightStyle {
28696        fade_out: Some(0.35),
28697        ..Default::default()
28698    };
28699
28700    let mut prev_end = label.filter_range.end;
28701    label
28702        .runs
28703        .iter()
28704        .enumerate()
28705        .flat_map(move |(ix, (range, highlight_id))| {
28706            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28707                HighlightStyle {
28708                    color: Some(local_player.cursor),
28709                    ..Default::default()
28710                }
28711            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28712                HighlightStyle {
28713                    background_color: Some(local_player.selection),
28714                    ..Default::default()
28715                }
28716            } else if let Some(style) = highlight_id.style(syntax_theme) {
28717                style
28718            } else {
28719                return Default::default();
28720            };
28721            let muted_style = style.highlight(fade_out);
28722
28723            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28724            if range.start >= label.filter_range.end {
28725                if range.start > prev_end {
28726                    runs.push((prev_end..range.start, fade_out));
28727                }
28728                runs.push((range.clone(), muted_style));
28729            } else if range.end <= label.filter_range.end {
28730                runs.push((range.clone(), style));
28731            } else {
28732                runs.push((range.start..label.filter_range.end, style));
28733                runs.push((label.filter_range.end..range.end, muted_style));
28734            }
28735            prev_end = cmp::max(prev_end, range.end);
28736
28737            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28738                runs.push((prev_end..label.text.len(), fade_out));
28739            }
28740
28741            runs
28742        })
28743}
28744
28745pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28746    let mut prev_index = 0;
28747    let mut prev_codepoint: Option<char> = None;
28748    text.char_indices()
28749        .chain([(text.len(), '\0')])
28750        .filter_map(move |(index, codepoint)| {
28751            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28752            let is_boundary = index == text.len()
28753                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28754                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28755            if is_boundary {
28756                let chunk = &text[prev_index..index];
28757                prev_index = index;
28758                Some(chunk)
28759            } else {
28760                None
28761            }
28762        })
28763}
28764
28765/// Given a string of text immediately before the cursor, iterates over possible
28766/// strings a snippet could match to. More precisely: returns an iterator over
28767/// suffixes of `text` created by splitting at word boundaries (before & after
28768/// every non-word character).
28769///
28770/// Shorter suffixes are returned first.
28771pub(crate) fn snippet_candidate_suffixes<'a>(
28772    text: &'a str,
28773    is_word_char: &'a dyn Fn(char) -> bool,
28774) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28775    let mut prev_index = text.len();
28776    let mut prev_codepoint = None;
28777    text.char_indices()
28778        .rev()
28779        .chain([(0, '\0')])
28780        .filter_map(move |(index, codepoint)| {
28781            let prev_index = std::mem::replace(&mut prev_index, index);
28782            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28783            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28784                None
28785            } else {
28786                let chunk = &text[prev_index..]; // go to end of string
28787                Some(chunk)
28788            }
28789        })
28790}
28791
28792pub trait RangeToAnchorExt: Sized {
28793    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28794
28795    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28796        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28797        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28798    }
28799}
28800
28801impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28802    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28803        let start_offset = self.start.to_offset(snapshot);
28804        let end_offset = self.end.to_offset(snapshot);
28805        if start_offset == end_offset {
28806            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28807        } else {
28808            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28809        }
28810    }
28811}
28812
28813pub trait RowExt {
28814    fn as_f64(&self) -> f64;
28815
28816    fn next_row(&self) -> Self;
28817
28818    fn previous_row(&self) -> Self;
28819
28820    fn minus(&self, other: Self) -> u32;
28821}
28822
28823impl RowExt for DisplayRow {
28824    fn as_f64(&self) -> f64 {
28825        self.0 as _
28826    }
28827
28828    fn next_row(&self) -> Self {
28829        Self(self.0 + 1)
28830    }
28831
28832    fn previous_row(&self) -> Self {
28833        Self(self.0.saturating_sub(1))
28834    }
28835
28836    fn minus(&self, other: Self) -> u32 {
28837        self.0 - other.0
28838    }
28839}
28840
28841impl RowExt for MultiBufferRow {
28842    fn as_f64(&self) -> f64 {
28843        self.0 as _
28844    }
28845
28846    fn next_row(&self) -> Self {
28847        Self(self.0 + 1)
28848    }
28849
28850    fn previous_row(&self) -> Self {
28851        Self(self.0.saturating_sub(1))
28852    }
28853
28854    fn minus(&self, other: Self) -> u32 {
28855        self.0 - other.0
28856    }
28857}
28858
28859trait RowRangeExt {
28860    type Row;
28861
28862    fn len(&self) -> usize;
28863
28864    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28865}
28866
28867impl RowRangeExt for Range<MultiBufferRow> {
28868    type Row = MultiBufferRow;
28869
28870    fn len(&self) -> usize {
28871        (self.end.0 - self.start.0) as usize
28872    }
28873
28874    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28875        (self.start.0..self.end.0).map(MultiBufferRow)
28876    }
28877}
28878
28879impl RowRangeExt for Range<DisplayRow> {
28880    type Row = DisplayRow;
28881
28882    fn len(&self) -> usize {
28883        (self.end.0 - self.start.0) as usize
28884    }
28885
28886    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28887        (self.start.0..self.end.0).map(DisplayRow)
28888    }
28889}
28890
28891/// If select range has more than one line, we
28892/// just point the cursor to range.start.
28893fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28894    if range.start.row == range.end.row {
28895        range
28896    } else {
28897        range.start..range.start
28898    }
28899}
28900pub struct KillRing(ClipboardItem);
28901impl Global for KillRing {}
28902
28903const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28904
28905enum BreakpointPromptEditAction {
28906    Log,
28907    Condition,
28908    HitCondition,
28909}
28910
28911struct BreakpointPromptEditor {
28912    pub(crate) prompt: Entity<Editor>,
28913    editor: WeakEntity<Editor>,
28914    breakpoint_anchor: Anchor,
28915    breakpoint: Breakpoint,
28916    edit_action: BreakpointPromptEditAction,
28917    block_ids: HashSet<CustomBlockId>,
28918    editor_margins: Arc<Mutex<EditorMargins>>,
28919    _subscriptions: Vec<Subscription>,
28920}
28921
28922impl BreakpointPromptEditor {
28923    const MAX_LINES: u8 = 4;
28924
28925    fn new(
28926        editor: WeakEntity<Editor>,
28927        breakpoint_anchor: Anchor,
28928        breakpoint: Breakpoint,
28929        edit_action: BreakpointPromptEditAction,
28930        window: &mut Window,
28931        cx: &mut Context<Self>,
28932    ) -> Self {
28933        let base_text = match edit_action {
28934            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28935            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28936            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28937        }
28938        .map(|msg| msg.to_string())
28939        .unwrap_or_default();
28940
28941        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28942        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28943
28944        let prompt = cx.new(|cx| {
28945            let mut prompt = Editor::new(
28946                EditorMode::AutoHeight {
28947                    min_lines: 1,
28948                    max_lines: Some(Self::MAX_LINES as usize),
28949                },
28950                buffer,
28951                None,
28952                window,
28953                cx,
28954            );
28955            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28956            prompt.set_show_cursor_when_unfocused(false, cx);
28957            prompt.set_placeholder_text(
28958                match edit_action {
28959                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28960                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28961                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28962                },
28963                window,
28964                cx,
28965            );
28966
28967            prompt
28968        });
28969
28970        Self {
28971            prompt,
28972            editor,
28973            breakpoint_anchor,
28974            breakpoint,
28975            edit_action,
28976            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28977            block_ids: Default::default(),
28978            _subscriptions: vec![],
28979        }
28980    }
28981
28982    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28983        self.block_ids.extend(block_ids)
28984    }
28985
28986    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28987        if let Some(editor) = self.editor.upgrade() {
28988            let message = self
28989                .prompt
28990                .read(cx)
28991                .buffer
28992                .read(cx)
28993                .as_singleton()
28994                .expect("A multi buffer in breakpoint prompt isn't possible")
28995                .read(cx)
28996                .as_rope()
28997                .to_string();
28998
28999            editor.update(cx, |editor, cx| {
29000                editor.edit_breakpoint_at_anchor(
29001                    self.breakpoint_anchor,
29002                    self.breakpoint.clone(),
29003                    match self.edit_action {
29004                        BreakpointPromptEditAction::Log => {
29005                            BreakpointEditAction::EditLogMessage(message.into())
29006                        }
29007                        BreakpointPromptEditAction::Condition => {
29008                            BreakpointEditAction::EditCondition(message.into())
29009                        }
29010                        BreakpointPromptEditAction::HitCondition => {
29011                            BreakpointEditAction::EditHitCondition(message.into())
29012                        }
29013                    },
29014                    cx,
29015                );
29016
29017                editor.remove_blocks(self.block_ids.clone(), None, cx);
29018                cx.focus_self(window);
29019            });
29020        }
29021    }
29022
29023    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29024        self.editor
29025            .update(cx, |editor, cx| {
29026                editor.remove_blocks(self.block_ids.clone(), None, cx);
29027                window.focus(&editor.focus_handle, cx);
29028            })
29029            .log_err();
29030    }
29031
29032    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29033        let settings = ThemeSettings::get_global(cx);
29034        let text_style = TextStyle {
29035            color: if self.prompt.read(cx).read_only(cx) {
29036                cx.theme().colors().text_disabled
29037            } else {
29038                cx.theme().colors().text
29039            },
29040            font_family: settings.buffer_font.family.clone(),
29041            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29042            font_size: settings.buffer_font_size(cx).into(),
29043            font_weight: settings.buffer_font.weight,
29044            line_height: relative(settings.buffer_line_height.value()),
29045            ..Default::default()
29046        };
29047        EditorElement::new(
29048            &self.prompt,
29049            EditorStyle {
29050                background: cx.theme().colors().editor_background,
29051                local_player: cx.theme().players().local(),
29052                text: text_style,
29053                ..Default::default()
29054            },
29055        )
29056    }
29057
29058    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29059        let focus_handle = self.prompt.focus_handle(cx);
29060        IconButton::new("cancel", IconName::Close)
29061            .icon_color(Color::Muted)
29062            .shape(IconButtonShape::Square)
29063            .tooltip(move |_window, cx| {
29064                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29065            })
29066            .on_click(cx.listener(|this, _, window, cx| {
29067                this.cancel(&menu::Cancel, window, cx);
29068            }))
29069    }
29070
29071    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29072        let focus_handle = self.prompt.focus_handle(cx);
29073        IconButton::new("confirm", IconName::Return)
29074            .icon_color(Color::Muted)
29075            .shape(IconButtonShape::Square)
29076            .tooltip(move |_window, cx| {
29077                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29078            })
29079            .on_click(cx.listener(|this, _, window, cx| {
29080                this.confirm(&menu::Confirm, window, cx);
29081            }))
29082    }
29083}
29084
29085impl Render for BreakpointPromptEditor {
29086    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29087        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29088        let editor_margins = *self.editor_margins.lock();
29089        let gutter_dimensions = editor_margins.gutter;
29090        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29091        let right_padding = editor_margins.right + px(9.);
29092        h_flex()
29093            .key_context("Editor")
29094            .bg(cx.theme().colors().editor_background)
29095            .border_y_1()
29096            .border_color(cx.theme().status().info_border)
29097            .size_full()
29098            .py(window.line_height() / 2.5)
29099            .pr(right_padding)
29100            .on_action(cx.listener(Self::confirm))
29101            .on_action(cx.listener(Self::cancel))
29102            .child(
29103                WithRemSize::new(ui_font_size)
29104                    .h_full()
29105                    .w(left_gutter_width)
29106                    .flex()
29107                    .flex_row()
29108                    .flex_shrink_0()
29109                    .items_center()
29110                    .justify_center()
29111                    .gap_1()
29112                    .child(self.render_close_button(cx)),
29113            )
29114            .child(
29115                h_flex()
29116                    .w_full()
29117                    .justify_between()
29118                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29119                    .child(
29120                        WithRemSize::new(ui_font_size)
29121                            .flex()
29122                            .flex_row()
29123                            .items_center()
29124                            .child(self.render_confirm_button(cx)),
29125                    ),
29126            )
29127    }
29128}
29129
29130impl Focusable for BreakpointPromptEditor {
29131    fn focus_handle(&self, cx: &App) -> FocusHandle {
29132        self.prompt.focus_handle(cx)
29133    }
29134}
29135
29136fn all_edits_insertions_or_deletions(
29137    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29138    snapshot: &MultiBufferSnapshot,
29139) -> bool {
29140    let mut all_insertions = true;
29141    let mut all_deletions = true;
29142
29143    for (range, new_text) in edits.iter() {
29144        let range_is_empty = range.to_offset(snapshot).is_empty();
29145        let text_is_empty = new_text.is_empty();
29146
29147        if range_is_empty != text_is_empty {
29148            if range_is_empty {
29149                all_deletions = false;
29150            } else {
29151                all_insertions = false;
29152            }
29153        } else {
29154            return false;
29155        }
29156
29157        if !all_insertions && !all_deletions {
29158            return false;
29159        }
29160    }
29161    all_insertions || all_deletions
29162}
29163
29164struct MissingEditPredictionKeybindingTooltip;
29165
29166impl Render for MissingEditPredictionKeybindingTooltip {
29167    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29168        ui::tooltip_container(cx, |container, cx| {
29169            container
29170                .flex_shrink_0()
29171                .max_w_80()
29172                .min_h(rems_from_px(124.))
29173                .justify_between()
29174                .child(
29175                    v_flex()
29176                        .flex_1()
29177                        .text_ui_sm(cx)
29178                        .child(Label::new("Conflict with Accept Keybinding"))
29179                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29180                )
29181                .child(
29182                    h_flex()
29183                        .pb_1()
29184                        .gap_1()
29185                        .items_end()
29186                        .w_full()
29187                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29188                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29189                        }))
29190                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29191                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29192                        })),
29193                )
29194        })
29195    }
29196}
29197
29198#[derive(Debug, Clone, Copy, PartialEq)]
29199pub struct LineHighlight {
29200    pub background: Background,
29201    pub border: Option<gpui::Hsla>,
29202    pub include_gutter: bool,
29203    pub type_id: Option<TypeId>,
29204}
29205
29206struct LineManipulationResult {
29207    pub new_text: String,
29208    pub line_count_before: usize,
29209    pub line_count_after: usize,
29210}
29211
29212fn render_diff_hunk_controls(
29213    row: u32,
29214    status: &DiffHunkStatus,
29215    hunk_range: Range<Anchor>,
29216    is_created_file: bool,
29217    line_height: Pixels,
29218    editor: &Entity<Editor>,
29219    _window: &mut Window,
29220    cx: &mut App,
29221) -> AnyElement {
29222    h_flex()
29223        .h(line_height)
29224        .mr_1()
29225        .gap_1()
29226        .px_0p5()
29227        .pb_1()
29228        .border_x_1()
29229        .border_b_1()
29230        .border_color(cx.theme().colors().border_variant)
29231        .rounded_b_lg()
29232        .bg(cx.theme().colors().editor_background)
29233        .gap_1()
29234        .block_mouse_except_scroll()
29235        .shadow_md()
29236        .child(if status.has_secondary_hunk() {
29237            Button::new(("stage", row as u64), "Stage")
29238                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29239                .tooltip({
29240                    let focus_handle = editor.focus_handle(cx);
29241                    move |_window, cx| {
29242                        Tooltip::for_action_in(
29243                            "Stage Hunk",
29244                            &::git::ToggleStaged,
29245                            &focus_handle,
29246                            cx,
29247                        )
29248                    }
29249                })
29250                .on_click({
29251                    let editor = editor.clone();
29252                    move |_event, _window, cx| {
29253                        editor.update(cx, |editor, cx| {
29254                            editor.stage_or_unstage_diff_hunks(
29255                                true,
29256                                vec![hunk_range.start..hunk_range.start],
29257                                cx,
29258                            );
29259                        });
29260                    }
29261                })
29262        } else {
29263            Button::new(("unstage", row as u64), "Unstage")
29264                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29265                .tooltip({
29266                    let focus_handle = editor.focus_handle(cx);
29267                    move |_window, cx| {
29268                        Tooltip::for_action_in(
29269                            "Unstage Hunk",
29270                            &::git::ToggleStaged,
29271                            &focus_handle,
29272                            cx,
29273                        )
29274                    }
29275                })
29276                .on_click({
29277                    let editor = editor.clone();
29278                    move |_event, _window, cx| {
29279                        editor.update(cx, |editor, cx| {
29280                            editor.stage_or_unstage_diff_hunks(
29281                                false,
29282                                vec![hunk_range.start..hunk_range.start],
29283                                cx,
29284                            );
29285                        });
29286                    }
29287                })
29288        })
29289        .child(
29290            Button::new(("restore", row as u64), "Restore")
29291                .tooltip({
29292                    let focus_handle = editor.focus_handle(cx);
29293                    move |_window, cx| {
29294                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29295                    }
29296                })
29297                .on_click({
29298                    let editor = editor.clone();
29299                    move |_event, window, cx| {
29300                        editor.update(cx, |editor, cx| {
29301                            let snapshot = editor.snapshot(window, cx);
29302                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29303                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29304                        });
29305                    }
29306                })
29307                .disabled(is_created_file),
29308        )
29309        .when(
29310            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29311            |el| {
29312                el.child(
29313                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29314                        .shape(IconButtonShape::Square)
29315                        .icon_size(IconSize::Small)
29316                        // .disabled(!has_multiple_hunks)
29317                        .tooltip({
29318                            let focus_handle = editor.focus_handle(cx);
29319                            move |_window, cx| {
29320                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29321                            }
29322                        })
29323                        .on_click({
29324                            let editor = editor.clone();
29325                            move |_event, window, cx| {
29326                                editor.update(cx, |editor, cx| {
29327                                    let snapshot = editor.snapshot(window, cx);
29328                                    let position =
29329                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29330                                    editor.go_to_hunk_before_or_after_position(
29331                                        &snapshot,
29332                                        position,
29333                                        Direction::Next,
29334                                        true,
29335                                        window,
29336                                        cx,
29337                                    );
29338                                    editor.expand_selected_diff_hunks(cx);
29339                                });
29340                            }
29341                        }),
29342                )
29343                .child(
29344                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29345                        .shape(IconButtonShape::Square)
29346                        .icon_size(IconSize::Small)
29347                        // .disabled(!has_multiple_hunks)
29348                        .tooltip({
29349                            let focus_handle = editor.focus_handle(cx);
29350                            move |_window, cx| {
29351                                Tooltip::for_action_in(
29352                                    "Previous Hunk",
29353                                    &GoToPreviousHunk,
29354                                    &focus_handle,
29355                                    cx,
29356                                )
29357                            }
29358                        })
29359                        .on_click({
29360                            let editor = editor.clone();
29361                            move |_event, window, cx| {
29362                                editor.update(cx, |editor, cx| {
29363                                    let snapshot = editor.snapshot(window, cx);
29364                                    let point =
29365                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29366                                    editor.go_to_hunk_before_or_after_position(
29367                                        &snapshot,
29368                                        point,
29369                                        Direction::Prev,
29370                                        true,
29371                                        window,
29372                                        cx,
29373                                    );
29374                                    editor.expand_selected_diff_hunks(cx);
29375                                });
29376                            }
29377                        }),
29378                )
29379            },
29380        )
29381        .into_any_element()
29382}
29383
29384pub fn multibuffer_context_lines(cx: &App) -> u32 {
29385    EditorSettings::try_get(cx)
29386        .map(|settings| settings.excerpt_context_lines)
29387        .unwrap_or(2)
29388        .min(32)
29389}