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 rust_analyzer_ext;
   39pub mod scroll;
   40mod selections_collection;
   41pub mod semantic_tokens;
   42mod split;
   43pub mod split_editor_view;
   44pub mod tasks;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  137    TreeSitterOptions, WordsQuery,
  138    language_settings::{
  139        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  140        all_language_settings, language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::DB;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  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::{ResolvedTask, RunnableTag, TaskTemplate, 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};
  213use ui_input::ErasedEditor;
  214use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  215use workspace::{
  216    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  217    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  218    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  219    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  220    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  221    searchable::SearchEvent,
  222};
  223use zed_actions::editor::{MoveDown, MoveUp};
  224
  225use crate::{
  226    code_context_menus::CompletionsMenuSource,
  227    editor_settings::MultiCursorModifier,
  228    hover_links::{find_url, find_url_from_range},
  229    inlays::{
  230        InlineValueCache,
  231        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  232    },
  233    scroll::{ScrollOffset, ScrollPixelOffset},
  234    selections_collection::resolve_selections_wrapping_blocks,
  235    semantic_tokens::SemanticTokenState,
  236    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  237};
  238
  239pub const FILE_HEADER_HEIGHT: u32 = 2;
  240pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  241pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  242const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  243const MAX_LINE_LEN: usize = 1024;
  244const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  245const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  246pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  247#[doc(hidden)]
  248pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  249pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  250
  251pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  252pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  253pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  254pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  255
  256pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  257pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  258pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  259
  260pub type RenderDiffHunkControlsFn = Arc<
  261    dyn Fn(
  262        u32,
  263        &DiffHunkStatus,
  264        Range<Anchor>,
  265        bool,
  266        Pixels,
  267        &Entity<Editor>,
  268        &mut Window,
  269        &mut App,
  270    ) -> AnyElement,
  271>;
  272
  273enum ReportEditorEvent {
  274    Saved { auto_saved: bool },
  275    EditorOpened,
  276    Closed,
  277}
  278
  279impl ReportEditorEvent {
  280    pub fn event_type(&self) -> &'static str {
  281        match self {
  282            Self::Saved { .. } => "Editor Saved",
  283            Self::EditorOpened => "Editor Opened",
  284            Self::Closed => "Editor Closed",
  285        }
  286    }
  287}
  288
  289pub enum ActiveDebugLine {}
  290pub enum DebugStackFrameLine {}
  291
  292pub enum ConflictsOuter {}
  293pub enum ConflictsOurs {}
  294pub enum ConflictsTheirs {}
  295pub enum ConflictsOursMarker {}
  296pub enum ConflictsTheirsMarker {}
  297
  298pub struct HunkAddedColor;
  299pub struct HunkRemovedColor;
  300
  301#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  302pub enum Navigated {
  303    Yes,
  304    No,
  305}
  306
  307impl Navigated {
  308    pub fn from_bool(yes: bool) -> Navigated {
  309        if yes { Navigated::Yes } else { Navigated::No }
  310    }
  311}
  312
  313#[derive(Debug, Clone, PartialEq, Eq)]
  314enum DisplayDiffHunk {
  315    Folded {
  316        display_row: DisplayRow,
  317    },
  318    Unfolded {
  319        is_created_file: bool,
  320        diff_base_byte_range: Range<usize>,
  321        display_row_range: Range<DisplayRow>,
  322        multi_buffer_range: Range<Anchor>,
  323        status: DiffHunkStatus,
  324        word_diffs: Vec<Range<MultiBufferOffset>>,
  325    },
  326}
  327
  328pub enum HideMouseCursorOrigin {
  329    TypingAction,
  330    MovementAction,
  331}
  332
  333pub fn init(cx: &mut App) {
  334    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  335    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  336
  337    workspace::register_project_item::<Editor>(cx);
  338    workspace::FollowableViewRegistry::register::<Editor>(cx);
  339    workspace::register_serializable_item::<Editor>(cx);
  340
  341    cx.observe_new(
  342        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  343            workspace.register_action(Editor::new_file);
  344            workspace.register_action(Editor::new_file_split);
  345            workspace.register_action(Editor::new_file_vertical);
  346            workspace.register_action(Editor::new_file_horizontal);
  347            workspace.register_action(Editor::cancel_language_server_work);
  348            workspace.register_action(Editor::toggle_focus);
  349        },
  350    )
  351    .detach();
  352
  353    cx.on_action(move |_: &workspace::NewFile, cx| {
  354        let app_state = workspace::AppState::global(cx);
  355        if let Some(app_state) = app_state.upgrade() {
  356            workspace::open_new(
  357                Default::default(),
  358                app_state,
  359                cx,
  360                |workspace, window, cx| {
  361                    Editor::new_file(workspace, &Default::default(), window, cx)
  362                },
  363            )
  364            .detach_and_log_err(cx);
  365        }
  366    })
  367    .on_action(move |_: &workspace::NewWindow, cx| {
  368        let app_state = workspace::AppState::global(cx);
  369        if let Some(app_state) = app_state.upgrade() {
  370            workspace::open_new(
  371                Default::default(),
  372                app_state,
  373                cx,
  374                |workspace, window, cx| {
  375                    cx.activate(true);
  376                    Editor::new_file(workspace, &Default::default(), window, cx)
  377                },
  378            )
  379            .detach_and_log_err(cx);
  380        }
  381    });
  382    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  383        Arc::new(ErasedEditorImpl(
  384            cx.new(|cx| Editor::single_line(window, cx)),
  385        )) as Arc<dyn ErasedEditor>
  386    });
  387    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  388}
  389
  390pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  391    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  392}
  393
  394pub trait DiagnosticRenderer {
  395    fn render_group(
  396        &self,
  397        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  398        buffer_id: BufferId,
  399        snapshot: EditorSnapshot,
  400        editor: WeakEntity<Editor>,
  401        language_registry: Option<Arc<LanguageRegistry>>,
  402        cx: &mut App,
  403    ) -> Vec<BlockProperties<Anchor>>;
  404
  405    fn render_hover(
  406        &self,
  407        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  408        range: Range<Point>,
  409        buffer_id: BufferId,
  410        language_registry: Option<Arc<LanguageRegistry>>,
  411        cx: &mut App,
  412    ) -> Option<Entity<markdown::Markdown>>;
  413
  414    fn open_link(
  415        &self,
  416        editor: &mut Editor,
  417        link: SharedString,
  418        window: &mut Window,
  419        cx: &mut Context<Editor>,
  420    );
  421}
  422
  423pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  424
  425impl GlobalDiagnosticRenderer {
  426    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  427        cx.try_global::<Self>().map(|g| g.0.clone())
  428    }
  429}
  430
  431impl gpui::Global for GlobalDiagnosticRenderer {}
  432pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  433    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  434}
  435
  436pub struct SearchWithinRange;
  437
  438trait InvalidationRegion {
  439    fn ranges(&self) -> &[Range<Anchor>];
  440}
  441
  442#[derive(Clone, Debug, PartialEq)]
  443pub enum SelectPhase {
  444    Begin {
  445        position: DisplayPoint,
  446        add: bool,
  447        click_count: usize,
  448    },
  449    BeginColumnar {
  450        position: DisplayPoint,
  451        reset: bool,
  452        mode: ColumnarMode,
  453        goal_column: u32,
  454    },
  455    Extend {
  456        position: DisplayPoint,
  457        click_count: usize,
  458    },
  459    Update {
  460        position: DisplayPoint,
  461        goal_column: u32,
  462        scroll_delta: gpui::Point<f32>,
  463    },
  464    End,
  465}
  466
  467#[derive(Clone, Debug, PartialEq)]
  468pub enum ColumnarMode {
  469    FromMouse,
  470    FromSelection,
  471}
  472
  473#[derive(Clone, Debug)]
  474pub enum SelectMode {
  475    Character,
  476    Word(Range<Anchor>),
  477    Line(Range<Anchor>),
  478    All,
  479}
  480
  481#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  482pub enum SizingBehavior {
  483    /// The editor will layout itself using `size_full` and will include the vertical
  484    /// scroll margin as requested by user settings.
  485    #[default]
  486    Default,
  487    /// The editor will layout itself using `size_full`, but will not have any
  488    /// vertical overscroll.
  489    ExcludeOverscrollMargin,
  490    /// The editor will request a vertical size according to its content and will be
  491    /// layouted without a vertical scroll margin.
  492    SizeByContent,
  493}
  494
  495#[derive(Clone, PartialEq, Eq, Debug)]
  496pub enum EditorMode {
  497    SingleLine,
  498    AutoHeight {
  499        min_lines: usize,
  500        max_lines: Option<usize>,
  501    },
  502    Full {
  503        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  504        scale_ui_elements_with_buffer_font_size: bool,
  505        /// When set to `true`, the editor will render a background for the active line.
  506        show_active_line_background: bool,
  507        /// Determines the sizing behavior for this editor
  508        sizing_behavior: SizingBehavior,
  509    },
  510    Minimap {
  511        parent: WeakEntity<Editor>,
  512    },
  513}
  514
  515impl EditorMode {
  516    pub fn full() -> Self {
  517        Self::Full {
  518            scale_ui_elements_with_buffer_font_size: true,
  519            show_active_line_background: true,
  520            sizing_behavior: SizingBehavior::Default,
  521        }
  522    }
  523
  524    #[inline]
  525    pub fn is_full(&self) -> bool {
  526        matches!(self, Self::Full { .. })
  527    }
  528
  529    #[inline]
  530    pub fn is_single_line(&self) -> bool {
  531        matches!(self, Self::SingleLine { .. })
  532    }
  533
  534    #[inline]
  535    fn is_minimap(&self) -> bool {
  536        matches!(self, Self::Minimap { .. })
  537    }
  538}
  539
  540#[derive(Copy, Clone, Debug)]
  541pub enum SoftWrap {
  542    /// Prefer not to wrap at all.
  543    ///
  544    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  545    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  546    GitDiff,
  547    /// Prefer a single line generally, unless an overly long line is encountered.
  548    None,
  549    /// Soft wrap lines that exceed the editor width.
  550    EditorWidth,
  551    /// Soft wrap lines at the preferred line length.
  552    Column(u32),
  553    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  554    Bounded(u32),
  555}
  556
  557#[derive(Clone)]
  558pub struct EditorStyle {
  559    pub background: Hsla,
  560    pub border: Hsla,
  561    pub local_player: PlayerColor,
  562    pub text: TextStyle,
  563    pub scrollbar_width: Pixels,
  564    pub syntax: Arc<SyntaxTheme>,
  565    pub status: StatusColors,
  566    pub inlay_hints_style: HighlightStyle,
  567    pub edit_prediction_styles: EditPredictionStyles,
  568    pub unnecessary_code_fade: f32,
  569    pub show_underlines: bool,
  570}
  571
  572impl Default for EditorStyle {
  573    fn default() -> Self {
  574        Self {
  575            background: Hsla::default(),
  576            border: Hsla::default(),
  577            local_player: PlayerColor::default(),
  578            text: TextStyle::default(),
  579            scrollbar_width: Pixels::default(),
  580            syntax: Default::default(),
  581            // HACK: Status colors don't have a real default.
  582            // We should look into removing the status colors from the editor
  583            // style and retrieve them directly from the theme.
  584            status: StatusColors::dark(),
  585            inlay_hints_style: HighlightStyle::default(),
  586            edit_prediction_styles: EditPredictionStyles {
  587                insertion: HighlightStyle::default(),
  588                whitespace: HighlightStyle::default(),
  589            },
  590            unnecessary_code_fade: Default::default(),
  591            show_underlines: true,
  592        }
  593    }
  594}
  595
  596pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  597    let show_background = language_settings::language_settings(None, None, cx)
  598        .inlay_hints
  599        .show_background;
  600
  601    let mut style = cx.theme().syntax().get("hint");
  602
  603    if style.color.is_none() {
  604        style.color = Some(cx.theme().status().hint);
  605    }
  606
  607    if !show_background {
  608        style.background_color = None;
  609        return style;
  610    }
  611
  612    if style.background_color.is_none() {
  613        style.background_color = Some(cx.theme().status().hint_background);
  614    }
  615
  616    style
  617}
  618
  619pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  620    EditPredictionStyles {
  621        insertion: HighlightStyle {
  622            color: Some(cx.theme().status().predictive),
  623            ..HighlightStyle::default()
  624        },
  625        whitespace: HighlightStyle {
  626            background_color: Some(cx.theme().status().created_background),
  627            ..HighlightStyle::default()
  628        },
  629    }
  630}
  631
  632type CompletionId = usize;
  633
  634pub(crate) enum EditDisplayMode {
  635    TabAccept,
  636    DiffPopover,
  637    Inline,
  638}
  639
  640enum EditPrediction {
  641    Edit {
  642        edits: Vec<(Range<Anchor>, Arc<str>)>,
  643        /// Predicted cursor position as (anchor, offset_from_anchor).
  644        /// The anchor is in multibuffer coordinates; after applying edits,
  645        /// resolve the anchor and add the offset to get the final cursor position.
  646        cursor_position: Option<(Anchor, usize)>,
  647        edit_preview: Option<EditPreview>,
  648        display_mode: EditDisplayMode,
  649        snapshot: BufferSnapshot,
  650    },
  651    /// Move to a specific location in the active editor
  652    MoveWithin {
  653        target: Anchor,
  654        snapshot: BufferSnapshot,
  655    },
  656    /// Move to a specific location in a different editor (not the active one)
  657    MoveOutside {
  658        target: language::Anchor,
  659        snapshot: BufferSnapshot,
  660    },
  661}
  662
  663struct EditPredictionState {
  664    inlay_ids: Vec<InlayId>,
  665    completion: EditPrediction,
  666    completion_id: Option<SharedString>,
  667    invalidation_range: Option<Range<Anchor>>,
  668}
  669
  670enum EditPredictionSettings {
  671    Disabled,
  672    Enabled {
  673        show_in_menu: bool,
  674        preview_requires_modifier: bool,
  675    },
  676}
  677
  678#[derive(Debug, Clone)]
  679struct InlineDiagnostic {
  680    message: SharedString,
  681    group_id: usize,
  682    is_primary: bool,
  683    start: Point,
  684    severity: lsp::DiagnosticSeverity,
  685}
  686
  687pub enum MenuEditPredictionsPolicy {
  688    Never,
  689    ByProvider,
  690}
  691
  692pub enum EditPredictionPreview {
  693    /// Modifier is not pressed
  694    Inactive { released_too_fast: bool },
  695    /// Modifier pressed
  696    Active {
  697        since: Instant,
  698        previous_scroll_position: Option<SharedScrollAnchor>,
  699    },
  700}
  701
  702impl EditPredictionPreview {
  703    pub fn released_too_fast(&self) -> bool {
  704        match self {
  705            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  706            EditPredictionPreview::Active { .. } => false,
  707        }
  708    }
  709
  710    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  711        if let EditPredictionPreview::Active {
  712            previous_scroll_position,
  713            ..
  714        } = self
  715        {
  716            *previous_scroll_position = scroll_position;
  717        }
  718    }
  719}
  720
  721pub struct ContextMenuOptions {
  722    pub min_entries_visible: usize,
  723    pub max_entries_visible: usize,
  724    pub placement: Option<ContextMenuPlacement>,
  725}
  726
  727#[derive(Debug, Clone, PartialEq, Eq)]
  728pub enum ContextMenuPlacement {
  729    Above,
  730    Below,
  731}
  732
  733#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  734struct EditorActionId(usize);
  735
  736impl EditorActionId {
  737    pub fn post_inc(&mut self) -> Self {
  738        let answer = self.0;
  739
  740        *self = Self(answer + 1);
  741
  742        Self(answer)
  743    }
  744}
  745
  746// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  747// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  748
  749type BackgroundHighlight = (
  750    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  751    Arc<[Range<Anchor>]>,
  752);
  753type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  754
  755#[derive(Default)]
  756struct ScrollbarMarkerState {
  757    scrollbar_size: Size<Pixels>,
  758    dirty: bool,
  759    markers: Arc<[PaintQuad]>,
  760    pending_refresh: Option<Task<Result<()>>>,
  761}
  762
  763impl ScrollbarMarkerState {
  764    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  765        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  766    }
  767}
  768
  769#[derive(Clone, Copy, PartialEq, Eq)]
  770pub enum MinimapVisibility {
  771    Disabled,
  772    Enabled {
  773        /// The configuration currently present in the users settings.
  774        setting_configuration: bool,
  775        /// Whether to override the currently set visibility from the users setting.
  776        toggle_override: bool,
  777    },
  778}
  779
  780impl MinimapVisibility {
  781    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  782        if mode.is_full() {
  783            Self::Enabled {
  784                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  785                toggle_override: false,
  786            }
  787        } else {
  788            Self::Disabled
  789        }
  790    }
  791
  792    fn hidden(&self) -> Self {
  793        match *self {
  794            Self::Enabled {
  795                setting_configuration,
  796                ..
  797            } => Self::Enabled {
  798                setting_configuration,
  799                toggle_override: setting_configuration,
  800            },
  801            Self::Disabled => Self::Disabled,
  802        }
  803    }
  804
  805    fn disabled(&self) -> bool {
  806        matches!(*self, Self::Disabled)
  807    }
  808
  809    fn settings_visibility(&self) -> bool {
  810        match *self {
  811            Self::Enabled {
  812                setting_configuration,
  813                ..
  814            } => setting_configuration,
  815            _ => false,
  816        }
  817    }
  818
  819    fn visible(&self) -> bool {
  820        match *self {
  821            Self::Enabled {
  822                setting_configuration,
  823                toggle_override,
  824            } => setting_configuration ^ toggle_override,
  825            _ => false,
  826        }
  827    }
  828
  829    fn toggle_visibility(&self) -> Self {
  830        match *self {
  831            Self::Enabled {
  832                toggle_override,
  833                setting_configuration,
  834            } => Self::Enabled {
  835                setting_configuration,
  836                toggle_override: !toggle_override,
  837            },
  838            Self::Disabled => Self::Disabled,
  839        }
  840    }
  841}
  842
  843#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  844pub enum BufferSerialization {
  845    All,
  846    NonDirtyBuffers,
  847}
  848
  849impl BufferSerialization {
  850    fn new(restore_unsaved_buffers: bool) -> Self {
  851        if restore_unsaved_buffers {
  852            Self::All
  853        } else {
  854            Self::NonDirtyBuffers
  855        }
  856    }
  857}
  858
  859#[derive(Clone, Debug)]
  860struct RunnableTasks {
  861    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  862    offset: multi_buffer::Anchor,
  863    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  864    column: u32,
  865    // Values of all named captures, including those starting with '_'
  866    extra_variables: HashMap<String, String>,
  867    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  868    context_range: Range<BufferOffset>,
  869}
  870
  871impl RunnableTasks {
  872    fn resolve<'a>(
  873        &'a self,
  874        cx: &'a task::TaskContext,
  875    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  876        self.templates.iter().filter_map(|(kind, template)| {
  877            template
  878                .resolve_task(&kind.to_id_base(), cx)
  879                .map(|task| (kind.clone(), task))
  880        })
  881    }
  882}
  883
  884#[derive(Clone)]
  885pub struct ResolvedTasks {
  886    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  887    position: Anchor,
  888}
  889
  890/// Addons allow storing per-editor state in other crates (e.g. Vim)
  891pub trait Addon: 'static {
  892    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  893
  894    fn render_buffer_header_controls(
  895        &self,
  896        _: &ExcerptInfo,
  897        _: &Window,
  898        _: &App,
  899    ) -> Option<AnyElement> {
  900        None
  901    }
  902
  903    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  904        None
  905    }
  906
  907    fn to_any(&self) -> &dyn std::any::Any;
  908
  909    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  910        None
  911    }
  912}
  913
  914struct ChangeLocation {
  915    current: Option<Vec<Anchor>>,
  916    original: Vec<Anchor>,
  917}
  918impl ChangeLocation {
  919    fn locations(&self) -> &[Anchor] {
  920        self.current.as_ref().unwrap_or(&self.original)
  921    }
  922}
  923
  924/// A set of caret positions, registered when the editor was edited.
  925pub struct ChangeList {
  926    changes: Vec<ChangeLocation>,
  927    /// Currently "selected" change.
  928    position: Option<usize>,
  929}
  930
  931impl ChangeList {
  932    pub fn new() -> Self {
  933        Self {
  934            changes: Vec::new(),
  935            position: None,
  936        }
  937    }
  938
  939    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  940    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  941    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  942        if self.changes.is_empty() {
  943            return None;
  944        }
  945
  946        let prev = self.position.unwrap_or(self.changes.len());
  947        let next = if direction == Direction::Prev {
  948            prev.saturating_sub(count)
  949        } else {
  950            (prev + count).min(self.changes.len() - 1)
  951        };
  952        self.position = Some(next);
  953        self.changes.get(next).map(|change| change.locations())
  954    }
  955
  956    /// Adds a new change to the list, resetting the change list position.
  957    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  958        self.position.take();
  959        if let Some(last) = self.changes.last_mut()
  960            && group
  961        {
  962            last.current = Some(new_positions)
  963        } else {
  964            self.changes.push(ChangeLocation {
  965                original: new_positions,
  966                current: None,
  967            });
  968        }
  969    }
  970
  971    pub fn last(&self) -> Option<&[Anchor]> {
  972        self.changes.last().map(|change| change.locations())
  973    }
  974
  975    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  976        self.changes.last().map(|change| change.original.as_slice())
  977    }
  978
  979    pub fn invert_last_group(&mut self) {
  980        if let Some(last) = self.changes.last_mut()
  981            && let Some(current) = last.current.as_mut()
  982        {
  983            mem::swap(&mut last.original, current);
  984        }
  985    }
  986}
  987
  988#[derive(Clone)]
  989struct InlineBlamePopoverState {
  990    scroll_handle: ScrollHandle,
  991    commit_message: Option<ParsedCommitMessage>,
  992    markdown: Entity<Markdown>,
  993}
  994
  995struct InlineBlamePopover {
  996    position: gpui::Point<Pixels>,
  997    hide_task: Option<Task<()>>,
  998    popover_bounds: Option<Bounds<Pixels>>,
  999    popover_state: InlineBlamePopoverState,
 1000    keyboard_grace: bool,
 1001}
 1002
 1003enum SelectionDragState {
 1004    /// State when no drag related activity is detected.
 1005    None,
 1006    /// State when the mouse is down on a selection that is about to be dragged.
 1007    ReadyToDrag {
 1008        selection: Selection<Anchor>,
 1009        click_position: gpui::Point<Pixels>,
 1010        mouse_down_time: Instant,
 1011    },
 1012    /// State when the mouse is dragging the selection in the editor.
 1013    Dragging {
 1014        selection: Selection<Anchor>,
 1015        drop_cursor: Selection<Anchor>,
 1016        hide_drop_cursor: bool,
 1017    },
 1018}
 1019
 1020enum ColumnarSelectionState {
 1021    FromMouse {
 1022        selection_tail: Anchor,
 1023        display_point: Option<DisplayPoint>,
 1024    },
 1025    FromSelection {
 1026        selection_tail: Anchor,
 1027    },
 1028}
 1029
 1030/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1031/// a breakpoint on them.
 1032#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1033struct PhantomBreakpointIndicator {
 1034    display_row: DisplayRow,
 1035    /// There's a small debounce between hovering over the line and showing the indicator.
 1036    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1037    is_active: bool,
 1038    collides_with_existing_breakpoint: bool,
 1039}
 1040
 1041/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1042/// in diff view mode.
 1043#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1044pub(crate) struct PhantomDiffReviewIndicator {
 1045    /// The starting anchor of the selection (or the only row if not dragging).
 1046    pub start: Anchor,
 1047    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1048    pub end: Anchor,
 1049    /// There's a small debounce between hovering over the line and showing the indicator.
 1050    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1051    pub is_active: bool,
 1052}
 1053
 1054#[derive(Clone, Debug)]
 1055pub(crate) struct DiffReviewDragState {
 1056    pub start_anchor: Anchor,
 1057    pub current_anchor: Anchor,
 1058}
 1059
 1060impl DiffReviewDragState {
 1061    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1062        let start = self.start_anchor.to_display_point(snapshot).row();
 1063        let current = self.current_anchor.to_display_point(snapshot).row();
 1064
 1065        (start..=current).sorted()
 1066    }
 1067}
 1068
 1069/// Identifies a specific hunk in the diff buffer.
 1070/// Used as a key to group comments by their location.
 1071#[derive(Clone, Debug)]
 1072pub struct DiffHunkKey {
 1073    /// The file path (relative to worktree) this hunk belongs to.
 1074    pub file_path: Arc<util::rel_path::RelPath>,
 1075    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1076    pub hunk_start_anchor: Anchor,
 1077}
 1078
 1079/// A review comment stored locally before being sent to the Agent panel.
 1080#[derive(Clone)]
 1081pub struct StoredReviewComment {
 1082    /// Unique identifier for this comment (for edit/delete operations).
 1083    pub id: usize,
 1084    /// The comment text entered by the user.
 1085    pub comment: String,
 1086    /// Anchors for the code range being reviewed.
 1087    pub range: Range<Anchor>,
 1088    /// Timestamp when the comment was created (for chronological ordering).
 1089    pub created_at: Instant,
 1090    /// Whether this comment is currently being edited inline.
 1091    pub is_editing: bool,
 1092}
 1093
 1094impl StoredReviewComment {
 1095    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1096        Self {
 1097            id,
 1098            comment,
 1099            range: anchor_range,
 1100            created_at: Instant::now(),
 1101            is_editing: false,
 1102        }
 1103    }
 1104}
 1105
 1106/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1107pub(crate) struct DiffReviewOverlay {
 1108    pub anchor_range: Range<Anchor>,
 1109    /// The block ID for the overlay.
 1110    pub block_id: CustomBlockId,
 1111    /// The editor entity for the review input.
 1112    pub prompt_editor: Entity<Editor>,
 1113    /// The hunk key this overlay belongs to.
 1114    pub hunk_key: DiffHunkKey,
 1115    /// Whether the comments section is expanded.
 1116    pub comments_expanded: bool,
 1117    /// Editors for comments currently being edited inline.
 1118    /// Key: comment ID, Value: Editor entity for inline editing.
 1119    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1120    /// Subscriptions for inline edit editors' action handlers.
 1121    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1122    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1123    /// The current user's avatar URI for display in comment rows.
 1124    pub user_avatar_uri: Option<SharedUri>,
 1125    /// Subscription to keep the action handler alive.
 1126    _subscription: Subscription,
 1127}
 1128
 1129/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1130///
 1131/// See the [module level documentation](self) for more information.
 1132pub struct Editor {
 1133    focus_handle: FocusHandle,
 1134    last_focused_descendant: Option<WeakFocusHandle>,
 1135    /// The text buffer being edited
 1136    buffer: Entity<MultiBuffer>,
 1137    /// Map of how text in the buffer should be displayed.
 1138    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1139    pub display_map: Entity<DisplayMap>,
 1140    placeholder_display_map: Option<Entity<DisplayMap>>,
 1141    pub selections: SelectionsCollection,
 1142    pub scroll_manager: ScrollManager,
 1143    /// When inline assist editors are linked, they all render cursors because
 1144    /// typing enters text into each of them, even the ones that aren't focused.
 1145    pub(crate) show_cursor_when_unfocused: bool,
 1146    columnar_selection_state: Option<ColumnarSelectionState>,
 1147    add_selections_state: Option<AddSelectionsState>,
 1148    select_next_state: Option<SelectNextState>,
 1149    select_prev_state: Option<SelectNextState>,
 1150    selection_history: SelectionHistory,
 1151    defer_selection_effects: bool,
 1152    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1153    autoclose_regions: Vec<AutocloseRegion>,
 1154    snippet_stack: InvalidationStack<SnippetState>,
 1155    select_syntax_node_history: SelectSyntaxNodeHistory,
 1156    ime_transaction: Option<TransactionId>,
 1157    pub diagnostics_max_severity: DiagnosticSeverity,
 1158    active_diagnostics: ActiveDiagnostic,
 1159    show_inline_diagnostics: bool,
 1160    inline_diagnostics_update: Task<()>,
 1161    inline_diagnostics_enabled: bool,
 1162    diagnostics_enabled: bool,
 1163    word_completions_enabled: bool,
 1164    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1165    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1166    hard_wrap: Option<usize>,
 1167    project: Option<Entity<Project>>,
 1168    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1169    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1170    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1171    blink_manager: Entity<BlinkManager>,
 1172    show_cursor_names: bool,
 1173    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1174    pub show_local_selections: bool,
 1175    mode: EditorMode,
 1176    show_breadcrumbs: bool,
 1177    show_gutter: bool,
 1178    show_scrollbars: ScrollbarAxes,
 1179    minimap_visibility: MinimapVisibility,
 1180    offset_content: bool,
 1181    disable_expand_excerpt_buttons: bool,
 1182    delegate_expand_excerpts: bool,
 1183    delegate_stage_and_restore: bool,
 1184    delegate_open_excerpts: bool,
 1185    enable_lsp_data: bool,
 1186    enable_runnables: bool,
 1187    show_line_numbers: Option<bool>,
 1188    use_relative_line_numbers: Option<bool>,
 1189    show_git_diff_gutter: Option<bool>,
 1190    show_code_actions: Option<bool>,
 1191    show_runnables: Option<bool>,
 1192    show_breakpoints: Option<bool>,
 1193    show_diff_review_button: bool,
 1194    show_wrap_guides: Option<bool>,
 1195    show_indent_guides: Option<bool>,
 1196    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1197    highlight_order: usize,
 1198    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1199    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1200    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1201    scrollbar_marker_state: ScrollbarMarkerState,
 1202    active_indent_guides_state: ActiveIndentGuidesState,
 1203    nav_history: Option<ItemNavHistory>,
 1204    context_menu: RefCell<Option<CodeContextMenu>>,
 1205    context_menu_options: Option<ContextMenuOptions>,
 1206    mouse_context_menu: Option<MouseContextMenu>,
 1207    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1208    inline_blame_popover: Option<InlineBlamePopover>,
 1209    inline_blame_popover_show_task: Option<Task<()>>,
 1210    signature_help_state: SignatureHelpState,
 1211    auto_signature_help: Option<bool>,
 1212    find_all_references_task_sources: Vec<Anchor>,
 1213    next_completion_id: CompletionId,
 1214    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1215    code_actions_task: Option<Task<Result<()>>>,
 1216    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1217    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1218    debounced_selection_highlight_complete: bool,
 1219    document_highlights_task: Option<Task<()>>,
 1220    linked_editing_range_task: Option<Task<Option<()>>>,
 1221    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1222    pending_rename: Option<RenameState>,
 1223    searchable: bool,
 1224    cursor_shape: CursorShape,
 1225    /// Whether the cursor is offset one character to the left when something is
 1226    /// selected (needed for vim visual mode)
 1227    cursor_offset_on_selection: bool,
 1228    current_line_highlight: Option<CurrentLineHighlight>,
 1229    /// Whether to collapse search match ranges to just their start position.
 1230    /// When true, navigating to a match positions the cursor at the match
 1231    /// without selecting the matched text.
 1232    collapse_matches: bool,
 1233    autoindent_mode: Option<AutoindentMode>,
 1234    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1235    input_enabled: bool,
 1236    use_modal_editing: bool,
 1237    read_only: bool,
 1238    leader_id: Option<CollaboratorId>,
 1239    remote_id: Option<ViewId>,
 1240    pub hover_state: HoverState,
 1241    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1242    prev_pressure_stage: Option<PressureStage>,
 1243    gutter_hovered: bool,
 1244    hovered_link_state: Option<HoveredLinkState>,
 1245    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1246    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1247    active_edit_prediction: Option<EditPredictionState>,
 1248    /// Used to prevent flickering as the user types while the menu is open
 1249    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1250    edit_prediction_settings: EditPredictionSettings,
 1251    edit_predictions_hidden_for_vim_mode: bool,
 1252    show_edit_predictions_override: Option<bool>,
 1253    show_completions_on_input_override: Option<bool>,
 1254    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1255    edit_prediction_preview: EditPredictionPreview,
 1256    edit_prediction_indent_conflict: bool,
 1257    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1258    next_inlay_id: usize,
 1259    next_color_inlay_id: usize,
 1260    _subscriptions: Vec<Subscription>,
 1261    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1262    gutter_dimensions: GutterDimensions,
 1263    style: Option<EditorStyle>,
 1264    text_style_refinement: Option<TextStyleRefinement>,
 1265    next_editor_action_id: EditorActionId,
 1266    editor_actions: Rc<
 1267        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1268    >,
 1269    use_autoclose: bool,
 1270    use_auto_surround: bool,
 1271    auto_replace_emoji_shortcode: bool,
 1272    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1273    show_git_blame_gutter: bool,
 1274    show_git_blame_inline: bool,
 1275    show_git_blame_inline_delay_task: Option<Task<()>>,
 1276    git_blame_inline_enabled: bool,
 1277    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1278    buffer_serialization: Option<BufferSerialization>,
 1279    show_selection_menu: Option<bool>,
 1280    blame: Option<Entity<GitBlame>>,
 1281    blame_subscription: Option<Subscription>,
 1282    custom_context_menu: Option<
 1283        Box<
 1284            dyn 'static
 1285                + Fn(
 1286                    &mut Self,
 1287                    DisplayPoint,
 1288                    &mut Window,
 1289                    &mut Context<Self>,
 1290                ) -> Option<Entity<ui::ContextMenu>>,
 1291        >,
 1292    >,
 1293    last_bounds: Option<Bounds<Pixels>>,
 1294    last_position_map: Option<Rc<PositionMap>>,
 1295    expect_bounds_change: Option<Bounds<Pixels>>,
 1296    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1297    tasks_update_task: Option<Task<()>>,
 1298    breakpoint_store: Option<Entity<BreakpointStore>>,
 1299    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1300    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1301    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1302    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1303    /// when hunks have comments stored.
 1304    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1305    /// Stored review comments grouped by hunk.
 1306    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1307    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1308    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1309    /// Counter for generating unique comment IDs.
 1310    next_review_comment_id: usize,
 1311    hovered_diff_hunk_row: Option<DisplayRow>,
 1312    pull_diagnostics_task: Task<()>,
 1313    in_project_search: bool,
 1314    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1315    breadcrumb_header: Option<String>,
 1316    focused_block: Option<FocusedBlock>,
 1317    next_scroll_position: NextScrollCursorCenterTopBottom,
 1318    addons: HashMap<TypeId, Box<dyn Addon>>,
 1319    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1320    load_diff_task: Option<Shared<Task<()>>>,
 1321    /// Whether we are temporarily displaying a diff other than git's
 1322    temporary_diff_override: bool,
 1323    selection_mark_mode: bool,
 1324    toggle_fold_multiple_buffers: Task<()>,
 1325    _scroll_cursor_center_top_bottom_task: Task<()>,
 1326    serialize_selections: Task<()>,
 1327    serialize_folds: Task<()>,
 1328    mouse_cursor_hidden: bool,
 1329    minimap: Option<Entity<Self>>,
 1330    hide_mouse_mode: HideMouseMode,
 1331    pub change_list: ChangeList,
 1332    inline_value_cache: InlineValueCache,
 1333    number_deleted_lines: bool,
 1334
 1335    selection_drag_state: SelectionDragState,
 1336    colors: Option<LspColorData>,
 1337    post_scroll_update: Task<()>,
 1338    refresh_colors_task: Task<()>,
 1339    use_document_folding_ranges: bool,
 1340    refresh_folding_ranges_task: Task<()>,
 1341    inlay_hints: Option<LspInlayHintData>,
 1342    folding_newlines: Task<()>,
 1343    select_next_is_case_sensitive: Option<bool>,
 1344    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1345    on_local_selections_changed:
 1346        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1347    suppress_selection_callback: bool,
 1348    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1349    accent_data: Option<AccentData>,
 1350    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1351    semantic_token_state: SemanticTokenState,
 1352    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1353    refresh_document_symbols_task: Shared<Task<()>>,
 1354    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1355    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1356    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1357    sticky_headers_task: Task<()>,
 1358    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1359}
 1360
 1361#[derive(Debug, PartialEq)]
 1362struct AccentData {
 1363    colors: AccentColors,
 1364    overrides: Vec<SharedString>,
 1365}
 1366
 1367fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1368    if debounce_ms > 0 {
 1369        Some(Duration::from_millis(debounce_ms))
 1370    } else {
 1371        None
 1372    }
 1373}
 1374
 1375#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1376enum NextScrollCursorCenterTopBottom {
 1377    #[default]
 1378    Center,
 1379    Top,
 1380    Bottom,
 1381}
 1382
 1383impl NextScrollCursorCenterTopBottom {
 1384    fn next(&self) -> Self {
 1385        match self {
 1386            Self::Center => Self::Top,
 1387            Self::Top => Self::Bottom,
 1388            Self::Bottom => Self::Center,
 1389        }
 1390    }
 1391}
 1392
 1393#[derive(Clone)]
 1394pub struct EditorSnapshot {
 1395    pub mode: EditorMode,
 1396    show_gutter: bool,
 1397    offset_content: bool,
 1398    show_line_numbers: Option<bool>,
 1399    number_deleted_lines: bool,
 1400    show_git_diff_gutter: Option<bool>,
 1401    show_code_actions: Option<bool>,
 1402    show_runnables: Option<bool>,
 1403    show_breakpoints: Option<bool>,
 1404    git_blame_gutter_max_author_length: Option<usize>,
 1405    pub display_snapshot: DisplaySnapshot,
 1406    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1407    is_focused: bool,
 1408    scroll_anchor: SharedScrollAnchor,
 1409    ongoing_scroll: OngoingScroll,
 1410    current_line_highlight: CurrentLineHighlight,
 1411    gutter_hovered: bool,
 1412    semantic_tokens_enabled: bool,
 1413}
 1414
 1415#[derive(Default, Debug, Clone, Copy)]
 1416pub struct GutterDimensions {
 1417    pub left_padding: Pixels,
 1418    pub right_padding: Pixels,
 1419    pub width: Pixels,
 1420    pub margin: Pixels,
 1421    pub git_blame_entries_width: Option<Pixels>,
 1422}
 1423
 1424impl GutterDimensions {
 1425    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1426        Self {
 1427            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1428            ..Default::default()
 1429        }
 1430    }
 1431
 1432    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1433        -cx.text_system().descent(font_id, font_size)
 1434    }
 1435    /// The full width of the space taken up by the gutter.
 1436    pub fn full_width(&self) -> Pixels {
 1437        self.margin + self.width
 1438    }
 1439
 1440    /// The width of the space reserved for the fold indicators,
 1441    /// use alongside 'justify_end' and `gutter_width` to
 1442    /// right align content with the line numbers
 1443    pub fn fold_area_width(&self) -> Pixels {
 1444        self.margin + self.right_padding
 1445    }
 1446}
 1447
 1448struct CharacterDimensions {
 1449    em_width: Pixels,
 1450    em_advance: Pixels,
 1451    line_height: Pixels,
 1452}
 1453
 1454#[derive(Debug)]
 1455pub struct RemoteSelection {
 1456    pub replica_id: ReplicaId,
 1457    pub selection: Selection<Anchor>,
 1458    pub cursor_shape: CursorShape,
 1459    pub collaborator_id: CollaboratorId,
 1460    pub line_mode: bool,
 1461    pub user_name: Option<SharedString>,
 1462    pub color: PlayerColor,
 1463}
 1464
 1465#[derive(Clone, Debug)]
 1466struct SelectionHistoryEntry {
 1467    selections: Arc<[Selection<Anchor>]>,
 1468    select_next_state: Option<SelectNextState>,
 1469    select_prev_state: Option<SelectNextState>,
 1470    add_selections_state: Option<AddSelectionsState>,
 1471}
 1472
 1473#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1474enum SelectionHistoryMode {
 1475    #[default]
 1476    Normal,
 1477    Undoing,
 1478    Redoing,
 1479    Skipping,
 1480}
 1481
 1482#[derive(Clone, PartialEq, Eq, Hash)]
 1483struct HoveredCursor {
 1484    replica_id: ReplicaId,
 1485    selection_id: usize,
 1486}
 1487
 1488#[derive(Debug)]
 1489/// SelectionEffects controls the side-effects of updating the selection.
 1490///
 1491/// The default behaviour does "what you mostly want":
 1492/// - it pushes to the nav history if the cursor moved by >10 lines
 1493/// - it re-triggers completion requests
 1494/// - it scrolls to fit
 1495///
 1496/// You might want to modify these behaviours. For example when doing a "jump"
 1497/// like go to definition, we always want to add to nav history; but when scrolling
 1498/// in vim mode we never do.
 1499///
 1500/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1501/// move.
 1502#[derive(Clone)]
 1503pub struct SelectionEffects {
 1504    nav_history: Option<bool>,
 1505    completions: bool,
 1506    scroll: Option<Autoscroll>,
 1507}
 1508
 1509impl Default for SelectionEffects {
 1510    fn default() -> Self {
 1511        Self {
 1512            nav_history: None,
 1513            completions: true,
 1514            scroll: Some(Autoscroll::fit()),
 1515        }
 1516    }
 1517}
 1518impl SelectionEffects {
 1519    pub fn scroll(scroll: Autoscroll) -> Self {
 1520        Self {
 1521            scroll: Some(scroll),
 1522            ..Default::default()
 1523        }
 1524    }
 1525
 1526    pub fn no_scroll() -> Self {
 1527        Self {
 1528            scroll: None,
 1529            ..Default::default()
 1530        }
 1531    }
 1532
 1533    pub fn completions(self, completions: bool) -> Self {
 1534        Self {
 1535            completions,
 1536            ..self
 1537        }
 1538    }
 1539
 1540    pub fn nav_history(self, nav_history: bool) -> Self {
 1541        Self {
 1542            nav_history: Some(nav_history),
 1543            ..self
 1544        }
 1545    }
 1546}
 1547
 1548struct DeferredSelectionEffectsState {
 1549    changed: bool,
 1550    effects: SelectionEffects,
 1551    old_cursor_position: Anchor,
 1552    history_entry: SelectionHistoryEntry,
 1553}
 1554
 1555#[derive(Default)]
 1556struct SelectionHistory {
 1557    #[allow(clippy::type_complexity)]
 1558    selections_by_transaction:
 1559        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1560    mode: SelectionHistoryMode,
 1561    undo_stack: VecDeque<SelectionHistoryEntry>,
 1562    redo_stack: VecDeque<SelectionHistoryEntry>,
 1563}
 1564
 1565impl SelectionHistory {
 1566    #[track_caller]
 1567    fn insert_transaction(
 1568        &mut self,
 1569        transaction_id: TransactionId,
 1570        selections: Arc<[Selection<Anchor>]>,
 1571    ) {
 1572        if selections.is_empty() {
 1573            log::error!(
 1574                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1575                std::panic::Location::caller()
 1576            );
 1577            return;
 1578        }
 1579        self.selections_by_transaction
 1580            .insert(transaction_id, (selections, None));
 1581    }
 1582
 1583    #[allow(clippy::type_complexity)]
 1584    fn transaction(
 1585        &self,
 1586        transaction_id: TransactionId,
 1587    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1588        self.selections_by_transaction.get(&transaction_id)
 1589    }
 1590
 1591    #[allow(clippy::type_complexity)]
 1592    fn transaction_mut(
 1593        &mut self,
 1594        transaction_id: TransactionId,
 1595    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1596        self.selections_by_transaction.get_mut(&transaction_id)
 1597    }
 1598
 1599    fn push(&mut self, entry: SelectionHistoryEntry) {
 1600        if !entry.selections.is_empty() {
 1601            match self.mode {
 1602                SelectionHistoryMode::Normal => {
 1603                    self.push_undo(entry);
 1604                    self.redo_stack.clear();
 1605                }
 1606                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1607                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1608                SelectionHistoryMode::Skipping => {}
 1609            }
 1610        }
 1611    }
 1612
 1613    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1614        if self
 1615            .undo_stack
 1616            .back()
 1617            .is_none_or(|e| e.selections != entry.selections)
 1618        {
 1619            self.undo_stack.push_back(entry);
 1620            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1621                self.undo_stack.pop_front();
 1622            }
 1623        }
 1624    }
 1625
 1626    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1627        if self
 1628            .redo_stack
 1629            .back()
 1630            .is_none_or(|e| e.selections != entry.selections)
 1631        {
 1632            self.redo_stack.push_back(entry);
 1633            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1634                self.redo_stack.pop_front();
 1635            }
 1636        }
 1637    }
 1638}
 1639
 1640#[derive(Clone, Copy)]
 1641pub struct RowHighlightOptions {
 1642    pub autoscroll: bool,
 1643    pub include_gutter: bool,
 1644}
 1645
 1646impl Default for RowHighlightOptions {
 1647    fn default() -> Self {
 1648        Self {
 1649            autoscroll: Default::default(),
 1650            include_gutter: true,
 1651        }
 1652    }
 1653}
 1654
 1655struct RowHighlight {
 1656    index: usize,
 1657    range: Range<Anchor>,
 1658    color: Hsla,
 1659    options: RowHighlightOptions,
 1660    type_id: TypeId,
 1661}
 1662
 1663#[derive(Clone, Debug)]
 1664struct AddSelectionsState {
 1665    groups: Vec<AddSelectionsGroup>,
 1666}
 1667
 1668#[derive(Clone, Debug)]
 1669struct AddSelectionsGroup {
 1670    above: bool,
 1671    stack: Vec<usize>,
 1672}
 1673
 1674#[derive(Clone)]
 1675struct SelectNextState {
 1676    query: AhoCorasick,
 1677    wordwise: bool,
 1678    done: bool,
 1679}
 1680
 1681impl std::fmt::Debug for SelectNextState {
 1682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1683        f.debug_struct(std::any::type_name::<Self>())
 1684            .field("wordwise", &self.wordwise)
 1685            .field("done", &self.done)
 1686            .finish()
 1687    }
 1688}
 1689
 1690#[derive(Debug)]
 1691struct AutocloseRegion {
 1692    selection_id: usize,
 1693    range: Range<Anchor>,
 1694    pair: BracketPair,
 1695}
 1696
 1697#[derive(Debug)]
 1698struct SnippetState {
 1699    ranges: Vec<Vec<Range<Anchor>>>,
 1700    active_index: usize,
 1701    choices: Vec<Option<Vec<String>>>,
 1702}
 1703
 1704#[doc(hidden)]
 1705pub struct RenameState {
 1706    pub range: Range<Anchor>,
 1707    pub old_name: Arc<str>,
 1708    pub editor: Entity<Editor>,
 1709    block_id: CustomBlockId,
 1710}
 1711
 1712struct InvalidationStack<T>(Vec<T>);
 1713
 1714struct RegisteredEditPredictionDelegate {
 1715    provider: Arc<dyn EditPredictionDelegateHandle>,
 1716    _subscription: Subscription,
 1717}
 1718
 1719#[derive(Debug, PartialEq, Eq)]
 1720pub struct ActiveDiagnosticGroup {
 1721    pub active_range: Range<Anchor>,
 1722    pub active_message: String,
 1723    pub group_id: usize,
 1724    pub blocks: HashSet<CustomBlockId>,
 1725}
 1726
 1727#[derive(Debug, PartialEq, Eq)]
 1728
 1729pub(crate) enum ActiveDiagnostic {
 1730    None,
 1731    All,
 1732    Group(ActiveDiagnosticGroup),
 1733}
 1734
 1735#[derive(Serialize, Deserialize, Clone, Debug)]
 1736pub struct ClipboardSelection {
 1737    /// The number of bytes in this selection.
 1738    pub len: usize,
 1739    /// Whether this was a full-line selection.
 1740    pub is_entire_line: bool,
 1741    /// The indentation of the first line when this content was originally copied.
 1742    pub first_line_indent: u32,
 1743    #[serde(default)]
 1744    pub file_path: Option<PathBuf>,
 1745    #[serde(default)]
 1746    pub line_range: Option<RangeInclusive<u32>>,
 1747}
 1748
 1749impl ClipboardSelection {
 1750    pub fn for_buffer(
 1751        len: usize,
 1752        is_entire_line: bool,
 1753        range: Range<Point>,
 1754        buffer: &MultiBufferSnapshot,
 1755        project: Option<&Entity<Project>>,
 1756        cx: &App,
 1757    ) -> Self {
 1758        let first_line_indent = buffer
 1759            .indent_size_for_line(MultiBufferRow(range.start.row))
 1760            .len;
 1761
 1762        let file_path = util::maybe!({
 1763            let project = project?.read(cx);
 1764            let file = buffer.file_at(range.start)?;
 1765            let project_path = ProjectPath {
 1766                worktree_id: file.worktree_id(cx),
 1767                path: file.path().clone(),
 1768            };
 1769            project.absolute_path(&project_path, cx)
 1770        });
 1771
 1772        let line_range = file_path.as_ref().and_then(|_| {
 1773            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1774            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1775            if start_excerpt_id == end_excerpt_id {
 1776                Some(start_point.row..=end_point.row)
 1777            } else {
 1778                None
 1779            }
 1780        });
 1781
 1782        Self {
 1783            len,
 1784            is_entire_line,
 1785            first_line_indent,
 1786            file_path,
 1787            line_range,
 1788        }
 1789    }
 1790}
 1791
 1792// selections, scroll behavior, was newest selection reversed
 1793type SelectSyntaxNodeHistoryState = (
 1794    Box<[Selection<Anchor>]>,
 1795    SelectSyntaxNodeScrollBehavior,
 1796    bool,
 1797);
 1798
 1799#[derive(Default)]
 1800struct SelectSyntaxNodeHistory {
 1801    stack: Vec<SelectSyntaxNodeHistoryState>,
 1802    // disable temporarily to allow changing selections without losing the stack
 1803    pub disable_clearing: bool,
 1804}
 1805
 1806impl SelectSyntaxNodeHistory {
 1807    pub fn try_clear(&mut self) {
 1808        if !self.disable_clearing {
 1809            self.stack.clear();
 1810        }
 1811    }
 1812
 1813    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1814        self.stack.push(selection);
 1815    }
 1816
 1817    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1818        self.stack.pop()
 1819    }
 1820}
 1821
 1822enum SelectSyntaxNodeScrollBehavior {
 1823    CursorTop,
 1824    FitSelection,
 1825    CursorBottom,
 1826}
 1827
 1828#[derive(Debug, Clone, Copy)]
 1829pub(crate) struct NavigationData {
 1830    cursor_anchor: Anchor,
 1831    cursor_position: Point,
 1832    scroll_anchor: ScrollAnchor,
 1833    scroll_top_row: u32,
 1834}
 1835
 1836#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1837pub enum GotoDefinitionKind {
 1838    Symbol,
 1839    Declaration,
 1840    Type,
 1841    Implementation,
 1842}
 1843
 1844pub enum FormatTarget {
 1845    Buffers(HashSet<Entity<Buffer>>),
 1846    Ranges(Vec<Range<MultiBufferPoint>>),
 1847}
 1848
 1849pub(crate) struct FocusedBlock {
 1850    id: BlockId,
 1851    focus_handle: WeakFocusHandle,
 1852}
 1853
 1854#[derive(Clone, Debug)]
 1855pub enum JumpData {
 1856    MultiBufferRow {
 1857        row: MultiBufferRow,
 1858        line_offset_from_top: u32,
 1859    },
 1860    MultiBufferPoint {
 1861        excerpt_id: ExcerptId,
 1862        position: Point,
 1863        anchor: text::Anchor,
 1864        line_offset_from_top: u32,
 1865    },
 1866}
 1867
 1868pub enum MultibufferSelectionMode {
 1869    First,
 1870    All,
 1871}
 1872
 1873#[derive(Clone, Copy, Debug, Default)]
 1874pub struct RewrapOptions {
 1875    pub override_language_settings: bool,
 1876    pub preserve_existing_whitespace: bool,
 1877}
 1878
 1879impl Editor {
 1880    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1881        let buffer = cx.new(|cx| Buffer::local("", cx));
 1882        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1883        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1884    }
 1885
 1886    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1887        let buffer = cx.new(|cx| Buffer::local("", cx));
 1888        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1889        Self::new(EditorMode::full(), buffer, None, window, cx)
 1890    }
 1891
 1892    pub fn auto_height(
 1893        min_lines: usize,
 1894        max_lines: usize,
 1895        window: &mut Window,
 1896        cx: &mut Context<Self>,
 1897    ) -> Self {
 1898        let buffer = cx.new(|cx| Buffer::local("", cx));
 1899        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1900        Self::new(
 1901            EditorMode::AutoHeight {
 1902                min_lines,
 1903                max_lines: Some(max_lines),
 1904            },
 1905            buffer,
 1906            None,
 1907            window,
 1908            cx,
 1909        )
 1910    }
 1911
 1912    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1913    /// The editor grows as tall as needed to fit its content.
 1914    pub fn auto_height_unbounded(
 1915        min_lines: usize,
 1916        window: &mut Window,
 1917        cx: &mut Context<Self>,
 1918    ) -> Self {
 1919        let buffer = cx.new(|cx| Buffer::local("", cx));
 1920        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1921        Self::new(
 1922            EditorMode::AutoHeight {
 1923                min_lines,
 1924                max_lines: None,
 1925            },
 1926            buffer,
 1927            None,
 1928            window,
 1929            cx,
 1930        )
 1931    }
 1932
 1933    pub fn for_buffer(
 1934        buffer: Entity<Buffer>,
 1935        project: Option<Entity<Project>>,
 1936        window: &mut Window,
 1937        cx: &mut Context<Self>,
 1938    ) -> Self {
 1939        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1940        Self::new(EditorMode::full(), buffer, project, window, cx)
 1941    }
 1942
 1943    pub fn for_multibuffer(
 1944        buffer: Entity<MultiBuffer>,
 1945        project: Option<Entity<Project>>,
 1946        window: &mut Window,
 1947        cx: &mut Context<Self>,
 1948    ) -> Self {
 1949        Self::new(EditorMode::full(), buffer, project, window, cx)
 1950    }
 1951
 1952    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1953        let mut clone = Self::new(
 1954            self.mode.clone(),
 1955            self.buffer.clone(),
 1956            self.project.clone(),
 1957            window,
 1958            cx,
 1959        );
 1960        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1961            let snapshot = display_map.snapshot(cx);
 1962            clone.display_map.update(cx, |display_map, cx| {
 1963                display_map.set_state(&snapshot, cx);
 1964            });
 1965            snapshot
 1966        });
 1967        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1968        clone.folds_did_change(cx);
 1969        clone.selections.clone_state(&self.selections);
 1970        clone
 1971            .scroll_manager
 1972            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1973        clone.searchable = self.searchable;
 1974        clone.read_only = self.read_only;
 1975        clone
 1976    }
 1977
 1978    pub fn new(
 1979        mode: EditorMode,
 1980        buffer: Entity<MultiBuffer>,
 1981        project: Option<Entity<Project>>,
 1982        window: &mut Window,
 1983        cx: &mut Context<Self>,
 1984    ) -> Self {
 1985        Editor::new_internal(mode, buffer, project, None, window, cx)
 1986    }
 1987
 1988    pub fn refresh_sticky_headers(
 1989        &mut self,
 1990        display_snapshot: &DisplaySnapshot,
 1991        cx: &mut Context<Editor>,
 1992    ) {
 1993        if !self.mode.is_full() {
 1994            return;
 1995        }
 1996        let multi_buffer = display_snapshot.buffer_snapshot();
 1997        let scroll_anchor = self
 1998            .scroll_manager
 1999            .native_anchor(display_snapshot, cx)
 2000            .anchor;
 2001        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2002            return;
 2003        };
 2004        let buffer = buffer.clone();
 2005
 2006        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2007        let max_row = buffer.max_point().row;
 2008        let start_row = buffer_visible_start.row.min(max_row);
 2009        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2010
 2011        let syntax = self.style(cx).syntax.clone();
 2012        let background_task = cx.background_spawn(async move {
 2013            buffer
 2014                .outline_items_containing(
 2015                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2016                    true,
 2017                    Some(syntax.as_ref()),
 2018                )
 2019                .into_iter()
 2020                .map(|outline_item| OutlineItem {
 2021                    depth: outline_item.depth,
 2022                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2023                    source_range_for_text: Anchor::range_in_buffer(
 2024                        excerpt_id,
 2025                        outline_item.source_range_for_text,
 2026                    ),
 2027                    text: outline_item.text,
 2028                    highlight_ranges: outline_item.highlight_ranges,
 2029                    name_ranges: outline_item.name_ranges,
 2030                    body_range: outline_item
 2031                        .body_range
 2032                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2033                    annotation_range: outline_item
 2034                        .annotation_range
 2035                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2036                })
 2037                .collect()
 2038        });
 2039        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2040            let sticky_headers = background_task.await;
 2041            this.update(cx, |this, cx| {
 2042                this.sticky_headers = Some(sticky_headers);
 2043                cx.notify();
 2044            })
 2045            .ok();
 2046        });
 2047    }
 2048
 2049    fn new_internal(
 2050        mode: EditorMode,
 2051        multi_buffer: Entity<MultiBuffer>,
 2052        project: Option<Entity<Project>>,
 2053        display_map: Option<Entity<DisplayMap>>,
 2054        window: &mut Window,
 2055        cx: &mut Context<Self>,
 2056    ) -> Self {
 2057        debug_assert!(
 2058            display_map.is_none() || mode.is_minimap(),
 2059            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2060        );
 2061
 2062        let full_mode = mode.is_full();
 2063        let is_minimap = mode.is_minimap();
 2064        let diagnostics_max_severity = if full_mode {
 2065            EditorSettings::get_global(cx)
 2066                .diagnostics_max_severity
 2067                .unwrap_or(DiagnosticSeverity::Hint)
 2068        } else {
 2069            DiagnosticSeverity::Off
 2070        };
 2071        let style = window.text_style();
 2072        let font_size = style.font_size.to_pixels(window.rem_size());
 2073        let editor = cx.entity().downgrade();
 2074        let fold_placeholder = FoldPlaceholder {
 2075            constrain_width: false,
 2076            render: Arc::new(move |fold_id, fold_range, cx| {
 2077                let editor = editor.clone();
 2078                FoldPlaceholder::fold_element(fold_id, cx)
 2079                    .cursor_pointer()
 2080                    .child("")
 2081                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2082                    .on_click(move |_, _window, cx| {
 2083                        editor
 2084                            .update(cx, |editor, cx| {
 2085                                editor.unfold_ranges(
 2086                                    &[fold_range.start..fold_range.end],
 2087                                    true,
 2088                                    false,
 2089                                    cx,
 2090                                );
 2091                                cx.stop_propagation();
 2092                            })
 2093                            .ok();
 2094                    })
 2095                    .into_any()
 2096            }),
 2097            merge_adjacent: true,
 2098            ..FoldPlaceholder::default()
 2099        };
 2100        let display_map = display_map.unwrap_or_else(|| {
 2101            cx.new(|cx| {
 2102                DisplayMap::new(
 2103                    multi_buffer.clone(),
 2104                    style.font(),
 2105                    font_size,
 2106                    None,
 2107                    FILE_HEADER_HEIGHT,
 2108                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2109                    fold_placeholder,
 2110                    diagnostics_max_severity,
 2111                    cx,
 2112                )
 2113            })
 2114        });
 2115
 2116        let selections = SelectionsCollection::new();
 2117
 2118        let blink_manager = cx.new(|cx| {
 2119            let mut blink_manager = BlinkManager::new(
 2120                CURSOR_BLINK_INTERVAL,
 2121                |cx| EditorSettings::get_global(cx).cursor_blink,
 2122                cx,
 2123            );
 2124            if is_minimap {
 2125                blink_manager.disable(cx);
 2126            }
 2127            blink_manager
 2128        });
 2129
 2130        let soft_wrap_mode_override =
 2131            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2132
 2133        let mut project_subscriptions = Vec::new();
 2134        if full_mode && let Some(project) = project.as_ref() {
 2135            project_subscriptions.push(cx.subscribe_in(
 2136                project,
 2137                window,
 2138                |editor, _, event, window, cx| match event {
 2139                    project::Event::RefreshCodeLens => {
 2140                        // we always query lens with actions, without storing them, always refreshing them
 2141                    }
 2142                    project::Event::RefreshInlayHints {
 2143                        server_id,
 2144                        request_id,
 2145                    } => {
 2146                        editor.refresh_inlay_hints(
 2147                            InlayHintRefreshReason::RefreshRequested {
 2148                                server_id: *server_id,
 2149                                request_id: *request_id,
 2150                            },
 2151                            cx,
 2152                        );
 2153                    }
 2154                    project::Event::RefreshSemanticTokens {
 2155                        server_id,
 2156                        request_id,
 2157                    } => {
 2158                        editor.refresh_semantic_tokens(
 2159                            None,
 2160                            Some(RefreshForServer {
 2161                                server_id: *server_id,
 2162                                request_id: *request_id,
 2163                            }),
 2164                            cx,
 2165                        );
 2166                    }
 2167                    project::Event::LanguageServerRemoved(_) => {
 2168                        editor.registered_buffers.clear();
 2169                        editor.register_visible_buffers(cx);
 2170                        editor.invalidate_semantic_tokens(None);
 2171                        editor.update_lsp_data(None, window, cx);
 2172                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2173                        if editor.tasks_update_task.is_none() {
 2174                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2175                        }
 2176                    }
 2177                    project::Event::LanguageServerAdded(..) => {
 2178                        if editor.tasks_update_task.is_none() {
 2179                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2180                        }
 2181                    }
 2182                    project::Event::SnippetEdit(id, snippet_edits) => {
 2183                        // todo(lw): Non singletons
 2184                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2185                            let snapshot = buffer.read(cx).snapshot();
 2186                            let focus_handle = editor.focus_handle(cx);
 2187                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2188                                for (range, snippet) in snippet_edits {
 2189                                    let buffer_range =
 2190                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2191                                    editor
 2192                                        .insert_snippet(
 2193                                            &[MultiBufferOffset(buffer_range.start)
 2194                                                ..MultiBufferOffset(buffer_range.end)],
 2195                                            snippet.clone(),
 2196                                            window,
 2197                                            cx,
 2198                                        )
 2199                                        .ok();
 2200                                }
 2201                            }
 2202                        }
 2203                    }
 2204                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2205                        let buffer_id = *buffer_id;
 2206                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2207                            editor.register_buffer(buffer_id, cx);
 2208                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2209                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2210                            refresh_linked_ranges(editor, window, cx);
 2211                            editor.refresh_code_actions(window, cx);
 2212                            editor.refresh_document_highlights(cx);
 2213                        }
 2214                    }
 2215
 2216                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2217                        let Some(workspace) = editor.workspace() else {
 2218                            return;
 2219                        };
 2220                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2221                        else {
 2222                            return;
 2223                        };
 2224
 2225                        if active_editor.entity_id() == cx.entity_id() {
 2226                            let entity_id = cx.entity_id();
 2227                            workspace.update(cx, |this, cx| {
 2228                                this.panes_mut()
 2229                                    .iter_mut()
 2230                                    .filter(|pane| pane.entity_id() != entity_id)
 2231                                    .for_each(|p| {
 2232                                        p.update(cx, |pane, _| {
 2233                                            pane.nav_history_mut().rename_item(
 2234                                                entity_id,
 2235                                                project_path.clone(),
 2236                                                abs_path.clone().into(),
 2237                                            );
 2238                                        })
 2239                                    });
 2240                            });
 2241
 2242                            Self::open_transaction_for_hidden_buffers(
 2243                                workspace,
 2244                                transaction.clone(),
 2245                                "Rename".to_string(),
 2246                                window,
 2247                                cx,
 2248                            );
 2249                        }
 2250                    }
 2251
 2252                    project::Event::WorkspaceEditApplied(transaction) => {
 2253                        let Some(workspace) = editor.workspace() else {
 2254                            return;
 2255                        };
 2256                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2257                        else {
 2258                            return;
 2259                        };
 2260
 2261                        if active_editor.entity_id() == cx.entity_id() {
 2262                            Self::open_transaction_for_hidden_buffers(
 2263                                workspace,
 2264                                transaction.clone(),
 2265                                "LSP Edit".to_string(),
 2266                                window,
 2267                                cx,
 2268                            );
 2269                        }
 2270                    }
 2271
 2272                    _ => {}
 2273                },
 2274            ));
 2275            if let Some(task_inventory) = project
 2276                .read(cx)
 2277                .task_store()
 2278                .read(cx)
 2279                .task_inventory()
 2280                .cloned()
 2281            {
 2282                project_subscriptions.push(cx.observe_in(
 2283                    &task_inventory,
 2284                    window,
 2285                    |editor, _, window, cx| {
 2286                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2287                    },
 2288                ));
 2289            };
 2290
 2291            project_subscriptions.push(cx.subscribe_in(
 2292                &project.read(cx).breakpoint_store(),
 2293                window,
 2294                |editor, _, event, window, cx| match event {
 2295                    BreakpointStoreEvent::ClearDebugLines => {
 2296                        editor.clear_row_highlights::<ActiveDebugLine>();
 2297                        editor.refresh_inline_values(cx);
 2298                    }
 2299                    BreakpointStoreEvent::SetDebugLine => {
 2300                        if editor.go_to_active_debug_line(window, cx) {
 2301                            cx.stop_propagation();
 2302                        }
 2303
 2304                        editor.refresh_inline_values(cx);
 2305                    }
 2306                    _ => {}
 2307                },
 2308            ));
 2309            let git_store = project.read(cx).git_store().clone();
 2310            let project = project.clone();
 2311            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2312                if let GitStoreEvent::RepositoryAdded = event {
 2313                    this.load_diff_task = Some(
 2314                        update_uncommitted_diff_for_buffer(
 2315                            cx.entity(),
 2316                            &project,
 2317                            this.buffer.read(cx).all_buffers(),
 2318                            this.buffer.clone(),
 2319                            cx,
 2320                        )
 2321                        .shared(),
 2322                    );
 2323                }
 2324            }));
 2325        }
 2326
 2327        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2328
 2329        let inlay_hint_settings =
 2330            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2331        let focus_handle = cx.focus_handle();
 2332        if !is_minimap {
 2333            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2334                .detach();
 2335            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2336                .detach();
 2337            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2338                .detach();
 2339            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2340                .detach();
 2341            cx.observe_pending_input(window, Self::observe_pending_input)
 2342                .detach();
 2343        }
 2344
 2345        let show_indent_guides =
 2346            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2347                Some(false)
 2348            } else {
 2349                None
 2350            };
 2351
 2352        let breakpoint_store = match (&mode, project.as_ref()) {
 2353            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2354            _ => None,
 2355        };
 2356
 2357        let mut code_action_providers = Vec::new();
 2358        let mut load_uncommitted_diff = None;
 2359        if let Some(project) = project.clone() {
 2360            load_uncommitted_diff = Some(
 2361                update_uncommitted_diff_for_buffer(
 2362                    cx.entity(),
 2363                    &project,
 2364                    multi_buffer.read(cx).all_buffers(),
 2365                    multi_buffer.clone(),
 2366                    cx,
 2367                )
 2368                .shared(),
 2369            );
 2370            code_action_providers.push(Rc::new(project) as Rc<_>);
 2371        }
 2372
 2373        let mut editor = Self {
 2374            focus_handle,
 2375            show_cursor_when_unfocused: false,
 2376            last_focused_descendant: None,
 2377            buffer: multi_buffer.clone(),
 2378            display_map: display_map.clone(),
 2379            placeholder_display_map: None,
 2380            selections,
 2381            scroll_manager: ScrollManager::new(cx),
 2382            columnar_selection_state: None,
 2383            add_selections_state: None,
 2384            select_next_state: None,
 2385            select_prev_state: None,
 2386            selection_history: SelectionHistory::default(),
 2387            defer_selection_effects: false,
 2388            deferred_selection_effects_state: None,
 2389            autoclose_regions: Vec::new(),
 2390            snippet_stack: InvalidationStack::default(),
 2391            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2392            ime_transaction: None,
 2393            active_diagnostics: ActiveDiagnostic::None,
 2394            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2395            inline_diagnostics_update: Task::ready(()),
 2396            inline_diagnostics: Vec::new(),
 2397            soft_wrap_mode_override,
 2398            diagnostics_max_severity,
 2399            hard_wrap: None,
 2400            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2401            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2402            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2403            project,
 2404            blink_manager: blink_manager.clone(),
 2405            show_local_selections: true,
 2406            show_scrollbars: ScrollbarAxes {
 2407                horizontal: full_mode,
 2408                vertical: full_mode,
 2409            },
 2410            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2411            offset_content: !matches!(mode, EditorMode::SingleLine),
 2412            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2413            show_gutter: full_mode,
 2414            show_line_numbers: (!full_mode).then_some(false),
 2415            use_relative_line_numbers: None,
 2416            disable_expand_excerpt_buttons: !full_mode,
 2417            delegate_expand_excerpts: false,
 2418            delegate_stage_and_restore: false,
 2419            delegate_open_excerpts: false,
 2420            enable_lsp_data: true,
 2421            enable_runnables: true,
 2422            show_git_diff_gutter: None,
 2423            show_code_actions: None,
 2424            show_runnables: None,
 2425            show_breakpoints: None,
 2426            show_diff_review_button: false,
 2427            show_wrap_guides: None,
 2428            show_indent_guides,
 2429            buffers_with_disabled_indent_guides: HashSet::default(),
 2430            highlight_order: 0,
 2431            highlighted_rows: HashMap::default(),
 2432            background_highlights: HashMap::default(),
 2433            gutter_highlights: HashMap::default(),
 2434            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2435            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2436            nav_history: None,
 2437            context_menu: RefCell::new(None),
 2438            context_menu_options: None,
 2439            mouse_context_menu: None,
 2440            completion_tasks: Vec::new(),
 2441            inline_blame_popover: None,
 2442            inline_blame_popover_show_task: None,
 2443            signature_help_state: SignatureHelpState::default(),
 2444            auto_signature_help: None,
 2445            find_all_references_task_sources: Vec::new(),
 2446            next_completion_id: 0,
 2447            next_inlay_id: 0,
 2448            code_action_providers,
 2449            available_code_actions: None,
 2450            code_actions_task: None,
 2451            quick_selection_highlight_task: None,
 2452            debounced_selection_highlight_task: None,
 2453            debounced_selection_highlight_complete: false,
 2454            document_highlights_task: None,
 2455            linked_editing_range_task: None,
 2456            pending_rename: None,
 2457            searchable: !is_minimap,
 2458            cursor_shape: EditorSettings::get_global(cx)
 2459                .cursor_shape
 2460                .unwrap_or_default(),
 2461            cursor_offset_on_selection: false,
 2462            current_line_highlight: None,
 2463            autoindent_mode: Some(AutoindentMode::EachLine),
 2464            collapse_matches: false,
 2465            workspace: None,
 2466            input_enabled: !is_minimap,
 2467            use_modal_editing: full_mode,
 2468            read_only: is_minimap,
 2469            use_autoclose: true,
 2470            use_auto_surround: true,
 2471            auto_replace_emoji_shortcode: false,
 2472            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2473            leader_id: None,
 2474            remote_id: None,
 2475            hover_state: HoverState::default(),
 2476            pending_mouse_down: None,
 2477            prev_pressure_stage: None,
 2478            hovered_link_state: None,
 2479            edit_prediction_provider: None,
 2480            active_edit_prediction: None,
 2481            stale_edit_prediction_in_menu: None,
 2482            edit_prediction_preview: EditPredictionPreview::Inactive {
 2483                released_too_fast: false,
 2484            },
 2485            inline_diagnostics_enabled: full_mode,
 2486            diagnostics_enabled: full_mode,
 2487            word_completions_enabled: full_mode,
 2488            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2489            gutter_hovered: false,
 2490            pixel_position_of_newest_cursor: None,
 2491            last_bounds: None,
 2492            last_position_map: None,
 2493            expect_bounds_change: None,
 2494            gutter_dimensions: GutterDimensions::default(),
 2495            style: None,
 2496            show_cursor_names: false,
 2497            hovered_cursors: HashMap::default(),
 2498            next_editor_action_id: EditorActionId::default(),
 2499            editor_actions: Rc::default(),
 2500            edit_predictions_hidden_for_vim_mode: false,
 2501            show_edit_predictions_override: None,
 2502            show_completions_on_input_override: None,
 2503            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2504            edit_prediction_settings: EditPredictionSettings::Disabled,
 2505            edit_prediction_indent_conflict: false,
 2506            edit_prediction_requires_modifier_in_indent_conflict: true,
 2507            custom_context_menu: None,
 2508            show_git_blame_gutter: false,
 2509            show_git_blame_inline: false,
 2510            show_selection_menu: None,
 2511            show_git_blame_inline_delay_task: None,
 2512            git_blame_inline_enabled: full_mode
 2513                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2514            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2515            buffer_serialization: is_minimap.not().then(|| {
 2516                BufferSerialization::new(
 2517                    ProjectSettings::get_global(cx)
 2518                        .session
 2519                        .restore_unsaved_buffers,
 2520                )
 2521            }),
 2522            blame: None,
 2523            blame_subscription: None,
 2524            tasks: BTreeMap::default(),
 2525
 2526            breakpoint_store,
 2527            gutter_breakpoint_indicator: (None, None),
 2528            gutter_diff_review_indicator: (None, None),
 2529            diff_review_drag_state: None,
 2530            diff_review_overlays: Vec::new(),
 2531            stored_review_comments: Vec::new(),
 2532            next_review_comment_id: 0,
 2533            hovered_diff_hunk_row: None,
 2534            _subscriptions: (!is_minimap)
 2535                .then(|| {
 2536                    vec![
 2537                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2538                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2539                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2540                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2541                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2542                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2543                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2544                        cx.observe_window_activation(window, |editor, window, cx| {
 2545                            let active = window.is_window_active();
 2546                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2547                                if active {
 2548                                    blink_manager.enable(cx);
 2549                                } else {
 2550                                    blink_manager.disable(cx);
 2551                                }
 2552                            });
 2553                            if active {
 2554                                editor.show_mouse_cursor(cx);
 2555                            }
 2556                        }),
 2557                    ]
 2558                })
 2559                .unwrap_or_default(),
 2560            tasks_update_task: None,
 2561            pull_diagnostics_task: Task::ready(()),
 2562            colors: None,
 2563            refresh_colors_task: Task::ready(()),
 2564            use_document_folding_ranges: false,
 2565            refresh_folding_ranges_task: Task::ready(()),
 2566            inlay_hints: None,
 2567            next_color_inlay_id: 0,
 2568            post_scroll_update: Task::ready(()),
 2569            linked_edit_ranges: Default::default(),
 2570            in_project_search: false,
 2571            previous_search_ranges: None,
 2572            breadcrumb_header: None,
 2573            focused_block: None,
 2574            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2575            addons: HashMap::default(),
 2576            registered_buffers: HashMap::default(),
 2577            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2578            selection_mark_mode: false,
 2579            toggle_fold_multiple_buffers: Task::ready(()),
 2580            serialize_selections: Task::ready(()),
 2581            serialize_folds: Task::ready(()),
 2582            text_style_refinement: None,
 2583            load_diff_task: load_uncommitted_diff,
 2584            temporary_diff_override: false,
 2585            mouse_cursor_hidden: false,
 2586            minimap: None,
 2587            hide_mouse_mode: EditorSettings::get_global(cx)
 2588                .hide_mouse
 2589                .unwrap_or_default(),
 2590            change_list: ChangeList::new(),
 2591            mode,
 2592            selection_drag_state: SelectionDragState::None,
 2593            folding_newlines: Task::ready(()),
 2594            lookup_key: None,
 2595            select_next_is_case_sensitive: None,
 2596            on_local_selections_changed: None,
 2597            suppress_selection_callback: false,
 2598            applicable_language_settings: HashMap::default(),
 2599            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2600            accent_data: None,
 2601            fetched_tree_sitter_chunks: HashMap::default(),
 2602            number_deleted_lines: false,
 2603            refresh_matching_bracket_highlights_task: Task::ready(()),
 2604            refresh_document_symbols_task: Task::ready(()).shared(),
 2605            lsp_document_symbols: HashMap::default(),
 2606            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2607            outline_symbols_at_cursor: None,
 2608            sticky_headers_task: Task::ready(()),
 2609            sticky_headers: None,
 2610        };
 2611
 2612        if is_minimap {
 2613            return editor;
 2614        }
 2615
 2616        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2617        editor.accent_data = editor.fetch_accent_data(cx);
 2618
 2619        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2620            editor
 2621                ._subscriptions
 2622                .push(cx.observe(breakpoints, |_, _, cx| {
 2623                    cx.notify();
 2624                }));
 2625        }
 2626        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2627        editor._subscriptions.extend(project_subscriptions);
 2628
 2629        editor._subscriptions.push(cx.subscribe_in(
 2630            &cx.entity(),
 2631            window,
 2632            |editor, _, e: &EditorEvent, window, cx| match e {
 2633                EditorEvent::ScrollPositionChanged { local, .. } => {
 2634                    if *local {
 2635                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2636                        editor.inline_blame_popover.take();
 2637                        let snapshot = editor.snapshot(window, cx);
 2638                        let new_anchor = editor
 2639                            .scroll_manager
 2640                            .native_anchor(&snapshot.display_snapshot, cx);
 2641                        editor.update_restoration_data(cx, move |data| {
 2642                            data.scroll_position = (
 2643                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2644                                new_anchor.offset,
 2645                            );
 2646                        });
 2647
 2648                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2649                            cx.background_executor()
 2650                                .timer(Duration::from_millis(50))
 2651                                .await;
 2652                            editor
 2653                                .update_in(cx, |editor, window, cx| {
 2654                                    editor.register_visible_buffers(cx);
 2655                                    editor.colorize_brackets(false, cx);
 2656                                    editor.refresh_inlay_hints(
 2657                                        InlayHintRefreshReason::NewLinesShown,
 2658                                        cx,
 2659                                    );
 2660                                    if !editor.buffer().read(cx).is_singleton() {
 2661                                        editor.update_lsp_data(None, window, cx);
 2662                                    }
 2663                                })
 2664                                .ok();
 2665                        });
 2666                    }
 2667                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2668                }
 2669                EditorEvent::Edited { .. } => {
 2670                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2671                        .map(|vim_mode| vim_mode.0)
 2672                        .unwrap_or(false);
 2673                    if !vim_mode {
 2674                        let display_map = editor.display_snapshot(cx);
 2675                        let selections = editor.selections.all_adjusted_display(&display_map);
 2676                        let pop_state = editor
 2677                            .change_list
 2678                            .last()
 2679                            .map(|previous| {
 2680                                previous.len() == selections.len()
 2681                                    && previous.iter().enumerate().all(|(ix, p)| {
 2682                                        p.to_display_point(&display_map).row()
 2683                                            == selections[ix].head().row()
 2684                                    })
 2685                            })
 2686                            .unwrap_or(false);
 2687                        let new_positions = selections
 2688                            .into_iter()
 2689                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2690                            .collect();
 2691                        editor
 2692                            .change_list
 2693                            .push_to_change_list(pop_state, new_positions);
 2694                    }
 2695                }
 2696                _ => (),
 2697            },
 2698        ));
 2699
 2700        if let Some(dap_store) = editor
 2701            .project
 2702            .as_ref()
 2703            .map(|project| project.read(cx).dap_store())
 2704        {
 2705            let weak_editor = cx.weak_entity();
 2706
 2707            editor
 2708                ._subscriptions
 2709                .push(
 2710                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2711                        let session_entity = cx.entity();
 2712                        weak_editor
 2713                            .update(cx, |editor, cx| {
 2714                                editor._subscriptions.push(
 2715                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2716                                );
 2717                            })
 2718                            .ok();
 2719                    }),
 2720                );
 2721
 2722            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2723                editor
 2724                    ._subscriptions
 2725                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2726            }
 2727        }
 2728
 2729        // skip adding the initial selection to selection history
 2730        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2731        editor.end_selection(window, cx);
 2732        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2733
 2734        editor.scroll_manager.show_scrollbars(window, cx);
 2735        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2736
 2737        if full_mode {
 2738            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2739            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2740
 2741            if editor.git_blame_inline_enabled {
 2742                editor.start_git_blame_inline(false, window, cx);
 2743            }
 2744
 2745            editor.go_to_active_debug_line(window, cx);
 2746
 2747            editor.minimap =
 2748                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2749            editor.colors = Some(LspColorData::new(cx));
 2750            editor.use_document_folding_ranges = true;
 2751            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2752
 2753            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2754                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2755            }
 2756            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2757        }
 2758
 2759        editor
 2760    }
 2761
 2762    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2763        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2764    }
 2765
 2766    pub fn deploy_mouse_context_menu(
 2767        &mut self,
 2768        position: gpui::Point<Pixels>,
 2769        context_menu: Entity<ContextMenu>,
 2770        window: &mut Window,
 2771        cx: &mut Context<Self>,
 2772    ) {
 2773        self.mouse_context_menu = Some(MouseContextMenu::new(
 2774            self,
 2775            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2776            context_menu,
 2777            window,
 2778            cx,
 2779        ));
 2780    }
 2781
 2782    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2783        self.mouse_context_menu
 2784            .as_ref()
 2785            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2786    }
 2787
 2788    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2789        if self
 2790            .selections
 2791            .pending_anchor()
 2792            .is_some_and(|pending_selection| {
 2793                let snapshot = self.buffer().read(cx).snapshot(cx);
 2794                pending_selection.range().includes(range, &snapshot)
 2795            })
 2796        {
 2797            return true;
 2798        }
 2799
 2800        self.selections
 2801            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2802            .into_iter()
 2803            .any(|selection| {
 2804                // This is needed to cover a corner case, if we just check for an existing
 2805                // selection in the fold range, having a cursor at the start of the fold
 2806                // marks it as selected. Non-empty selections don't cause this.
 2807                let length = selection.end - selection.start;
 2808                length > 0
 2809            })
 2810    }
 2811
 2812    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2813        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2814    }
 2815
 2816    fn key_context_internal(
 2817        &self,
 2818        has_active_edit_prediction: bool,
 2819        window: &mut Window,
 2820        cx: &mut App,
 2821    ) -> KeyContext {
 2822        let mut key_context = KeyContext::new_with_defaults();
 2823        key_context.add("Editor");
 2824        let mode = match self.mode {
 2825            EditorMode::SingleLine => "single_line",
 2826            EditorMode::AutoHeight { .. } => "auto_height",
 2827            EditorMode::Minimap { .. } => "minimap",
 2828            EditorMode::Full { .. } => "full",
 2829        };
 2830
 2831        if EditorSettings::jupyter_enabled(cx) {
 2832            key_context.add("jupyter");
 2833        }
 2834
 2835        key_context.set("mode", mode);
 2836        if self.pending_rename.is_some() {
 2837            key_context.add("renaming");
 2838        }
 2839
 2840        if let Some(snippet_stack) = self.snippet_stack.last() {
 2841            key_context.add("in_snippet");
 2842
 2843            if snippet_stack.active_index > 0 {
 2844                key_context.add("has_previous_tabstop");
 2845            }
 2846
 2847            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2848                key_context.add("has_next_tabstop");
 2849            }
 2850        }
 2851
 2852        match self.context_menu.borrow().as_ref() {
 2853            Some(CodeContextMenu::Completions(menu)) => {
 2854                if menu.visible() {
 2855                    key_context.add("menu");
 2856                    key_context.add("showing_completions");
 2857                }
 2858            }
 2859            Some(CodeContextMenu::CodeActions(menu)) => {
 2860                if menu.visible() {
 2861                    key_context.add("menu");
 2862                    key_context.add("showing_code_actions")
 2863                }
 2864            }
 2865            None => {}
 2866        }
 2867
 2868        if self.signature_help_state.has_multiple_signatures() {
 2869            key_context.add("showing_signature_help");
 2870        }
 2871
 2872        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2873        if !self.focus_handle(cx).contains_focused(window, cx)
 2874            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2875        {
 2876            for addon in self.addons.values() {
 2877                addon.extend_key_context(&mut key_context, cx)
 2878            }
 2879        }
 2880
 2881        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2882            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2883                Some(
 2884                    file.full_path(cx)
 2885                        .extension()?
 2886                        .to_string_lossy()
 2887                        .to_lowercase(),
 2888                )
 2889            }) {
 2890                key_context.set("extension", extension);
 2891            }
 2892        } else {
 2893            key_context.add("multibuffer");
 2894        }
 2895
 2896        if has_active_edit_prediction {
 2897            if self.edit_prediction_in_conflict() {
 2898                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2899            } else {
 2900                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2901                key_context.add("copilot_suggestion");
 2902            }
 2903        }
 2904
 2905        if self.selection_mark_mode {
 2906            key_context.add("selection_mode");
 2907        }
 2908
 2909        let disjoint = self.selections.disjoint_anchors();
 2910        let snapshot = self.snapshot(window, cx);
 2911        let snapshot = snapshot.buffer_snapshot();
 2912        if self.mode == EditorMode::SingleLine
 2913            && let [selection] = disjoint
 2914            && selection.start == selection.end
 2915            && selection.end.to_offset(snapshot) == snapshot.len()
 2916        {
 2917            key_context.add("end_of_input");
 2918        }
 2919
 2920        if self.has_any_expanded_diff_hunks(cx) {
 2921            key_context.add("diffs_expanded");
 2922        }
 2923
 2924        key_context
 2925    }
 2926
 2927    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2928        self.last_bounds.as_ref()
 2929    }
 2930
 2931    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2932        if self.mouse_cursor_hidden {
 2933            self.mouse_cursor_hidden = false;
 2934            cx.notify();
 2935        }
 2936    }
 2937
 2938    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2939        let hide_mouse_cursor = match origin {
 2940            HideMouseCursorOrigin::TypingAction => {
 2941                matches!(
 2942                    self.hide_mouse_mode,
 2943                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2944                )
 2945            }
 2946            HideMouseCursorOrigin::MovementAction => {
 2947                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2948            }
 2949        };
 2950        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2951            self.mouse_cursor_hidden = hide_mouse_cursor;
 2952            cx.notify();
 2953        }
 2954    }
 2955
 2956    pub fn edit_prediction_in_conflict(&self) -> bool {
 2957        if !self.show_edit_predictions_in_menu() {
 2958            return false;
 2959        }
 2960
 2961        let showing_completions = self
 2962            .context_menu
 2963            .borrow()
 2964            .as_ref()
 2965            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2966
 2967        showing_completions
 2968            || self.edit_prediction_requires_modifier()
 2969            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2970            // bindings to insert tab characters.
 2971            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2972    }
 2973
 2974    pub fn accept_edit_prediction_keybind(
 2975        &self,
 2976        granularity: EditPredictionGranularity,
 2977        window: &mut Window,
 2978        cx: &mut App,
 2979    ) -> AcceptEditPredictionBinding {
 2980        let key_context = self.key_context_internal(true, window, cx);
 2981        let in_conflict = self.edit_prediction_in_conflict();
 2982
 2983        let bindings =
 2984            match granularity {
 2985                EditPredictionGranularity::Word => window
 2986                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2987                EditPredictionGranularity::Line => window
 2988                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2989                EditPredictionGranularity::Full => {
 2990                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2991                }
 2992            };
 2993
 2994        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2995            !in_conflict
 2996                || binding
 2997                    .keystrokes()
 2998                    .first()
 2999                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 3000        }))
 3001    }
 3002
 3003    pub fn new_file(
 3004        workspace: &mut Workspace,
 3005        _: &workspace::NewFile,
 3006        window: &mut Window,
 3007        cx: &mut Context<Workspace>,
 3008    ) {
 3009        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3010            "Failed to create buffer",
 3011            window,
 3012            cx,
 3013            |e, _, _| match e.error_code() {
 3014                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3015                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3016                e.error_tag("required").unwrap_or("the latest version")
 3017            )),
 3018                _ => None,
 3019            },
 3020        );
 3021    }
 3022
 3023    pub fn new_in_workspace(
 3024        workspace: &mut Workspace,
 3025        window: &mut Window,
 3026        cx: &mut Context<Workspace>,
 3027    ) -> Task<Result<Entity<Editor>>> {
 3028        let project = workspace.project().clone();
 3029        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3030
 3031        cx.spawn_in(window, async move |workspace, cx| {
 3032            let buffer = create.await?;
 3033            workspace.update_in(cx, |workspace, window, cx| {
 3034                let editor =
 3035                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3036                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3037                editor
 3038            })
 3039        })
 3040    }
 3041
 3042    fn new_file_vertical(
 3043        workspace: &mut Workspace,
 3044        _: &workspace::NewFileSplitVertical,
 3045        window: &mut Window,
 3046        cx: &mut Context<Workspace>,
 3047    ) {
 3048        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3049    }
 3050
 3051    fn new_file_horizontal(
 3052        workspace: &mut Workspace,
 3053        _: &workspace::NewFileSplitHorizontal,
 3054        window: &mut Window,
 3055        cx: &mut Context<Workspace>,
 3056    ) {
 3057        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3058    }
 3059
 3060    fn new_file_split(
 3061        workspace: &mut Workspace,
 3062        action: &workspace::NewFileSplit,
 3063        window: &mut Window,
 3064        cx: &mut Context<Workspace>,
 3065    ) {
 3066        Self::new_file_in_direction(workspace, action.0, window, cx)
 3067    }
 3068
 3069    fn new_file_in_direction(
 3070        workspace: &mut Workspace,
 3071        direction: SplitDirection,
 3072        window: &mut Window,
 3073        cx: &mut Context<Workspace>,
 3074    ) {
 3075        let project = workspace.project().clone();
 3076        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3077
 3078        cx.spawn_in(window, async move |workspace, cx| {
 3079            let buffer = create.await?;
 3080            workspace.update_in(cx, move |workspace, window, cx| {
 3081                workspace.split_item(
 3082                    direction,
 3083                    Box::new(
 3084                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3085                    ),
 3086                    window,
 3087                    cx,
 3088                )
 3089            })?;
 3090            anyhow::Ok(())
 3091        })
 3092        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3093            match e.error_code() {
 3094                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3095                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3096                e.error_tag("required").unwrap_or("the latest version")
 3097            )),
 3098                _ => None,
 3099            }
 3100        });
 3101    }
 3102
 3103    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3104        self.leader_id
 3105    }
 3106
 3107    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3108        &self.buffer
 3109    }
 3110
 3111    pub fn project(&self) -> Option<&Entity<Project>> {
 3112        self.project.as_ref()
 3113    }
 3114
 3115    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3116        self.workspace.as_ref()?.0.upgrade()
 3117    }
 3118
 3119    /// Detaches a task and shows an error notification in the workspace if available,
 3120    /// otherwise just logs the error.
 3121    pub fn detach_and_notify_err<R, E>(
 3122        &self,
 3123        task: Task<Result<R, E>>,
 3124        window: &mut Window,
 3125        cx: &mut App,
 3126    ) where
 3127        E: std::fmt::Debug + std::fmt::Display + 'static,
 3128        R: 'static,
 3129    {
 3130        if let Some(workspace) = self.workspace() {
 3131            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3132        } else {
 3133            task.detach_and_log_err(cx);
 3134        }
 3135    }
 3136
 3137    /// Returns the workspace serialization ID if this editor should be serialized.
 3138    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3139        self.workspace
 3140            .as_ref()
 3141            .filter(|_| self.should_serialize_buffer())
 3142            .and_then(|workspace| workspace.1)
 3143    }
 3144
 3145    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3146        self.buffer().read(cx).title(cx)
 3147    }
 3148
 3149    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3150        let git_blame_gutter_max_author_length = self
 3151            .render_git_blame_gutter(cx)
 3152            .then(|| {
 3153                if let Some(blame) = self.blame.as_ref() {
 3154                    let max_author_length =
 3155                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3156                    Some(max_author_length)
 3157                } else {
 3158                    None
 3159                }
 3160            })
 3161            .flatten();
 3162
 3163        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3164
 3165        EditorSnapshot {
 3166            mode: self.mode.clone(),
 3167            show_gutter: self.show_gutter,
 3168            offset_content: self.offset_content,
 3169            show_line_numbers: self.show_line_numbers,
 3170            number_deleted_lines: self.number_deleted_lines,
 3171            show_git_diff_gutter: self.show_git_diff_gutter,
 3172            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3173            show_code_actions: self.show_code_actions,
 3174            show_runnables: self.show_runnables,
 3175            show_breakpoints: self.show_breakpoints,
 3176            git_blame_gutter_max_author_length,
 3177            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3178            display_snapshot,
 3179            placeholder_display_snapshot: self
 3180                .placeholder_display_map
 3181                .as_ref()
 3182                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3183            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3184            is_focused: self.focus_handle.is_focused(window),
 3185            current_line_highlight: self
 3186                .current_line_highlight
 3187                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3188            gutter_hovered: self.gutter_hovered,
 3189        }
 3190    }
 3191
 3192    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3193        self.buffer.read(cx).language_at(point, cx)
 3194    }
 3195
 3196    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3197        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3198    }
 3199
 3200    pub fn active_excerpt(
 3201        &self,
 3202        cx: &App,
 3203    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3204        self.buffer
 3205            .read(cx)
 3206            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3207    }
 3208
 3209    pub fn mode(&self) -> &EditorMode {
 3210        &self.mode
 3211    }
 3212
 3213    pub fn set_mode(&mut self, mode: EditorMode) {
 3214        self.mode = mode;
 3215    }
 3216
 3217    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3218        self.collaboration_hub.as_deref()
 3219    }
 3220
 3221    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3222        self.collaboration_hub = Some(hub);
 3223    }
 3224
 3225    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3226        self.in_project_search = in_project_search;
 3227    }
 3228
 3229    pub fn set_custom_context_menu(
 3230        &mut self,
 3231        f: impl 'static
 3232        + Fn(
 3233            &mut Self,
 3234            DisplayPoint,
 3235            &mut Window,
 3236            &mut Context<Self>,
 3237        ) -> Option<Entity<ui::ContextMenu>>,
 3238    ) {
 3239        self.custom_context_menu = Some(Box::new(f))
 3240    }
 3241
 3242    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3243        self.completion_provider = provider;
 3244    }
 3245
 3246    #[cfg(any(test, feature = "test-support"))]
 3247    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3248        self.completion_provider.clone()
 3249    }
 3250
 3251    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3252        self.semantics_provider.clone()
 3253    }
 3254
 3255    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3256        self.semantics_provider = provider;
 3257    }
 3258
 3259    pub fn set_edit_prediction_provider<T>(
 3260        &mut self,
 3261        provider: Option<Entity<T>>,
 3262        window: &mut Window,
 3263        cx: &mut Context<Self>,
 3264    ) where
 3265        T: EditPredictionDelegate,
 3266    {
 3267        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3268            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3269                if this.focus_handle.is_focused(window) {
 3270                    this.update_visible_edit_prediction(window, cx);
 3271                }
 3272            }),
 3273            provider: Arc::new(provider),
 3274        });
 3275        self.update_edit_prediction_settings(cx);
 3276        self.refresh_edit_prediction(false, false, window, cx);
 3277    }
 3278
 3279    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3280        self.placeholder_display_map
 3281            .as_ref()
 3282            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3283    }
 3284
 3285    pub fn set_placeholder_text(
 3286        &mut self,
 3287        placeholder_text: &str,
 3288        window: &mut Window,
 3289        cx: &mut Context<Self>,
 3290    ) {
 3291        let multibuffer = cx
 3292            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3293
 3294        let style = window.text_style();
 3295
 3296        self.placeholder_display_map = Some(cx.new(|cx| {
 3297            DisplayMap::new(
 3298                multibuffer,
 3299                style.font(),
 3300                style.font_size.to_pixels(window.rem_size()),
 3301                None,
 3302                FILE_HEADER_HEIGHT,
 3303                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3304                Default::default(),
 3305                DiagnosticSeverity::Off,
 3306                cx,
 3307            )
 3308        }));
 3309        cx.notify();
 3310    }
 3311
 3312    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3313        self.cursor_shape = cursor_shape;
 3314
 3315        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3316        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3317
 3318        cx.notify();
 3319    }
 3320
 3321    pub fn cursor_shape(&self) -> CursorShape {
 3322        self.cursor_shape
 3323    }
 3324
 3325    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3326        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3327    }
 3328
 3329    pub fn set_current_line_highlight(
 3330        &mut self,
 3331        current_line_highlight: Option<CurrentLineHighlight>,
 3332    ) {
 3333        self.current_line_highlight = current_line_highlight;
 3334    }
 3335
 3336    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3337        self.collapse_matches = collapse_matches;
 3338    }
 3339
 3340    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3341        if self.collapse_matches {
 3342            return range.start..range.start;
 3343        }
 3344        range.clone()
 3345    }
 3346
 3347    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3348        self.display_map.read(cx).clip_at_line_ends
 3349    }
 3350
 3351    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3352        if self.display_map.read(cx).clip_at_line_ends != clip {
 3353            self.display_map
 3354                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3355        }
 3356    }
 3357
 3358    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3359        self.input_enabled = input_enabled;
 3360    }
 3361
 3362    pub fn set_edit_predictions_hidden_for_vim_mode(
 3363        &mut self,
 3364        hidden: bool,
 3365        window: &mut Window,
 3366        cx: &mut Context<Self>,
 3367    ) {
 3368        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3369            self.edit_predictions_hidden_for_vim_mode = hidden;
 3370            if hidden {
 3371                self.update_visible_edit_prediction(window, cx);
 3372            } else {
 3373                self.refresh_edit_prediction(true, false, window, cx);
 3374            }
 3375        }
 3376    }
 3377
 3378    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3379        self.menu_edit_predictions_policy = value;
 3380    }
 3381
 3382    pub fn set_autoindent(&mut self, autoindent: bool) {
 3383        if autoindent {
 3384            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3385        } else {
 3386            self.autoindent_mode = None;
 3387        }
 3388    }
 3389
 3390    pub fn capability(&self, cx: &App) -> Capability {
 3391        if self.read_only {
 3392            Capability::ReadOnly
 3393        } else {
 3394            self.buffer.read(cx).capability()
 3395        }
 3396    }
 3397
 3398    pub fn read_only(&self, cx: &App) -> bool {
 3399        self.read_only || self.buffer.read(cx).read_only()
 3400    }
 3401
 3402    pub fn set_read_only(&mut self, read_only: bool) {
 3403        self.read_only = read_only;
 3404    }
 3405
 3406    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3407        self.use_autoclose = autoclose;
 3408    }
 3409
 3410    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3411        self.use_auto_surround = auto_surround;
 3412    }
 3413
 3414    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3415        self.auto_replace_emoji_shortcode = auto_replace;
 3416    }
 3417
 3418    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3419        self.buffer_serialization = should_serialize.then(|| {
 3420            BufferSerialization::new(
 3421                ProjectSettings::get_global(cx)
 3422                    .session
 3423                    .restore_unsaved_buffers,
 3424            )
 3425        })
 3426    }
 3427
 3428    fn should_serialize_buffer(&self) -> bool {
 3429        self.buffer_serialization.is_some()
 3430    }
 3431
 3432    pub fn toggle_edit_predictions(
 3433        &mut self,
 3434        _: &ToggleEditPrediction,
 3435        window: &mut Window,
 3436        cx: &mut Context<Self>,
 3437    ) {
 3438        if self.show_edit_predictions_override.is_some() {
 3439            self.set_show_edit_predictions(None, window, cx);
 3440        } else {
 3441            let show_edit_predictions = !self.edit_predictions_enabled();
 3442            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3443        }
 3444    }
 3445
 3446    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3447        self.show_completions_on_input_override = show_completions_on_input;
 3448    }
 3449
 3450    pub fn set_show_edit_predictions(
 3451        &mut self,
 3452        show_edit_predictions: Option<bool>,
 3453        window: &mut Window,
 3454        cx: &mut Context<Self>,
 3455    ) {
 3456        self.show_edit_predictions_override = show_edit_predictions;
 3457        self.update_edit_prediction_settings(cx);
 3458
 3459        if let Some(false) = show_edit_predictions {
 3460            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3461        } else {
 3462            self.refresh_edit_prediction(false, true, window, cx);
 3463        }
 3464    }
 3465
 3466    fn edit_predictions_disabled_in_scope(
 3467        &self,
 3468        buffer: &Entity<Buffer>,
 3469        buffer_position: language::Anchor,
 3470        cx: &App,
 3471    ) -> bool {
 3472        let snapshot = buffer.read(cx).snapshot();
 3473        let settings = snapshot.settings_at(buffer_position, cx);
 3474
 3475        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3476            return false;
 3477        };
 3478
 3479        scope.override_name().is_some_and(|scope_name| {
 3480            settings
 3481                .edit_predictions_disabled_in
 3482                .iter()
 3483                .any(|s| s == scope_name)
 3484        })
 3485    }
 3486
 3487    pub fn set_use_modal_editing(&mut self, to: bool) {
 3488        self.use_modal_editing = to;
 3489    }
 3490
 3491    pub fn use_modal_editing(&self) -> bool {
 3492        self.use_modal_editing
 3493    }
 3494
 3495    fn selections_did_change(
 3496        &mut self,
 3497        local: bool,
 3498        old_cursor_position: &Anchor,
 3499        effects: SelectionEffects,
 3500        window: &mut Window,
 3501        cx: &mut Context<Self>,
 3502    ) {
 3503        window.invalidate_character_coordinates();
 3504
 3505        // Copy selections to primary selection buffer
 3506        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3507        if local {
 3508            let selections = self
 3509                .selections
 3510                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3511            let buffer_handle = self.buffer.read(cx).read(cx);
 3512
 3513            let mut text = String::new();
 3514            for (index, selection) in selections.iter().enumerate() {
 3515                let text_for_selection = buffer_handle
 3516                    .text_for_range(selection.start..selection.end)
 3517                    .collect::<String>();
 3518
 3519                text.push_str(&text_for_selection);
 3520                if index != selections.len() - 1 {
 3521                    text.push('\n');
 3522                }
 3523            }
 3524
 3525            if !text.is_empty() {
 3526                cx.write_to_primary(ClipboardItem::new_string(text));
 3527            }
 3528        }
 3529
 3530        let selection_anchors = self.selections.disjoint_anchors_arc();
 3531
 3532        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3533            self.buffer.update(cx, |buffer, cx| {
 3534                buffer.set_active_selections(
 3535                    &selection_anchors,
 3536                    self.selections.line_mode(),
 3537                    self.cursor_shape,
 3538                    cx,
 3539                )
 3540            });
 3541        }
 3542        let display_map = self
 3543            .display_map
 3544            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3545        let buffer = display_map.buffer_snapshot();
 3546        if self.selections.count() == 1 {
 3547            self.add_selections_state = None;
 3548        }
 3549        self.select_next_state = None;
 3550        self.select_prev_state = None;
 3551        self.select_syntax_node_history.try_clear();
 3552        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3553        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3554        self.take_rename(false, window, cx);
 3555
 3556        let newest_selection = self.selections.newest_anchor();
 3557        let new_cursor_position = newest_selection.head();
 3558        let selection_start = newest_selection.start;
 3559
 3560        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3561            self.push_to_nav_history(
 3562                *old_cursor_position,
 3563                Some(new_cursor_position.to_point(buffer)),
 3564                false,
 3565                effects.nav_history == Some(true),
 3566                cx,
 3567            );
 3568        }
 3569
 3570        if local {
 3571            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3572                self.register_buffer(buffer_id, cx);
 3573            }
 3574
 3575            let mut context_menu = self.context_menu.borrow_mut();
 3576            let completion_menu = match context_menu.as_ref() {
 3577                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3578                Some(CodeContextMenu::CodeActions(_)) => {
 3579                    *context_menu = None;
 3580                    None
 3581                }
 3582                None => None,
 3583            };
 3584            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3585            drop(context_menu);
 3586
 3587            if effects.completions
 3588                && let Some(completion_position) = completion_position
 3589            {
 3590                let start_offset = selection_start.to_offset(buffer);
 3591                let position_matches = start_offset == completion_position.to_offset(buffer);
 3592                let continue_showing = if let Some((snap, ..)) =
 3593                    buffer.point_to_buffer_offset(completion_position)
 3594                    && !snap.capability.editable()
 3595                {
 3596                    false
 3597                } else if position_matches {
 3598                    if self.snippet_stack.is_empty() {
 3599                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3600                            == Some(CharKind::Word)
 3601                    } else {
 3602                        // Snippet choices can be shown even when the cursor is in whitespace.
 3603                        // Dismissing the menu with actions like backspace is handled by
 3604                        // invalidation regions.
 3605                        true
 3606                    }
 3607                } else {
 3608                    false
 3609                };
 3610
 3611                if continue_showing {
 3612                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3613                } else {
 3614                    self.hide_context_menu(window, cx);
 3615                }
 3616            }
 3617
 3618            hide_hover(self, cx);
 3619
 3620            if old_cursor_position.to_display_point(&display_map).row()
 3621                != new_cursor_position.to_display_point(&display_map).row()
 3622            {
 3623                self.available_code_actions.take();
 3624            }
 3625            self.refresh_code_actions(window, cx);
 3626            self.refresh_document_highlights(cx);
 3627            refresh_linked_ranges(self, window, cx);
 3628
 3629            self.refresh_selected_text_highlights(false, window, cx);
 3630            self.refresh_matching_bracket_highlights(&display_map, cx);
 3631            self.refresh_outline_symbols_at_cursor(cx);
 3632            self.update_visible_edit_prediction(window, cx);
 3633            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3634            self.inline_blame_popover.take();
 3635            if self.git_blame_inline_enabled {
 3636                self.start_inline_blame_timer(window, cx);
 3637            }
 3638        }
 3639
 3640        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3641
 3642        if local && !self.suppress_selection_callback {
 3643            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3644                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3645                callback(cursor_position, window, cx);
 3646            }
 3647        }
 3648
 3649        cx.emit(EditorEvent::SelectionsChanged { local });
 3650
 3651        let selections = &self.selections.disjoint_anchors_arc();
 3652        if selections.len() == 1 {
 3653            cx.emit(SearchEvent::ActiveMatchChanged)
 3654        }
 3655        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3656            let inmemory_selections = selections
 3657                .iter()
 3658                .map(|s| {
 3659                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3660                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3661                })
 3662                .collect();
 3663            self.update_restoration_data(cx, |data| {
 3664                data.selections = inmemory_selections;
 3665            });
 3666
 3667            if WorkspaceSettings::get(None, cx).restore_on_startup
 3668                != RestoreOnStartupBehavior::EmptyTab
 3669                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3670            {
 3671                let snapshot = self.buffer().read(cx).snapshot(cx);
 3672                let selections = selections.clone();
 3673                let background_executor = cx.background_executor().clone();
 3674                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3675                self.serialize_selections = cx.background_spawn(async move {
 3676                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3677                    let db_selections = selections
 3678                        .iter()
 3679                        .map(|selection| {
 3680                            (
 3681                                selection.start.to_offset(&snapshot).0,
 3682                                selection.end.to_offset(&snapshot).0,
 3683                            )
 3684                        })
 3685                        .collect();
 3686
 3687                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3688                        .await
 3689                        .with_context(|| {
 3690                            format!(
 3691                                "persisting editor selections for editor {editor_id}, \
 3692                                workspace {workspace_id:?}"
 3693                            )
 3694                        })
 3695                        .log_err();
 3696                });
 3697            }
 3698        }
 3699
 3700        cx.notify();
 3701    }
 3702
 3703    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3704        use text::ToOffset as _;
 3705        use text::ToPoint as _;
 3706
 3707        if self.mode.is_minimap()
 3708            || WorkspaceSettings::get(None, cx).restore_on_startup
 3709                == RestoreOnStartupBehavior::EmptyTab
 3710        {
 3711            return;
 3712        }
 3713
 3714        if !self.buffer().read(cx).is_singleton() {
 3715            return;
 3716        }
 3717
 3718        let display_snapshot = self
 3719            .display_map
 3720            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3721        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3722            return;
 3723        };
 3724        let inmemory_folds = display_snapshot
 3725            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3726            .map(|fold| {
 3727                fold.range.start.text_anchor.to_point(&snapshot)
 3728                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3729            })
 3730            .collect();
 3731        self.update_restoration_data(cx, |data| {
 3732            data.folds = inmemory_folds;
 3733        });
 3734
 3735        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3736            return;
 3737        };
 3738
 3739        // Get file path for path-based fold storage (survives tab close)
 3740        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3741            project::File::from_dyn(buffer.read(cx).file())
 3742                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3743        }) else {
 3744            return;
 3745        };
 3746
 3747        let background_executor = cx.background_executor().clone();
 3748        const FINGERPRINT_LEN: usize = 32;
 3749        let db_folds = display_snapshot
 3750            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3751            .map(|fold| {
 3752                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3753                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3754
 3755                // Extract fingerprints - content at fold boundaries for validation on restore
 3756                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3757                // content that might change independently.
 3758                // start_fp: first min(32, fold_len) bytes of fold content
 3759                // end_fp: last min(32, fold_len) bytes of fold content
 3760                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3761                let fold_len = end - start;
 3762                let start_fp_end = snapshot
 3763                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3764                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3765                let end_fp_start = snapshot
 3766                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3767                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3768
 3769                (start, end, start_fp, end_fp)
 3770            })
 3771            .collect::<Vec<_>>();
 3772        self.serialize_folds = cx.background_spawn(async move {
 3773            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3774            if db_folds.is_empty() {
 3775                // No folds - delete any persisted folds for this file
 3776                DB.delete_file_folds(workspace_id, file_path)
 3777                    .await
 3778                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3779                    .log_err();
 3780            } else {
 3781                DB.save_file_folds(workspace_id, file_path, db_folds)
 3782                    .await
 3783                    .with_context(|| {
 3784                        format!("persisting file folds for workspace {workspace_id:?}")
 3785                    })
 3786                    .log_err();
 3787            }
 3788        });
 3789    }
 3790
 3791    pub fn sync_selections(
 3792        &mut self,
 3793        other: Entity<Editor>,
 3794        cx: &mut Context<Self>,
 3795    ) -> gpui::Subscription {
 3796        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3797        if !other_selections.is_empty() {
 3798            self.selections
 3799                .change_with(&self.display_snapshot(cx), |selections| {
 3800                    selections.select_anchors(other_selections);
 3801                });
 3802        }
 3803
 3804        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3805            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3806                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3807                if other_selections.is_empty() {
 3808                    return;
 3809                }
 3810                let snapshot = this.display_snapshot(cx);
 3811                this.selections.change_with(&snapshot, |selections| {
 3812                    selections.select_anchors(other_selections);
 3813                });
 3814            }
 3815        });
 3816
 3817        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3818            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3819                let these_selections = this.selections.disjoint_anchors().to_vec();
 3820                if these_selections.is_empty() {
 3821                    return;
 3822                }
 3823                other.update(cx, |other_editor, cx| {
 3824                    let snapshot = other_editor.display_snapshot(cx);
 3825                    other_editor
 3826                        .selections
 3827                        .change_with(&snapshot, |selections| {
 3828                            selections.select_anchors(these_selections);
 3829                        })
 3830                });
 3831            }
 3832        });
 3833
 3834        Subscription::join(other_subscription, this_subscription)
 3835    }
 3836
 3837    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3838        if self.buffer().read(cx).is_singleton() {
 3839            return;
 3840        }
 3841        let snapshot = self.buffer.read(cx).snapshot(cx);
 3842        let buffer_ids: HashSet<BufferId> = self
 3843            .selections
 3844            .disjoint_anchor_ranges()
 3845            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3846            .collect();
 3847        for buffer_id in buffer_ids {
 3848            self.unfold_buffer(buffer_id, cx);
 3849        }
 3850    }
 3851
 3852    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3853    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3854    /// effects of selection change occur at the end of the transaction.
 3855    pub fn change_selections<R>(
 3856        &mut self,
 3857        effects: SelectionEffects,
 3858        window: &mut Window,
 3859        cx: &mut Context<Self>,
 3860        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3861    ) -> R {
 3862        let snapshot = self.display_snapshot(cx);
 3863        if let Some(state) = &mut self.deferred_selection_effects_state {
 3864            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3865            state.effects.completions = effects.completions;
 3866            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3867            let (changed, result) = self.selections.change_with(&snapshot, change);
 3868            state.changed |= changed;
 3869            return result;
 3870        }
 3871        let mut state = DeferredSelectionEffectsState {
 3872            changed: false,
 3873            effects,
 3874            old_cursor_position: self.selections.newest_anchor().head(),
 3875            history_entry: SelectionHistoryEntry {
 3876                selections: self.selections.disjoint_anchors_arc(),
 3877                select_next_state: self.select_next_state.clone(),
 3878                select_prev_state: self.select_prev_state.clone(),
 3879                add_selections_state: self.add_selections_state.clone(),
 3880            },
 3881        };
 3882        let (changed, result) = self.selections.change_with(&snapshot, change);
 3883        state.changed = state.changed || changed;
 3884        if self.defer_selection_effects {
 3885            self.deferred_selection_effects_state = Some(state);
 3886        } else {
 3887            self.apply_selection_effects(state, window, cx);
 3888        }
 3889        result
 3890    }
 3891
 3892    /// Defers the effects of selection change, so that the effects of multiple calls to
 3893    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3894    /// to selection history and the state of popovers based on selection position aren't
 3895    /// erroneously updated.
 3896    pub fn with_selection_effects_deferred<R>(
 3897        &mut self,
 3898        window: &mut Window,
 3899        cx: &mut Context<Self>,
 3900        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3901    ) -> R {
 3902        let already_deferred = self.defer_selection_effects;
 3903        self.defer_selection_effects = true;
 3904        let result = update(self, window, cx);
 3905        if !already_deferred {
 3906            self.defer_selection_effects = false;
 3907            if let Some(state) = self.deferred_selection_effects_state.take() {
 3908                self.apply_selection_effects(state, window, cx);
 3909            }
 3910        }
 3911        result
 3912    }
 3913
 3914    fn apply_selection_effects(
 3915        &mut self,
 3916        state: DeferredSelectionEffectsState,
 3917        window: &mut Window,
 3918        cx: &mut Context<Self>,
 3919    ) {
 3920        if state.changed {
 3921            self.selection_history.push(state.history_entry);
 3922
 3923            if let Some(autoscroll) = state.effects.scroll {
 3924                self.request_autoscroll(autoscroll, cx);
 3925            }
 3926
 3927            let old_cursor_position = &state.old_cursor_position;
 3928
 3929            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3930
 3931            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3932                self.show_signature_help_auto(window, cx);
 3933            }
 3934        }
 3935    }
 3936
 3937    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3938    where
 3939        I: IntoIterator<Item = (Range<S>, T)>,
 3940        S: ToOffset,
 3941        T: Into<Arc<str>>,
 3942    {
 3943        if self.read_only(cx) {
 3944            return;
 3945        }
 3946
 3947        self.buffer
 3948            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3949    }
 3950
 3951    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3952    where
 3953        I: IntoIterator<Item = (Range<S>, T)>,
 3954        S: ToOffset,
 3955        T: Into<Arc<str>>,
 3956    {
 3957        if self.read_only(cx) {
 3958            return;
 3959        }
 3960
 3961        self.buffer.update(cx, |buffer, cx| {
 3962            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3963        });
 3964    }
 3965
 3966    pub fn edit_with_block_indent<I, S, T>(
 3967        &mut self,
 3968        edits: I,
 3969        original_indent_columns: Vec<Option<u32>>,
 3970        cx: &mut Context<Self>,
 3971    ) where
 3972        I: IntoIterator<Item = (Range<S>, T)>,
 3973        S: ToOffset,
 3974        T: Into<Arc<str>>,
 3975    {
 3976        if self.read_only(cx) {
 3977            return;
 3978        }
 3979
 3980        self.buffer.update(cx, |buffer, cx| {
 3981            buffer.edit(
 3982                edits,
 3983                Some(AutoindentMode::Block {
 3984                    original_indent_columns,
 3985                }),
 3986                cx,
 3987            )
 3988        });
 3989    }
 3990
 3991    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3992        self.hide_context_menu(window, cx);
 3993
 3994        match phase {
 3995            SelectPhase::Begin {
 3996                position,
 3997                add,
 3998                click_count,
 3999            } => self.begin_selection(position, add, click_count, window, cx),
 4000            SelectPhase::BeginColumnar {
 4001                position,
 4002                goal_column,
 4003                reset,
 4004                mode,
 4005            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4006            SelectPhase::Extend {
 4007                position,
 4008                click_count,
 4009            } => self.extend_selection(position, click_count, window, cx),
 4010            SelectPhase::Update {
 4011                position,
 4012                goal_column,
 4013                scroll_delta,
 4014            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4015            SelectPhase::End => self.end_selection(window, cx),
 4016        }
 4017    }
 4018
 4019    fn extend_selection(
 4020        &mut self,
 4021        position: DisplayPoint,
 4022        click_count: usize,
 4023        window: &mut Window,
 4024        cx: &mut Context<Self>,
 4025    ) {
 4026        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4027        let tail = self
 4028            .selections
 4029            .newest::<MultiBufferOffset>(&display_map)
 4030            .tail();
 4031        let click_count = click_count.max(match self.selections.select_mode() {
 4032            SelectMode::Character => 1,
 4033            SelectMode::Word(_) => 2,
 4034            SelectMode::Line(_) => 3,
 4035            SelectMode::All => 4,
 4036        });
 4037        self.begin_selection(position, false, click_count, window, cx);
 4038
 4039        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4040
 4041        let current_selection = match self.selections.select_mode() {
 4042            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4043            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4044        };
 4045
 4046        let mut pending_selection = self
 4047            .selections
 4048            .pending_anchor()
 4049            .cloned()
 4050            .expect("extend_selection not called with pending selection");
 4051
 4052        if pending_selection
 4053            .start
 4054            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4055            == Ordering::Greater
 4056        {
 4057            pending_selection.start = current_selection.start;
 4058        }
 4059        if pending_selection
 4060            .end
 4061            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4062            == Ordering::Less
 4063        {
 4064            pending_selection.end = current_selection.end;
 4065            pending_selection.reversed = true;
 4066        }
 4067
 4068        let mut pending_mode = self.selections.pending_mode().unwrap();
 4069        match &mut pending_mode {
 4070            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4071            _ => {}
 4072        }
 4073
 4074        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4075            SelectionEffects::scroll(Autoscroll::fit())
 4076        } else {
 4077            SelectionEffects::no_scroll()
 4078        };
 4079
 4080        self.change_selections(effects, window, cx, |s| {
 4081            s.set_pending(pending_selection.clone(), pending_mode);
 4082            s.set_is_extending(true);
 4083        });
 4084    }
 4085
 4086    fn begin_selection(
 4087        &mut self,
 4088        position: DisplayPoint,
 4089        add: bool,
 4090        click_count: usize,
 4091        window: &mut Window,
 4092        cx: &mut Context<Self>,
 4093    ) {
 4094        if !self.focus_handle.is_focused(window) {
 4095            self.last_focused_descendant = None;
 4096            window.focus(&self.focus_handle, cx);
 4097        }
 4098
 4099        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4100        let buffer = display_map.buffer_snapshot();
 4101        let position = display_map.clip_point(position, Bias::Left);
 4102
 4103        let start;
 4104        let end;
 4105        let mode;
 4106        let mut auto_scroll;
 4107        match click_count {
 4108            1 => {
 4109                start = buffer.anchor_before(position.to_point(&display_map));
 4110                end = start;
 4111                mode = SelectMode::Character;
 4112                auto_scroll = true;
 4113            }
 4114            2 => {
 4115                let position = display_map
 4116                    .clip_point(position, Bias::Left)
 4117                    .to_offset(&display_map, Bias::Left);
 4118                let (range, _) = buffer.surrounding_word(position, None);
 4119                start = buffer.anchor_before(range.start);
 4120                end = buffer.anchor_before(range.end);
 4121                mode = SelectMode::Word(start..end);
 4122                auto_scroll = true;
 4123            }
 4124            3 => {
 4125                let position = display_map
 4126                    .clip_point(position, Bias::Left)
 4127                    .to_point(&display_map);
 4128                let line_start = display_map.prev_line_boundary(position).0;
 4129                let next_line_start = buffer.clip_point(
 4130                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4131                    Bias::Left,
 4132                );
 4133                start = buffer.anchor_before(line_start);
 4134                end = buffer.anchor_before(next_line_start);
 4135                mode = SelectMode::Line(start..end);
 4136                auto_scroll = true;
 4137            }
 4138            _ => {
 4139                start = buffer.anchor_before(MultiBufferOffset(0));
 4140                end = buffer.anchor_before(buffer.len());
 4141                mode = SelectMode::All;
 4142                auto_scroll = false;
 4143            }
 4144        }
 4145        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4146
 4147        let point_to_delete: Option<usize> = {
 4148            let selected_points: Vec<Selection<Point>> =
 4149                self.selections.disjoint_in_range(start..end, &display_map);
 4150
 4151            if !add || click_count > 1 {
 4152                None
 4153            } else if !selected_points.is_empty() {
 4154                Some(selected_points[0].id)
 4155            } else {
 4156                let clicked_point_already_selected =
 4157                    self.selections.disjoint_anchors().iter().find(|selection| {
 4158                        selection.start.to_point(buffer) == start.to_point(buffer)
 4159                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4160                    });
 4161
 4162                clicked_point_already_selected.map(|selection| selection.id)
 4163            }
 4164        };
 4165
 4166        let selections_count = self.selections.count();
 4167        let effects = if auto_scroll {
 4168            SelectionEffects::default()
 4169        } else {
 4170            SelectionEffects::no_scroll()
 4171        };
 4172
 4173        self.change_selections(effects, window, cx, |s| {
 4174            if let Some(point_to_delete) = point_to_delete {
 4175                s.delete(point_to_delete);
 4176
 4177                if selections_count == 1 {
 4178                    s.set_pending_anchor_range(start..end, mode);
 4179                }
 4180            } else {
 4181                if !add {
 4182                    s.clear_disjoint();
 4183                }
 4184
 4185                s.set_pending_anchor_range(start..end, mode);
 4186            }
 4187        });
 4188    }
 4189
 4190    fn begin_columnar_selection(
 4191        &mut self,
 4192        position: DisplayPoint,
 4193        goal_column: u32,
 4194        reset: bool,
 4195        mode: ColumnarMode,
 4196        window: &mut Window,
 4197        cx: &mut Context<Self>,
 4198    ) {
 4199        if !self.focus_handle.is_focused(window) {
 4200            self.last_focused_descendant = None;
 4201            window.focus(&self.focus_handle, cx);
 4202        }
 4203
 4204        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4205
 4206        if reset {
 4207            let pointer_position = display_map
 4208                .buffer_snapshot()
 4209                .anchor_before(position.to_point(&display_map));
 4210
 4211            self.change_selections(
 4212                SelectionEffects::scroll(Autoscroll::newest()),
 4213                window,
 4214                cx,
 4215                |s| {
 4216                    s.clear_disjoint();
 4217                    s.set_pending_anchor_range(
 4218                        pointer_position..pointer_position,
 4219                        SelectMode::Character,
 4220                    );
 4221                },
 4222            );
 4223        };
 4224
 4225        let tail = self.selections.newest::<Point>(&display_map).tail();
 4226        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4227        self.columnar_selection_state = match mode {
 4228            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4229                selection_tail: selection_anchor,
 4230                display_point: if reset {
 4231                    if position.column() != goal_column {
 4232                        Some(DisplayPoint::new(position.row(), goal_column))
 4233                    } else {
 4234                        None
 4235                    }
 4236                } else {
 4237                    None
 4238                },
 4239            }),
 4240            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4241                selection_tail: selection_anchor,
 4242            }),
 4243        };
 4244
 4245        if !reset {
 4246            self.select_columns(position, goal_column, &display_map, window, cx);
 4247        }
 4248    }
 4249
 4250    fn update_selection(
 4251        &mut self,
 4252        position: DisplayPoint,
 4253        goal_column: u32,
 4254        scroll_delta: gpui::Point<f32>,
 4255        window: &mut Window,
 4256        cx: &mut Context<Self>,
 4257    ) {
 4258        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4259
 4260        if self.columnar_selection_state.is_some() {
 4261            self.select_columns(position, goal_column, &display_map, window, cx);
 4262        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4263            let buffer = display_map.buffer_snapshot();
 4264            let head;
 4265            let tail;
 4266            let mode = self.selections.pending_mode().unwrap();
 4267            match &mode {
 4268                SelectMode::Character => {
 4269                    head = position.to_point(&display_map);
 4270                    tail = pending.tail().to_point(buffer);
 4271                }
 4272                SelectMode::Word(original_range) => {
 4273                    let offset = display_map
 4274                        .clip_point(position, Bias::Left)
 4275                        .to_offset(&display_map, Bias::Left);
 4276                    let original_range = original_range.to_offset(buffer);
 4277
 4278                    let head_offset = if buffer.is_inside_word(offset, None)
 4279                        || original_range.contains(&offset)
 4280                    {
 4281                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4282                        if word_range.start < original_range.start {
 4283                            word_range.start
 4284                        } else {
 4285                            word_range.end
 4286                        }
 4287                    } else {
 4288                        offset
 4289                    };
 4290
 4291                    head = head_offset.to_point(buffer);
 4292                    if head_offset <= original_range.start {
 4293                        tail = original_range.end.to_point(buffer);
 4294                    } else {
 4295                        tail = original_range.start.to_point(buffer);
 4296                    }
 4297                }
 4298                SelectMode::Line(original_range) => {
 4299                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4300
 4301                    let position = display_map
 4302                        .clip_point(position, Bias::Left)
 4303                        .to_point(&display_map);
 4304                    let line_start = display_map.prev_line_boundary(position).0;
 4305                    let next_line_start = buffer.clip_point(
 4306                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4307                        Bias::Left,
 4308                    );
 4309
 4310                    if line_start < original_range.start {
 4311                        head = line_start
 4312                    } else {
 4313                        head = next_line_start
 4314                    }
 4315
 4316                    if head <= original_range.start {
 4317                        tail = original_range.end;
 4318                    } else {
 4319                        tail = original_range.start;
 4320                    }
 4321                }
 4322                SelectMode::All => {
 4323                    return;
 4324                }
 4325            };
 4326
 4327            if head < tail {
 4328                pending.start = buffer.anchor_before(head);
 4329                pending.end = buffer.anchor_before(tail);
 4330                pending.reversed = true;
 4331            } else {
 4332                pending.start = buffer.anchor_before(tail);
 4333                pending.end = buffer.anchor_before(head);
 4334                pending.reversed = false;
 4335            }
 4336
 4337            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4338                s.set_pending(pending.clone(), mode);
 4339            });
 4340        } else {
 4341            log::error!("update_selection dispatched with no pending selection");
 4342            return;
 4343        }
 4344
 4345        self.apply_scroll_delta(scroll_delta, window, cx);
 4346        cx.notify();
 4347    }
 4348
 4349    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4350        self.columnar_selection_state.take();
 4351        if let Some(pending_mode) = self.selections.pending_mode() {
 4352            let selections = self
 4353                .selections
 4354                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4355            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4356                s.select(selections);
 4357                s.clear_pending();
 4358                if s.is_extending() {
 4359                    s.set_is_extending(false);
 4360                } else {
 4361                    s.set_select_mode(pending_mode);
 4362                }
 4363            });
 4364        }
 4365    }
 4366
 4367    fn select_columns(
 4368        &mut self,
 4369        head: DisplayPoint,
 4370        goal_column: u32,
 4371        display_map: &DisplaySnapshot,
 4372        window: &mut Window,
 4373        cx: &mut Context<Self>,
 4374    ) {
 4375        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4376            return;
 4377        };
 4378
 4379        let tail = match columnar_state {
 4380            ColumnarSelectionState::FromMouse {
 4381                selection_tail,
 4382                display_point,
 4383            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4384            ColumnarSelectionState::FromSelection { selection_tail } => {
 4385                selection_tail.to_display_point(display_map)
 4386            }
 4387        };
 4388
 4389        let start_row = cmp::min(tail.row(), head.row());
 4390        let end_row = cmp::max(tail.row(), head.row());
 4391        let start_column = cmp::min(tail.column(), goal_column);
 4392        let end_column = cmp::max(tail.column(), goal_column);
 4393        let reversed = start_column < tail.column();
 4394
 4395        let selection_ranges = (start_row.0..=end_row.0)
 4396            .map(DisplayRow)
 4397            .filter_map(|row| {
 4398                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4399                    || start_column <= display_map.line_len(row))
 4400                    && !display_map.is_block_line(row)
 4401                {
 4402                    let start = display_map
 4403                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4404                        .to_point(display_map);
 4405                    let end = display_map
 4406                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4407                        .to_point(display_map);
 4408                    if reversed {
 4409                        Some(end..start)
 4410                    } else {
 4411                        Some(start..end)
 4412                    }
 4413                } else {
 4414                    None
 4415                }
 4416            })
 4417            .collect::<Vec<_>>();
 4418        if selection_ranges.is_empty() {
 4419            return;
 4420        }
 4421
 4422        let ranges = match columnar_state {
 4423            ColumnarSelectionState::FromMouse { .. } => {
 4424                let mut non_empty_ranges = selection_ranges
 4425                    .iter()
 4426                    .filter(|selection_range| selection_range.start != selection_range.end)
 4427                    .peekable();
 4428                if non_empty_ranges.peek().is_some() {
 4429                    non_empty_ranges.cloned().collect()
 4430                } else {
 4431                    selection_ranges
 4432                }
 4433            }
 4434            _ => selection_ranges,
 4435        };
 4436
 4437        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4438            s.select_ranges(ranges);
 4439        });
 4440        cx.notify();
 4441    }
 4442
 4443    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4444        self.selections
 4445            .all_adjusted(snapshot)
 4446            .iter()
 4447            .any(|selection| !selection.is_empty())
 4448    }
 4449
 4450    pub fn has_pending_nonempty_selection(&self) -> bool {
 4451        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4452            Some(Selection { start, end, .. }) => start != end,
 4453            None => false,
 4454        };
 4455
 4456        pending_nonempty_selection
 4457            || (self.columnar_selection_state.is_some()
 4458                && self.selections.disjoint_anchors().len() > 1)
 4459    }
 4460
 4461    pub fn has_pending_selection(&self) -> bool {
 4462        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4463    }
 4464
 4465    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4466        self.selection_mark_mode = false;
 4467        self.selection_drag_state = SelectionDragState::None;
 4468
 4469        if self.dismiss_menus_and_popups(true, window, cx) {
 4470            cx.notify();
 4471            return;
 4472        }
 4473        if self.clear_expanded_diff_hunks(cx) {
 4474            cx.notify();
 4475            return;
 4476        }
 4477        if self.show_git_blame_gutter {
 4478            self.show_git_blame_gutter = false;
 4479            cx.notify();
 4480            return;
 4481        }
 4482
 4483        if self.mode.is_full()
 4484            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4485        {
 4486            cx.notify();
 4487            return;
 4488        }
 4489
 4490        cx.propagate();
 4491    }
 4492
 4493    pub fn dismiss_menus_and_popups(
 4494        &mut self,
 4495        is_user_requested: bool,
 4496        window: &mut Window,
 4497        cx: &mut Context<Self>,
 4498    ) -> bool {
 4499        let mut dismissed = false;
 4500
 4501        dismissed |= self.take_rename(false, window, cx).is_some();
 4502        dismissed |= self.hide_blame_popover(true, cx);
 4503        dismissed |= hide_hover(self, cx);
 4504        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4505        dismissed |= self.hide_context_menu(window, cx).is_some();
 4506        dismissed |= self.mouse_context_menu.take().is_some();
 4507        dismissed |= is_user_requested
 4508            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4509        dismissed |= self.snippet_stack.pop().is_some();
 4510        if self.diff_review_drag_state.is_some() {
 4511            self.cancel_diff_review_drag(cx);
 4512            dismissed = true;
 4513        }
 4514        if !self.diff_review_overlays.is_empty() {
 4515            self.dismiss_all_diff_review_overlays(cx);
 4516            dismissed = true;
 4517        }
 4518
 4519        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4520            self.dismiss_diagnostics(cx);
 4521            dismissed = true;
 4522        }
 4523
 4524        dismissed
 4525    }
 4526
 4527    fn linked_editing_ranges_for(
 4528        &self,
 4529        selection: Range<text::Anchor>,
 4530        cx: &App,
 4531    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4532        if self.linked_edit_ranges.is_empty() {
 4533            return None;
 4534        }
 4535        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4536            selection.end.buffer_id.and_then(|end_buffer_id| {
 4537                if selection.start.buffer_id != Some(end_buffer_id) {
 4538                    return None;
 4539                }
 4540                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4541                let snapshot = buffer.read(cx).snapshot();
 4542                self.linked_edit_ranges
 4543                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4544                    .map(|ranges| (ranges, snapshot, buffer))
 4545            })?;
 4546        use text::ToOffset as TO;
 4547        // find offset from the start of current range to current cursor position
 4548        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4549
 4550        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4551        let start_difference = start_offset - start_byte_offset;
 4552        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4553        let end_difference = end_offset - start_byte_offset;
 4554
 4555        // Current range has associated linked ranges.
 4556        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4557        for range in linked_ranges.iter() {
 4558            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4559            let end_offset = start_offset + end_difference;
 4560            let start_offset = start_offset + start_difference;
 4561            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4562                continue;
 4563            }
 4564            if self.selections.disjoint_anchor_ranges().any(|s| {
 4565                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4566                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4567                {
 4568                    return false;
 4569                }
 4570                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4571                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4572            }) {
 4573                continue;
 4574            }
 4575            let start = buffer_snapshot.anchor_after(start_offset);
 4576            let end = buffer_snapshot.anchor_after(end_offset);
 4577            linked_edits
 4578                .entry(buffer.clone())
 4579                .or_default()
 4580                .push(start..end);
 4581        }
 4582        Some(linked_edits)
 4583    }
 4584
 4585    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4586        let text: Arc<str> = text.into();
 4587
 4588        if self.read_only(cx) {
 4589            return;
 4590        }
 4591
 4592        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4593
 4594        self.unfold_buffers_with_selections(cx);
 4595
 4596        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4597        let mut bracket_inserted = false;
 4598        let mut edits = Vec::new();
 4599        let mut linked_edits = LinkedEdits::new();
 4600        let mut new_selections = Vec::with_capacity(selections.len());
 4601        let mut new_autoclose_regions = Vec::new();
 4602        let snapshot = self.buffer.read(cx).read(cx);
 4603        let mut clear_linked_edit_ranges = false;
 4604        let mut all_selections_read_only = true;
 4605        let mut has_adjacent_edits = false;
 4606        let mut in_adjacent_group = false;
 4607
 4608        let mut regions = self
 4609            .selections_with_autoclose_regions(selections, &snapshot)
 4610            .peekable();
 4611
 4612        while let Some((selection, autoclose_region)) = regions.next() {
 4613            if snapshot
 4614                .point_to_buffer_point(selection.head())
 4615                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4616            {
 4617                continue;
 4618            }
 4619            if snapshot
 4620                .point_to_buffer_point(selection.tail())
 4621                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4622            {
 4623                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4624                continue;
 4625            }
 4626            all_selections_read_only = false;
 4627
 4628            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4629                // Determine if the inserted text matches the opening or closing
 4630                // bracket of any of this language's bracket pairs.
 4631                let mut bracket_pair = None;
 4632                let mut is_bracket_pair_start = false;
 4633                let mut is_bracket_pair_end = false;
 4634                if !text.is_empty() {
 4635                    let mut bracket_pair_matching_end = None;
 4636                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4637                    //  and they are removing the character that triggered IME popup.
 4638                    for (pair, enabled) in scope.brackets() {
 4639                        if !pair.close && !pair.surround {
 4640                            continue;
 4641                        }
 4642
 4643                        if enabled && pair.start.ends_with(text.as_ref()) {
 4644                            let prefix_len = pair.start.len() - text.len();
 4645                            let preceding_text_matches_prefix = prefix_len == 0
 4646                                || (selection.start.column >= (prefix_len as u32)
 4647                                    && snapshot.contains_str_at(
 4648                                        Point::new(
 4649                                            selection.start.row,
 4650                                            selection.start.column - (prefix_len as u32),
 4651                                        ),
 4652                                        &pair.start[..prefix_len],
 4653                                    ));
 4654                            if preceding_text_matches_prefix {
 4655                                bracket_pair = Some(pair.clone());
 4656                                is_bracket_pair_start = true;
 4657                                break;
 4658                            }
 4659                        }
 4660                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4661                        {
 4662                            // take first bracket pair matching end, but don't break in case a later bracket
 4663                            // pair matches start
 4664                            bracket_pair_matching_end = Some(pair.clone());
 4665                        }
 4666                    }
 4667                    if let Some(end) = bracket_pair_matching_end
 4668                        && bracket_pair.is_none()
 4669                    {
 4670                        bracket_pair = Some(end);
 4671                        is_bracket_pair_end = true;
 4672                    }
 4673                }
 4674
 4675                if let Some(bracket_pair) = bracket_pair {
 4676                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4677                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4678                    let auto_surround =
 4679                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4680                    if selection.is_empty() {
 4681                        if is_bracket_pair_start {
 4682                            // If the inserted text is a suffix of an opening bracket and the
 4683                            // selection is preceded by the rest of the opening bracket, then
 4684                            // insert the closing bracket.
 4685                            let following_text_allows_autoclose = snapshot
 4686                                .chars_at(selection.start)
 4687                                .next()
 4688                                .is_none_or(|c| scope.should_autoclose_before(c));
 4689
 4690                            let preceding_text_allows_autoclose = selection.start.column == 0
 4691                                || snapshot
 4692                                    .reversed_chars_at(selection.start)
 4693                                    .next()
 4694                                    .is_none_or(|c| {
 4695                                        bracket_pair.start != bracket_pair.end
 4696                                            || !snapshot
 4697                                                .char_classifier_at(selection.start)
 4698                                                .is_word(c)
 4699                                    });
 4700
 4701                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4702                                && bracket_pair.start.len() == 1
 4703                            {
 4704                                let target = bracket_pair.start.chars().next().unwrap();
 4705                                let mut byte_offset = 0u32;
 4706                                let current_line_count = snapshot
 4707                                    .reversed_chars_at(selection.start)
 4708                                    .take_while(|&c| c != '\n')
 4709                                    .filter(|c| {
 4710                                        byte_offset += c.len_utf8() as u32;
 4711                                        if *c != target {
 4712                                            return false;
 4713                                        }
 4714
 4715                                        let point = Point::new(
 4716                                            selection.start.row,
 4717                                            selection.start.column.saturating_sub(byte_offset),
 4718                                        );
 4719
 4720                                        let is_enabled = snapshot
 4721                                            .language_scope_at(point)
 4722                                            .and_then(|scope| {
 4723                                                scope
 4724                                                    .brackets()
 4725                                                    .find(|(pair, _)| {
 4726                                                        pair.start == bracket_pair.start
 4727                                                    })
 4728                                                    .map(|(_, enabled)| enabled)
 4729                                            })
 4730                                            .unwrap_or(true);
 4731
 4732                                        let is_delimiter = snapshot
 4733                                            .language_scope_at(Point::new(
 4734                                                point.row,
 4735                                                point.column + 1,
 4736                                            ))
 4737                                            .and_then(|scope| {
 4738                                                scope
 4739                                                    .brackets()
 4740                                                    .find(|(pair, _)| {
 4741                                                        pair.start == bracket_pair.start
 4742                                                    })
 4743                                                    .map(|(_, enabled)| !enabled)
 4744                                            })
 4745                                            .unwrap_or(false);
 4746
 4747                                        is_enabled && !is_delimiter
 4748                                    })
 4749                                    .count();
 4750                                current_line_count % 2 == 1
 4751                            } else {
 4752                                false
 4753                            };
 4754
 4755                            if autoclose
 4756                                && bracket_pair.close
 4757                                && following_text_allows_autoclose
 4758                                && preceding_text_allows_autoclose
 4759                                && !is_closing_quote
 4760                            {
 4761                                let anchor = snapshot.anchor_before(selection.end);
 4762                                new_selections.push((selection.map(|_| anchor), text.len()));
 4763                                new_autoclose_regions.push((
 4764                                    anchor,
 4765                                    text.len(),
 4766                                    selection.id,
 4767                                    bracket_pair.clone(),
 4768                                ));
 4769                                edits.push((
 4770                                    selection.range(),
 4771                                    format!("{}{}", text, bracket_pair.end).into(),
 4772                                ));
 4773                                bracket_inserted = true;
 4774                                continue;
 4775                            }
 4776                        }
 4777
 4778                        if let Some(region) = autoclose_region {
 4779                            // If the selection is followed by an auto-inserted closing bracket,
 4780                            // then don't insert that closing bracket again; just move the selection
 4781                            // past the closing bracket.
 4782                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4783                                && text.as_ref() == region.pair.end.as_str()
 4784                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4785                            if should_skip {
 4786                                let anchor = snapshot.anchor_after(selection.end);
 4787                                new_selections
 4788                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4789                                continue;
 4790                            }
 4791                        }
 4792
 4793                        let always_treat_brackets_as_autoclosed = snapshot
 4794                            .language_settings_at(selection.start, cx)
 4795                            .always_treat_brackets_as_autoclosed;
 4796                        if always_treat_brackets_as_autoclosed
 4797                            && is_bracket_pair_end
 4798                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4799                        {
 4800                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4801                            // and the inserted text is a closing bracket and the selection is followed
 4802                            // by the closing bracket then move the selection past the closing bracket.
 4803                            let anchor = snapshot.anchor_after(selection.end);
 4804                            new_selections.push((selection.map(|_| anchor), text.len()));
 4805                            continue;
 4806                        }
 4807                    }
 4808                    // If an opening bracket is 1 character long and is typed while
 4809                    // text is selected, then surround that text with the bracket pair.
 4810                    else if auto_surround
 4811                        && bracket_pair.surround
 4812                        && is_bracket_pair_start
 4813                        && bracket_pair.start.chars().count() == 1
 4814                    {
 4815                        edits.push((selection.start..selection.start, text.clone()));
 4816                        edits.push((
 4817                            selection.end..selection.end,
 4818                            bracket_pair.end.as_str().into(),
 4819                        ));
 4820                        bracket_inserted = true;
 4821                        new_selections.push((
 4822                            Selection {
 4823                                id: selection.id,
 4824                                start: snapshot.anchor_after(selection.start),
 4825                                end: snapshot.anchor_before(selection.end),
 4826                                reversed: selection.reversed,
 4827                                goal: selection.goal,
 4828                            },
 4829                            0,
 4830                        ));
 4831                        continue;
 4832                    }
 4833                }
 4834            }
 4835
 4836            if self.auto_replace_emoji_shortcode
 4837                && selection.is_empty()
 4838                && text.as_ref().ends_with(':')
 4839                && let Some(possible_emoji_short_code) =
 4840                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4841                && !possible_emoji_short_code.is_empty()
 4842                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4843            {
 4844                let emoji_shortcode_start = Point::new(
 4845                    selection.start.row,
 4846                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4847                );
 4848
 4849                // Remove shortcode from buffer
 4850                edits.push((
 4851                    emoji_shortcode_start..selection.start,
 4852                    "".to_string().into(),
 4853                ));
 4854                new_selections.push((
 4855                    Selection {
 4856                        id: selection.id,
 4857                        start: snapshot.anchor_after(emoji_shortcode_start),
 4858                        end: snapshot.anchor_before(selection.start),
 4859                        reversed: selection.reversed,
 4860                        goal: selection.goal,
 4861                    },
 4862                    0,
 4863                ));
 4864
 4865                // Insert emoji
 4866                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4867                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4868                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4869
 4870                continue;
 4871            }
 4872
 4873            let next_is_adjacent = regions
 4874                .peek()
 4875                .is_some_and(|(next, _)| selection.end == next.start);
 4876
 4877            // If not handling any auto-close operation, then just replace the selected
 4878            // text with the given input and move the selection to the end of the
 4879            // newly inserted text.
 4880            let anchor = if in_adjacent_group || next_is_adjacent {
 4881                // After edits the right bias would shift those anchor to the next visible fragment
 4882                // but we want to resolve to the previous one
 4883                snapshot.anchor_before(selection.end)
 4884            } else {
 4885                snapshot.anchor_after(selection.end)
 4886            };
 4887
 4888            if !self.linked_edit_ranges.is_empty() {
 4889                let start_anchor = snapshot.anchor_before(selection.start);
 4890
 4891                let is_word_char = text.chars().next().is_none_or(|char| {
 4892                    let classifier = snapshot
 4893                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4894                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4895                    classifier.is_word(char)
 4896                });
 4897
 4898                if is_word_char {
 4899                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4900                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4901                } else {
 4902                    clear_linked_edit_ranges = true;
 4903                }
 4904            }
 4905
 4906            new_selections.push((selection.map(|_| anchor), 0));
 4907            edits.push((selection.start..selection.end, text.clone()));
 4908
 4909            has_adjacent_edits |= next_is_adjacent;
 4910            in_adjacent_group = next_is_adjacent;
 4911        }
 4912
 4913        if all_selections_read_only {
 4914            return;
 4915        }
 4916
 4917        drop(regions);
 4918        drop(snapshot);
 4919
 4920        self.transact(window, cx, |this, window, cx| {
 4921            if clear_linked_edit_ranges {
 4922                this.linked_edit_ranges.clear();
 4923            }
 4924            let initial_buffer_versions =
 4925                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4926
 4927            this.buffer.update(cx, |buffer, cx| {
 4928                if has_adjacent_edits {
 4929                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4930                } else {
 4931                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4932                }
 4933            });
 4934            linked_edits.apply(cx);
 4935            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4936            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4937            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4938            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4939                new_anchor_selections,
 4940                &map,
 4941            )
 4942            .zip(new_selection_deltas)
 4943            .map(|(selection, delta)| Selection {
 4944                id: selection.id,
 4945                start: selection.start + delta,
 4946                end: selection.end + delta,
 4947                reversed: selection.reversed,
 4948                goal: SelectionGoal::None,
 4949            })
 4950            .collect::<Vec<_>>();
 4951
 4952            let mut i = 0;
 4953            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4954                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4955                let start = map.buffer_snapshot().anchor_before(position);
 4956                let end = map.buffer_snapshot().anchor_after(position);
 4957                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4958                    match existing_state
 4959                        .range
 4960                        .start
 4961                        .cmp(&start, map.buffer_snapshot())
 4962                    {
 4963                        Ordering::Less => i += 1,
 4964                        Ordering::Greater => break,
 4965                        Ordering::Equal => {
 4966                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4967                                Ordering::Less => i += 1,
 4968                                Ordering::Equal => break,
 4969                                Ordering::Greater => break,
 4970                            }
 4971                        }
 4972                    }
 4973                }
 4974                this.autoclose_regions.insert(
 4975                    i,
 4976                    AutocloseRegion {
 4977                        selection_id,
 4978                        range: start..end,
 4979                        pair,
 4980                    },
 4981                );
 4982            }
 4983
 4984            let had_active_edit_prediction = this.has_active_edit_prediction();
 4985            this.change_selections(
 4986                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4987                window,
 4988                cx,
 4989                |s| s.select(new_selections),
 4990            );
 4991
 4992            if !bracket_inserted
 4993                && let Some(on_type_format_task) =
 4994                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4995            {
 4996                on_type_format_task.detach_and_log_err(cx);
 4997            }
 4998
 4999            let editor_settings = EditorSettings::get_global(cx);
 5000            if bracket_inserted
 5001                && (editor_settings.auto_signature_help
 5002                    || editor_settings.show_signature_help_after_edits)
 5003            {
 5004                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5005            }
 5006
 5007            let trigger_in_words =
 5008                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5009            if this.hard_wrap.is_some() {
 5010                let latest: Range<Point> = this.selections.newest(&map).range();
 5011                if latest.is_empty()
 5012                    && this
 5013                        .buffer()
 5014                        .read(cx)
 5015                        .snapshot(cx)
 5016                        .line_len(MultiBufferRow(latest.start.row))
 5017                        == latest.start.column
 5018                {
 5019                    this.rewrap_impl(
 5020                        RewrapOptions {
 5021                            override_language_settings: true,
 5022                            preserve_existing_whitespace: true,
 5023                        },
 5024                        cx,
 5025                    )
 5026                }
 5027            }
 5028            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5029            refresh_linked_ranges(this, window, cx);
 5030            this.refresh_edit_prediction(true, false, window, cx);
 5031            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5032        });
 5033    }
 5034
 5035    fn find_possible_emoji_shortcode_at_position(
 5036        snapshot: &MultiBufferSnapshot,
 5037        position: Point,
 5038    ) -> Option<String> {
 5039        let mut chars = Vec::new();
 5040        let mut found_colon = false;
 5041        for char in snapshot.reversed_chars_at(position).take(100) {
 5042            // Found a possible emoji shortcode in the middle of the buffer
 5043            if found_colon {
 5044                if char.is_whitespace() {
 5045                    chars.reverse();
 5046                    return Some(chars.iter().collect());
 5047                }
 5048                // If the previous character is not a whitespace, we are in the middle of a word
 5049                // and we only want to complete the shortcode if the word is made up of other emojis
 5050                let mut containing_word = String::new();
 5051                for ch in snapshot
 5052                    .reversed_chars_at(position)
 5053                    .skip(chars.len() + 1)
 5054                    .take(100)
 5055                {
 5056                    if ch.is_whitespace() {
 5057                        break;
 5058                    }
 5059                    containing_word.push(ch);
 5060                }
 5061                let containing_word = containing_word.chars().rev().collect::<String>();
 5062                if util::word_consists_of_emojis(containing_word.as_str()) {
 5063                    chars.reverse();
 5064                    return Some(chars.iter().collect());
 5065                }
 5066            }
 5067
 5068            if char.is_whitespace() || !char.is_ascii() {
 5069                return None;
 5070            }
 5071            if char == ':' {
 5072                found_colon = true;
 5073            } else {
 5074                chars.push(char);
 5075            }
 5076        }
 5077        // Found a possible emoji shortcode at the beginning of the buffer
 5078        chars.reverse();
 5079        Some(chars.iter().collect())
 5080    }
 5081
 5082    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5083        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5084        self.transact(window, cx, |this, window, cx| {
 5085            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5086                let selections = this
 5087                    .selections
 5088                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5089                let multi_buffer = this.buffer.read(cx);
 5090                let buffer = multi_buffer.snapshot(cx);
 5091                selections
 5092                    .iter()
 5093                    .map(|selection| {
 5094                        let start_point = selection.start.to_point(&buffer);
 5095                        let mut existing_indent =
 5096                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5097                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5098                        let start = selection.start;
 5099                        let end = selection.end;
 5100                        let selection_is_empty = start == end;
 5101                        let language_scope = buffer.language_scope_at(start);
 5102                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5103                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5104                                &buffer,
 5105                                start..end,
 5106                                language,
 5107                            )
 5108                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5109                                    &buffer,
 5110                                    start..end,
 5111                                );
 5112
 5113                            let mut newline_config = NewlineConfig::Newline {
 5114                                additional_indent: IndentSize::spaces(0),
 5115                                extra_line_additional_indent: if needs_extra_newline {
 5116                                    Some(IndentSize::spaces(0))
 5117                                } else {
 5118                                    None
 5119                                },
 5120                                prevent_auto_indent: false,
 5121                            };
 5122
 5123                            let comment_delimiter = maybe!({
 5124                                if !selection_is_empty {
 5125                                    return None;
 5126                                }
 5127
 5128                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5129                                    return None;
 5130                                }
 5131
 5132                                return comment_delimiter_for_newline(
 5133                                    &start_point,
 5134                                    &buffer,
 5135                                    language,
 5136                                );
 5137                            });
 5138
 5139                            let doc_delimiter = maybe!({
 5140                                if !selection_is_empty {
 5141                                    return None;
 5142                                }
 5143
 5144                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5145                                    return None;
 5146                                }
 5147
 5148                                return documentation_delimiter_for_newline(
 5149                                    &start_point,
 5150                                    &buffer,
 5151                                    language,
 5152                                    &mut newline_config,
 5153                                );
 5154                            });
 5155
 5156                            let list_delimiter = maybe!({
 5157                                if !selection_is_empty {
 5158                                    return None;
 5159                                }
 5160
 5161                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5162                                    return None;
 5163                                }
 5164
 5165                                return list_delimiter_for_newline(
 5166                                    &start_point,
 5167                                    &buffer,
 5168                                    language,
 5169                                    &mut newline_config,
 5170                                );
 5171                            });
 5172
 5173                            (
 5174                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5175                                newline_config,
 5176                            )
 5177                        } else {
 5178                            (
 5179                                None,
 5180                                NewlineConfig::Newline {
 5181                                    additional_indent: IndentSize::spaces(0),
 5182                                    extra_line_additional_indent: None,
 5183                                    prevent_auto_indent: false,
 5184                                },
 5185                            )
 5186                        };
 5187
 5188                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5189                            NewlineConfig::ClearCurrentLine => {
 5190                                let row_start =
 5191                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5192                                (row_start, String::new(), false)
 5193                            }
 5194                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5195                                let row_start =
 5196                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5197                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5198                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5199                                let reduced_indent =
 5200                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5201                                let mut new_text = String::new();
 5202                                new_text.extend(reduced_indent.chars());
 5203                                new_text.push_str(continuation);
 5204                                (row_start, new_text, true)
 5205                            }
 5206                            NewlineConfig::Newline {
 5207                                additional_indent,
 5208                                extra_line_additional_indent,
 5209                                prevent_auto_indent,
 5210                            } => {
 5211                                let capacity_for_delimiter =
 5212                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5213                                let extra_line_len = extra_line_additional_indent
 5214                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5215                                    .unwrap_or(0);
 5216                                let mut new_text = String::with_capacity(
 5217                                    1 + capacity_for_delimiter
 5218                                        + existing_indent.len as usize
 5219                                        + additional_indent.len as usize
 5220                                        + extra_line_len,
 5221                                );
 5222                                new_text.push('\n');
 5223                                new_text.extend(existing_indent.chars());
 5224                                new_text.extend(additional_indent.chars());
 5225                                if let Some(delimiter) = &delimiter {
 5226                                    new_text.push_str(delimiter);
 5227                                }
 5228                                if let Some(extra_indent) = extra_line_additional_indent {
 5229                                    new_text.push('\n');
 5230                                    new_text.extend(existing_indent.chars());
 5231                                    new_text.extend(extra_indent.chars());
 5232                                }
 5233                                (start, new_text, *prevent_auto_indent)
 5234                            }
 5235                        };
 5236
 5237                        let anchor = buffer.anchor_after(end);
 5238                        let new_selection = selection.map(|_| anchor);
 5239                        (
 5240                            ((edit_start..end, new_text), prevent_auto_indent),
 5241                            (newline_config.has_extra_line(), new_selection),
 5242                        )
 5243                    })
 5244                    .unzip()
 5245            };
 5246
 5247            let mut auto_indent_edits = Vec::new();
 5248            let mut edits = Vec::new();
 5249            for (edit, prevent_auto_indent) in edits_with_flags {
 5250                if prevent_auto_indent {
 5251                    edits.push(edit);
 5252                } else {
 5253                    auto_indent_edits.push(edit);
 5254                }
 5255            }
 5256            if !edits.is_empty() {
 5257                this.edit(edits, cx);
 5258            }
 5259            if !auto_indent_edits.is_empty() {
 5260                this.edit_with_autoindent(auto_indent_edits, cx);
 5261            }
 5262
 5263            let buffer = this.buffer.read(cx).snapshot(cx);
 5264            let new_selections = selection_info
 5265                .into_iter()
 5266                .map(|(extra_newline_inserted, new_selection)| {
 5267                    let mut cursor = new_selection.end.to_point(&buffer);
 5268                    if extra_newline_inserted {
 5269                        cursor.row -= 1;
 5270                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5271                    }
 5272                    new_selection.map(|_| cursor)
 5273                })
 5274                .collect();
 5275
 5276            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5277            this.refresh_edit_prediction(true, false, window, cx);
 5278            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5279                task.detach_and_log_err(cx);
 5280            }
 5281        });
 5282    }
 5283
 5284    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5285        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5286
 5287        let buffer = self.buffer.read(cx);
 5288        let snapshot = buffer.snapshot(cx);
 5289
 5290        let mut edits = Vec::new();
 5291        let mut rows = Vec::new();
 5292
 5293        for (rows_inserted, selection) in self
 5294            .selections
 5295            .all_adjusted(&self.display_snapshot(cx))
 5296            .into_iter()
 5297            .enumerate()
 5298        {
 5299            let cursor = selection.head();
 5300            let row = cursor.row;
 5301
 5302            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5303
 5304            let newline = "\n".to_string();
 5305            edits.push((start_of_line..start_of_line, newline));
 5306
 5307            rows.push(row + rows_inserted as u32);
 5308        }
 5309
 5310        self.transact(window, cx, |editor, window, cx| {
 5311            editor.edit(edits, cx);
 5312
 5313            editor.change_selections(Default::default(), window, cx, |s| {
 5314                let mut index = 0;
 5315                s.move_cursors_with(&mut |map, _, _| {
 5316                    let row = rows[index];
 5317                    index += 1;
 5318
 5319                    let point = Point::new(row, 0);
 5320                    let boundary = map.next_line_boundary(point).1;
 5321                    let clipped = map.clip_point(boundary, Bias::Left);
 5322
 5323                    (clipped, SelectionGoal::None)
 5324                });
 5325            });
 5326
 5327            let mut indent_edits = Vec::new();
 5328            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5329            for row in rows {
 5330                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5331                for (row, indent) in indents {
 5332                    if indent.len == 0 {
 5333                        continue;
 5334                    }
 5335
 5336                    let text = match indent.kind {
 5337                        IndentKind::Space => " ".repeat(indent.len as usize),
 5338                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5339                    };
 5340                    let point = Point::new(row.0, 0);
 5341                    indent_edits.push((point..point, text));
 5342                }
 5343            }
 5344            editor.edit(indent_edits, cx);
 5345            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5346                format.detach_and_log_err(cx);
 5347            }
 5348        });
 5349    }
 5350
 5351    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5352        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5353
 5354        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5355        let mut rows = Vec::new();
 5356        let mut rows_inserted = 0;
 5357
 5358        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5359            let cursor = selection.head();
 5360            let row = cursor.row;
 5361
 5362            let point = Point::new(row, 0);
 5363            let Some((buffer_handle, buffer_point, _)) =
 5364                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5365            else {
 5366                continue;
 5367            };
 5368
 5369            buffer_edits
 5370                .entry(buffer_handle.entity_id())
 5371                .or_insert_with(|| (buffer_handle, Vec::new()))
 5372                .1
 5373                .push(buffer_point);
 5374
 5375            rows_inserted += 1;
 5376            rows.push(row + rows_inserted);
 5377        }
 5378
 5379        self.transact(window, cx, |editor, window, cx| {
 5380            for (_, (buffer_handle, points)) in &buffer_edits {
 5381                buffer_handle.update(cx, |buffer, cx| {
 5382                    let edits: Vec<_> = points
 5383                        .iter()
 5384                        .map(|point| {
 5385                            let target = Point::new(point.row + 1, 0);
 5386                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5387                            (start_of_line..start_of_line, "\n")
 5388                        })
 5389                        .collect();
 5390                    buffer.edit(edits, None, cx);
 5391                });
 5392            }
 5393
 5394            editor.change_selections(Default::default(), window, cx, |s| {
 5395                let mut index = 0;
 5396                s.move_cursors_with(&mut |map, _, _| {
 5397                    let row = rows[index];
 5398                    index += 1;
 5399
 5400                    let point = Point::new(row, 0);
 5401                    let boundary = map.next_line_boundary(point).1;
 5402                    let clipped = map.clip_point(boundary, Bias::Left);
 5403
 5404                    (clipped, SelectionGoal::None)
 5405                });
 5406            });
 5407
 5408            let mut indent_edits = Vec::new();
 5409            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5410            for row in rows {
 5411                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5412                for (row, indent) in indents {
 5413                    if indent.len == 0 {
 5414                        continue;
 5415                    }
 5416
 5417                    let text = match indent.kind {
 5418                        IndentKind::Space => " ".repeat(indent.len as usize),
 5419                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5420                    };
 5421                    let point = Point::new(row.0, 0);
 5422                    indent_edits.push((point..point, text));
 5423                }
 5424            }
 5425            editor.edit(indent_edits, cx);
 5426            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5427                format.detach_and_log_err(cx);
 5428            }
 5429        });
 5430    }
 5431
 5432    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5433        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5434            original_indent_columns: Vec::new(),
 5435        });
 5436        self.replace_selections(text, autoindent, window, cx, false);
 5437    }
 5438
 5439    /// Replaces the editor's selections with the provided `text`, applying the
 5440    /// given `autoindent_mode` (`None` will skip autoindentation).
 5441    ///
 5442    /// Early returns if the editor is in read-only mode, without applying any
 5443    /// edits.
 5444    fn replace_selections(
 5445        &mut self,
 5446        text: &str,
 5447        autoindent_mode: Option<AutoindentMode>,
 5448        window: &mut Window,
 5449        cx: &mut Context<Self>,
 5450        apply_linked_edits: bool,
 5451    ) {
 5452        if self.read_only(cx) {
 5453            return;
 5454        }
 5455
 5456        let text: Arc<str> = text.into();
 5457        self.transact(window, cx, |this, window, cx| {
 5458            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5459            let linked_edits = if apply_linked_edits {
 5460                this.linked_edits_for_selections(text.clone(), cx)
 5461            } else {
 5462                LinkedEdits::new()
 5463            };
 5464
 5465            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5466                let anchors = {
 5467                    let snapshot = buffer.read(cx);
 5468                    old_selections
 5469                        .iter()
 5470                        .map(|s| {
 5471                            let anchor = snapshot.anchor_after(s.head());
 5472                            s.map(|_| anchor)
 5473                        })
 5474                        .collect::<Vec<_>>()
 5475                };
 5476                buffer.edit(
 5477                    old_selections
 5478                        .iter()
 5479                        .map(|s| (s.start..s.end, text.clone())),
 5480                    autoindent_mode,
 5481                    cx,
 5482                );
 5483                anchors
 5484            });
 5485
 5486            linked_edits.apply(cx);
 5487
 5488            this.change_selections(Default::default(), window, cx, |s| {
 5489                s.select_anchors(selection_anchors);
 5490            });
 5491
 5492            if apply_linked_edits {
 5493                refresh_linked_ranges(this, window, cx);
 5494            }
 5495
 5496            cx.notify();
 5497        });
 5498    }
 5499
 5500    /// Collects linked edits for the current selections, pairing each linked
 5501    /// range with `text`.
 5502    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5503        let mut linked_edits = LinkedEdits::new();
 5504        if !self.linked_edit_ranges.is_empty() {
 5505            for selection in self.selections.disjoint_anchors() {
 5506                let start = selection.start.text_anchor;
 5507                let end = selection.end.text_anchor;
 5508                linked_edits.push(self, start..end, text.clone(), cx);
 5509            }
 5510        }
 5511        linked_edits
 5512    }
 5513
 5514    /// Deletes the content covered by the current selections and applies
 5515    /// linked edits.
 5516    pub fn delete_selections_with_linked_edits(
 5517        &mut self,
 5518        window: &mut Window,
 5519        cx: &mut Context<Self>,
 5520    ) {
 5521        self.replace_selections("", None, window, cx, true);
 5522    }
 5523
 5524    #[cfg(any(test, feature = "test-support"))]
 5525    pub fn set_linked_edit_ranges_for_testing(
 5526        &mut self,
 5527        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5528        cx: &mut Context<Self>,
 5529    ) -> Option<()> {
 5530        let Some((buffer, _)) = self
 5531            .buffer
 5532            .read(cx)
 5533            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5534        else {
 5535            return None;
 5536        };
 5537        let buffer = buffer.read(cx);
 5538        let buffer_id = buffer.remote_id();
 5539        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5540        for (base_range, linked_ranges_points) in ranges {
 5541            let base_anchor =
 5542                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5543            let linked_anchors = linked_ranges_points
 5544                .into_iter()
 5545                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5546                .collect();
 5547            linked_ranges.push((base_anchor, linked_anchors));
 5548        }
 5549        let mut map = HashMap::default();
 5550        map.insert(buffer_id, linked_ranges);
 5551        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5552        Some(())
 5553    }
 5554
 5555    fn trigger_completion_on_input(
 5556        &mut self,
 5557        text: &str,
 5558        trigger_in_words: bool,
 5559        window: &mut Window,
 5560        cx: &mut Context<Self>,
 5561    ) {
 5562        let completions_source = self
 5563            .context_menu
 5564            .borrow()
 5565            .as_ref()
 5566            .and_then(|menu| match menu {
 5567                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5568                CodeContextMenu::CodeActions(_) => None,
 5569            });
 5570
 5571        match completions_source {
 5572            Some(CompletionsMenuSource::Words { .. }) => {
 5573                self.open_or_update_completions_menu(
 5574                    Some(CompletionsMenuSource::Words {
 5575                        ignore_threshold: false,
 5576                    }),
 5577                    None,
 5578                    trigger_in_words,
 5579                    window,
 5580                    cx,
 5581                );
 5582            }
 5583            _ => self.open_or_update_completions_menu(
 5584                None,
 5585                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5586                true,
 5587                window,
 5588                cx,
 5589            ),
 5590        }
 5591    }
 5592
 5593    /// If any empty selections is touching the start of its innermost containing autoclose
 5594    /// region, expand it to select the brackets.
 5595    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5596        let selections = self
 5597            .selections
 5598            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5599        let buffer = self.buffer.read(cx).read(cx);
 5600        let new_selections = self
 5601            .selections_with_autoclose_regions(selections, &buffer)
 5602            .map(|(mut selection, region)| {
 5603                if !selection.is_empty() {
 5604                    return selection;
 5605                }
 5606
 5607                if let Some(region) = region {
 5608                    let mut range = region.range.to_offset(&buffer);
 5609                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5610                        range.start -= region.pair.start.len();
 5611                        if buffer.contains_str_at(range.start, &region.pair.start)
 5612                            && buffer.contains_str_at(range.end, &region.pair.end)
 5613                        {
 5614                            range.end += region.pair.end.len();
 5615                            selection.start = range.start;
 5616                            selection.end = range.end;
 5617
 5618                            return selection;
 5619                        }
 5620                    }
 5621                }
 5622
 5623                let always_treat_brackets_as_autoclosed = buffer
 5624                    .language_settings_at(selection.start, cx)
 5625                    .always_treat_brackets_as_autoclosed;
 5626
 5627                if !always_treat_brackets_as_autoclosed {
 5628                    return selection;
 5629                }
 5630
 5631                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5632                    for (pair, enabled) in scope.brackets() {
 5633                        if !enabled || !pair.close {
 5634                            continue;
 5635                        }
 5636
 5637                        if buffer.contains_str_at(selection.start, &pair.end) {
 5638                            let pair_start_len = pair.start.len();
 5639                            if buffer.contains_str_at(
 5640                                selection.start.saturating_sub_usize(pair_start_len),
 5641                                &pair.start,
 5642                            ) {
 5643                                selection.start -= pair_start_len;
 5644                                selection.end += pair.end.len();
 5645
 5646                                return selection;
 5647                            }
 5648                        }
 5649                    }
 5650                }
 5651
 5652                selection
 5653            })
 5654            .collect();
 5655
 5656        drop(buffer);
 5657        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5658            selections.select(new_selections)
 5659        });
 5660    }
 5661
 5662    /// Iterate the given selections, and for each one, find the smallest surrounding
 5663    /// autoclose region. This uses the ordering of the selections and the autoclose
 5664    /// regions to avoid repeated comparisons.
 5665    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5666        &'a self,
 5667        selections: impl IntoIterator<Item = Selection<D>>,
 5668        buffer: &'a MultiBufferSnapshot,
 5669    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5670        let mut i = 0;
 5671        let mut regions = self.autoclose_regions.as_slice();
 5672        selections.into_iter().map(move |selection| {
 5673            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5674
 5675            let mut enclosing = None;
 5676            while let Some(pair_state) = regions.get(i) {
 5677                if pair_state.range.end.to_offset(buffer) < range.start {
 5678                    regions = &regions[i + 1..];
 5679                    i = 0;
 5680                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5681                    break;
 5682                } else {
 5683                    if pair_state.selection_id == selection.id {
 5684                        enclosing = Some(pair_state);
 5685                    }
 5686                    i += 1;
 5687                }
 5688            }
 5689
 5690            (selection, enclosing)
 5691        })
 5692    }
 5693
 5694    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5695    fn invalidate_autoclose_regions(
 5696        &mut self,
 5697        mut selections: &[Selection<Anchor>],
 5698        buffer: &MultiBufferSnapshot,
 5699    ) {
 5700        self.autoclose_regions.retain(|state| {
 5701            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5702                return false;
 5703            }
 5704
 5705            let mut i = 0;
 5706            while let Some(selection) = selections.get(i) {
 5707                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5708                    selections = &selections[1..];
 5709                    continue;
 5710                }
 5711                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5712                    break;
 5713                }
 5714                if selection.id == state.selection_id {
 5715                    return true;
 5716                } else {
 5717                    i += 1;
 5718                }
 5719            }
 5720            false
 5721        });
 5722    }
 5723
 5724    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5725        let offset = position.to_offset(buffer);
 5726        let (word_range, kind) =
 5727            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5728        if offset > word_range.start && kind == Some(CharKind::Word) {
 5729            Some(
 5730                buffer
 5731                    .text_for_range(word_range.start..offset)
 5732                    .collect::<String>(),
 5733            )
 5734        } else {
 5735            None
 5736        }
 5737    }
 5738
 5739    pub fn visible_excerpts(
 5740        &self,
 5741        lsp_related_only: bool,
 5742        cx: &mut Context<Editor>,
 5743    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5744        let project = self.project().cloned();
 5745        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5746        let multi_buffer = self.buffer().read(cx);
 5747        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5748        let multi_buffer_visible_start = self
 5749            .scroll_manager
 5750            .native_anchor(&display_snapshot, cx)
 5751            .anchor
 5752            .to_point(&multi_buffer_snapshot);
 5753        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5754            multi_buffer_visible_start
 5755                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5756            Bias::Left,
 5757        );
 5758        multi_buffer_snapshot
 5759            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5760            .into_iter()
 5761            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5762            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5763                if !lsp_related_only {
 5764                    return Some((
 5765                        excerpt_id,
 5766                        (
 5767                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5768                            buffer.version().clone(),
 5769                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5770                        ),
 5771                    ));
 5772                }
 5773
 5774                let project = project.as_ref()?.read(cx);
 5775                let buffer_file = project::File::from_dyn(buffer.file())?;
 5776                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5777                let worktree_entry = buffer_worktree
 5778                    .read(cx)
 5779                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5780                if worktree_entry.is_ignored {
 5781                    None
 5782                } else {
 5783                    Some((
 5784                        excerpt_id,
 5785                        (
 5786                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5787                            buffer.version().clone(),
 5788                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5789                        ),
 5790                    ))
 5791                }
 5792            })
 5793            .collect()
 5794    }
 5795
 5796    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5797        TextLayoutDetails {
 5798            text_system: window.text_system().clone(),
 5799            editor_style: self.style.clone().unwrap(),
 5800            rem_size: window.rem_size(),
 5801            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5802            visible_rows: self.visible_line_count(),
 5803            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5804        }
 5805    }
 5806
 5807    fn trigger_on_type_formatting(
 5808        &self,
 5809        input: String,
 5810        window: &mut Window,
 5811        cx: &mut Context<Self>,
 5812    ) -> Option<Task<Result<()>>> {
 5813        if input.chars().count() != 1 {
 5814            return None;
 5815        }
 5816
 5817        let project = self.project()?;
 5818        let position = self.selections.newest_anchor().head();
 5819        let (buffer, buffer_position) = self
 5820            .buffer
 5821            .read(cx)
 5822            .text_anchor_for_position(position, cx)?;
 5823
 5824        let settings = language_settings::language_settings(
 5825            buffer
 5826                .read(cx)
 5827                .language_at(buffer_position)
 5828                .map(|l| l.name()),
 5829            buffer.read(cx).file(),
 5830            cx,
 5831        );
 5832        if !settings.use_on_type_format {
 5833            return None;
 5834        }
 5835
 5836        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5837        // hence we do LSP request & edit on host side only — add formats to host's history.
 5838        let push_to_lsp_host_history = true;
 5839        // If this is not the host, append its history with new edits.
 5840        let push_to_client_history = project.read(cx).is_via_collab();
 5841
 5842        let on_type_formatting = project.update(cx, |project, cx| {
 5843            project.on_type_format(
 5844                buffer.clone(),
 5845                buffer_position,
 5846                input,
 5847                push_to_lsp_host_history,
 5848                cx,
 5849            )
 5850        });
 5851        Some(cx.spawn_in(window, async move |editor, cx| {
 5852            if let Some(transaction) = on_type_formatting.await? {
 5853                if push_to_client_history {
 5854                    buffer.update(cx, |buffer, _| {
 5855                        buffer.push_transaction(transaction, Instant::now());
 5856                        buffer.finalize_last_transaction();
 5857                    });
 5858                }
 5859                editor.update(cx, |editor, cx| {
 5860                    editor.refresh_document_highlights(cx);
 5861                })?;
 5862            }
 5863            Ok(())
 5864        }))
 5865    }
 5866
 5867    pub fn show_word_completions(
 5868        &mut self,
 5869        _: &ShowWordCompletions,
 5870        window: &mut Window,
 5871        cx: &mut Context<Self>,
 5872    ) {
 5873        self.open_or_update_completions_menu(
 5874            Some(CompletionsMenuSource::Words {
 5875                ignore_threshold: true,
 5876            }),
 5877            None,
 5878            false,
 5879            window,
 5880            cx,
 5881        );
 5882    }
 5883
 5884    pub fn show_completions(
 5885        &mut self,
 5886        _: &ShowCompletions,
 5887        window: &mut Window,
 5888        cx: &mut Context<Self>,
 5889    ) {
 5890        self.open_or_update_completions_menu(None, None, false, window, cx);
 5891    }
 5892
 5893    fn open_or_update_completions_menu(
 5894        &mut self,
 5895        requested_source: Option<CompletionsMenuSource>,
 5896        trigger: Option<String>,
 5897        trigger_in_words: bool,
 5898        window: &mut Window,
 5899        cx: &mut Context<Self>,
 5900    ) {
 5901        if self.pending_rename.is_some() {
 5902            return;
 5903        }
 5904
 5905        let completions_source = self
 5906            .context_menu
 5907            .borrow()
 5908            .as_ref()
 5909            .and_then(|menu| match menu {
 5910                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5911                CodeContextMenu::CodeActions(_) => None,
 5912            });
 5913
 5914        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5915
 5916        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5917        // inserted and selected. To handle that case, the start of the selection is used so that
 5918        // the menu starts with all choices.
 5919        let position = self
 5920            .selections
 5921            .newest_anchor()
 5922            .start
 5923            .bias_right(&multibuffer_snapshot);
 5924        if position.diff_base_anchor.is_some() {
 5925            return;
 5926        }
 5927        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5928        let Some(buffer) = buffer_position
 5929            .text_anchor
 5930            .buffer_id
 5931            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5932        else {
 5933            return;
 5934        };
 5935        let buffer_snapshot = buffer.read(cx).snapshot();
 5936
 5937        let menu_is_open = matches!(
 5938            self.context_menu.borrow().as_ref(),
 5939            Some(CodeContextMenu::Completions(_))
 5940        );
 5941
 5942        let language = buffer_snapshot
 5943            .language_at(buffer_position.text_anchor)
 5944            .map(|language| language.name());
 5945
 5946        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5947        let completion_settings = language_settings.completions.clone();
 5948
 5949        let show_completions_on_input = self
 5950            .show_completions_on_input_override
 5951            .unwrap_or(language_settings.show_completions_on_input);
 5952        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5953            return;
 5954        }
 5955
 5956        let query: Option<Arc<String>> =
 5957            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5958                .map(|query| query.into());
 5959
 5960        drop(multibuffer_snapshot);
 5961
 5962        // Hide the current completions menu when query is empty. Without this, cached
 5963        // completions from before the trigger char may be reused (#32774).
 5964        if query.is_none() && menu_is_open {
 5965            self.hide_context_menu(window, cx);
 5966        }
 5967
 5968        let mut ignore_word_threshold = false;
 5969        let provider = match requested_source {
 5970            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5971            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5972                ignore_word_threshold = ignore_threshold;
 5973                None
 5974            }
 5975            Some(CompletionsMenuSource::SnippetChoices)
 5976            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5977                log::error!("bug: SnippetChoices requested_source is not handled");
 5978                None
 5979            }
 5980        };
 5981
 5982        let sort_completions = provider
 5983            .as_ref()
 5984            .is_some_and(|provider| provider.sort_completions());
 5985
 5986        let filter_completions = provider
 5987            .as_ref()
 5988            .is_none_or(|provider| provider.filter_completions());
 5989
 5990        let was_snippets_only = matches!(
 5991            completions_source,
 5992            Some(CompletionsMenuSource::SnippetsOnly)
 5993        );
 5994
 5995        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5996            if filter_completions {
 5997                menu.filter(
 5998                    query.clone().unwrap_or_default(),
 5999                    buffer_position.text_anchor,
 6000                    &buffer,
 6001                    provider.clone(),
 6002                    window,
 6003                    cx,
 6004                );
 6005            }
 6006            // When `is_incomplete` is false, no need to re-query completions when the current query
 6007            // is a suffix of the initial query.
 6008            let was_complete = !menu.is_incomplete;
 6009            if was_complete && !was_snippets_only {
 6010                // If the new query is a suffix of the old query (typing more characters) and
 6011                // the previous result was complete, the existing completions can be filtered.
 6012                //
 6013                // Note that snippet completions are always complete.
 6014                let query_matches = match (&menu.initial_query, &query) {
 6015                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6016                    (None, _) => true,
 6017                    _ => false,
 6018                };
 6019                if query_matches {
 6020                    let position_matches = if menu.initial_position == position {
 6021                        true
 6022                    } else {
 6023                        let snapshot = self.buffer.read(cx).read(cx);
 6024                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6025                    };
 6026                    if position_matches {
 6027                        return;
 6028                    }
 6029                }
 6030            }
 6031        };
 6032
 6033        let Anchor {
 6034            excerpt_id: buffer_excerpt_id,
 6035            text_anchor: buffer_position,
 6036            ..
 6037        } = buffer_position;
 6038
 6039        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6040            buffer_snapshot.surrounding_word(buffer_position, None)
 6041        {
 6042            let word_to_exclude = buffer_snapshot
 6043                .text_for_range(word_range.clone())
 6044                .collect::<String>();
 6045            (
 6046                buffer_snapshot.anchor_before(word_range.start)
 6047                    ..buffer_snapshot.anchor_after(buffer_position),
 6048                Some(word_to_exclude),
 6049            )
 6050        } else {
 6051            (buffer_position..buffer_position, None)
 6052        };
 6053
 6054        let show_completion_documentation = buffer_snapshot
 6055            .settings_at(buffer_position, cx)
 6056            .show_completion_documentation;
 6057
 6058        // The document can be large, so stay in reasonable bounds when searching for words,
 6059        // otherwise completion pop-up might be slow to appear.
 6060        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6061        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6062        let min_word_search = buffer_snapshot.clip_point(
 6063            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6064            Bias::Left,
 6065        );
 6066        let max_word_search = buffer_snapshot.clip_point(
 6067            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6068            Bias::Right,
 6069        );
 6070        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6071            ..buffer_snapshot.point_to_offset(max_word_search);
 6072
 6073        let skip_digits = query
 6074            .as_ref()
 6075            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6076
 6077        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6078            trigger.as_ref().is_none_or(|trigger| {
 6079                provider.is_completion_trigger(
 6080                    &buffer,
 6081                    position.text_anchor,
 6082                    trigger,
 6083                    trigger_in_words,
 6084                    cx,
 6085                )
 6086            })
 6087        });
 6088
 6089        let provider_responses = if let Some(provider) = &provider
 6090            && load_provider_completions
 6091        {
 6092            let trigger_character =
 6093                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6094            let completion_context = CompletionContext {
 6095                trigger_kind: match &trigger_character {
 6096                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6097                    None => CompletionTriggerKind::INVOKED,
 6098                },
 6099                trigger_character,
 6100            };
 6101
 6102            provider.completions(
 6103                buffer_excerpt_id,
 6104                &buffer,
 6105                buffer_position,
 6106                completion_context,
 6107                window,
 6108                cx,
 6109            )
 6110        } else {
 6111            Task::ready(Ok(Vec::new()))
 6112        };
 6113
 6114        let load_word_completions = if !self.word_completions_enabled {
 6115            false
 6116        } else if requested_source
 6117            == Some(CompletionsMenuSource::Words {
 6118                ignore_threshold: true,
 6119            })
 6120        {
 6121            true
 6122        } else {
 6123            load_provider_completions
 6124                && completion_settings.words != WordsCompletionMode::Disabled
 6125                && (ignore_word_threshold || {
 6126                    let words_min_length = completion_settings.words_min_length;
 6127                    // check whether word has at least `words_min_length` characters
 6128                    let query_chars = query.iter().flat_map(|q| q.chars());
 6129                    query_chars.take(words_min_length).count() == words_min_length
 6130                })
 6131        };
 6132
 6133        let mut words = if load_word_completions {
 6134            cx.background_spawn({
 6135                let buffer_snapshot = buffer_snapshot.clone();
 6136                async move {
 6137                    buffer_snapshot.words_in_range(WordsQuery {
 6138                        fuzzy_contents: None,
 6139                        range: word_search_range,
 6140                        skip_digits,
 6141                    })
 6142                }
 6143            })
 6144        } else {
 6145            Task::ready(BTreeMap::default())
 6146        };
 6147
 6148        let snippets = if let Some(provider) = &provider
 6149            && provider.show_snippets()
 6150            && let Some(project) = self.project()
 6151        {
 6152            let char_classifier = buffer_snapshot
 6153                .char_classifier_at(buffer_position)
 6154                .scope_context(Some(CharScopeContext::Completion));
 6155            project.update(cx, |project, cx| {
 6156                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6157            })
 6158        } else {
 6159            Task::ready(Ok(CompletionResponse {
 6160                completions: Vec::new(),
 6161                display_options: Default::default(),
 6162                is_incomplete: false,
 6163            }))
 6164        };
 6165
 6166        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6167
 6168        let id = post_inc(&mut self.next_completion_id);
 6169        let task = cx.spawn_in(window, async move |editor, cx| {
 6170            let Ok(()) = editor.update(cx, |this, _| {
 6171                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6172            }) else {
 6173                return;
 6174            };
 6175
 6176            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6177            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6178            let mut completions = Vec::new();
 6179            let mut is_incomplete = false;
 6180            let mut display_options: Option<CompletionDisplayOptions> = None;
 6181            if let Some(provider_responses) = provider_responses.await.log_err()
 6182                && !provider_responses.is_empty()
 6183            {
 6184                for response in provider_responses {
 6185                    completions.extend(response.completions);
 6186                    is_incomplete = is_incomplete || response.is_incomplete;
 6187                    match display_options.as_mut() {
 6188                        None => {
 6189                            display_options = Some(response.display_options);
 6190                        }
 6191                        Some(options) => options.merge(&response.display_options),
 6192                    }
 6193                }
 6194                if completion_settings.words == WordsCompletionMode::Fallback {
 6195                    words = Task::ready(BTreeMap::default());
 6196                }
 6197            }
 6198            let display_options = display_options.unwrap_or_default();
 6199
 6200            let mut words = words.await;
 6201            if let Some(word_to_exclude) = &word_to_exclude {
 6202                words.remove(word_to_exclude);
 6203            }
 6204            for lsp_completion in &completions {
 6205                words.remove(&lsp_completion.new_text);
 6206            }
 6207            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6208                replace_range: word_replace_range.clone(),
 6209                new_text: word.clone(),
 6210                label: CodeLabel::plain(word, None),
 6211                match_start: None,
 6212                snippet_deduplication_key: None,
 6213                icon_path: None,
 6214                documentation: None,
 6215                source: CompletionSource::BufferWord {
 6216                    word_range,
 6217                    resolved: false,
 6218                },
 6219                insert_text_mode: Some(InsertTextMode::AS_IS),
 6220                confirm: None,
 6221            }));
 6222
 6223            completions.extend(
 6224                snippets
 6225                    .await
 6226                    .into_iter()
 6227                    .flat_map(|response| response.completions),
 6228            );
 6229
 6230            let menu = if completions.is_empty() {
 6231                None
 6232            } else {
 6233                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6234                    let languages = editor
 6235                        .workspace
 6236                        .as_ref()
 6237                        .and_then(|(workspace, _)| workspace.upgrade())
 6238                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6239                    let menu = CompletionsMenu::new(
 6240                        id,
 6241                        requested_source.unwrap_or(if load_provider_completions {
 6242                            CompletionsMenuSource::Normal
 6243                        } else {
 6244                            CompletionsMenuSource::SnippetsOnly
 6245                        }),
 6246                        sort_completions,
 6247                        show_completion_documentation,
 6248                        position,
 6249                        query.clone(),
 6250                        is_incomplete,
 6251                        buffer.clone(),
 6252                        completions.into(),
 6253                        editor
 6254                            .context_menu()
 6255                            .borrow_mut()
 6256                            .as_ref()
 6257                            .map(|menu| menu.primary_scroll_handle()),
 6258                        display_options,
 6259                        snippet_sort_order,
 6260                        languages,
 6261                        language,
 6262                        cx,
 6263                    );
 6264
 6265                    let query = if filter_completions { query } else { None };
 6266                    let matches_task = menu.do_async_filtering(
 6267                        query.unwrap_or_default(),
 6268                        buffer_position,
 6269                        &buffer,
 6270                        cx,
 6271                    );
 6272                    (menu, matches_task)
 6273                }) else {
 6274                    return;
 6275                };
 6276
 6277                let matches = matches_task.await;
 6278
 6279                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6280                    // Newer menu already set, so exit.
 6281                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6282                        editor.context_menu.borrow().as_ref()
 6283                        && prev_menu.id > id
 6284                    {
 6285                        return;
 6286                    };
 6287
 6288                    // Only valid to take prev_menu because either the new menu is immediately set
 6289                    // below, or the menu is hidden.
 6290                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6291                        editor.context_menu.borrow_mut().take()
 6292                    {
 6293                        let position_matches =
 6294                            if prev_menu.initial_position == menu.initial_position {
 6295                                true
 6296                            } else {
 6297                                let snapshot = editor.buffer.read(cx).read(cx);
 6298                                prev_menu.initial_position.to_offset(&snapshot)
 6299                                    == menu.initial_position.to_offset(&snapshot)
 6300                            };
 6301                        if position_matches {
 6302                            // Preserve markdown cache before `set_filter_results` because it will
 6303                            // try to populate the documentation cache.
 6304                            menu.preserve_markdown_cache(prev_menu);
 6305                        }
 6306                    };
 6307
 6308                    menu.set_filter_results(matches, provider, window, cx);
 6309                }) else {
 6310                    return;
 6311                };
 6312
 6313                menu.visible().then_some(menu)
 6314            };
 6315
 6316            editor
 6317                .update_in(cx, |editor, window, cx| {
 6318                    if editor.focus_handle.is_focused(window)
 6319                        && let Some(menu) = menu
 6320                    {
 6321                        *editor.context_menu.borrow_mut() =
 6322                            Some(CodeContextMenu::Completions(menu));
 6323
 6324                        crate::hover_popover::hide_hover(editor, cx);
 6325                        if editor.show_edit_predictions_in_menu() {
 6326                            editor.update_visible_edit_prediction(window, cx);
 6327                        } else {
 6328                            editor
 6329                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6330                        }
 6331
 6332                        cx.notify();
 6333                        return;
 6334                    }
 6335
 6336                    if editor.completion_tasks.len() <= 1 {
 6337                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6338                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6339                        // If it was already hidden and we don't show edit predictions in the menu,
 6340                        // we should also show the edit prediction when available.
 6341                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6342                            editor.update_visible_edit_prediction(window, cx);
 6343                        }
 6344                    }
 6345                })
 6346                .ok();
 6347        });
 6348
 6349        self.completion_tasks.push((id, task));
 6350    }
 6351
 6352    #[cfg(any(test, feature = "test-support"))]
 6353    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6354        let menu = self.context_menu.borrow();
 6355        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6356            let completions = menu.completions.borrow();
 6357            Some(completions.to_vec())
 6358        } else {
 6359            None
 6360        }
 6361    }
 6362
 6363    pub fn with_completions_menu_matching_id<R>(
 6364        &self,
 6365        id: CompletionId,
 6366        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6367    ) -> R {
 6368        let mut context_menu = self.context_menu.borrow_mut();
 6369        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6370            return f(None);
 6371        };
 6372        if completions_menu.id != id {
 6373            return f(None);
 6374        }
 6375        f(Some(completions_menu))
 6376    }
 6377
 6378    pub fn confirm_completion(
 6379        &mut self,
 6380        action: &ConfirmCompletion,
 6381        window: &mut Window,
 6382        cx: &mut Context<Self>,
 6383    ) -> Option<Task<Result<()>>> {
 6384        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6385        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6386    }
 6387
 6388    pub fn confirm_completion_insert(
 6389        &mut self,
 6390        _: &ConfirmCompletionInsert,
 6391        window: &mut Window,
 6392        cx: &mut Context<Self>,
 6393    ) -> Option<Task<Result<()>>> {
 6394        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6395        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6396    }
 6397
 6398    pub fn confirm_completion_replace(
 6399        &mut self,
 6400        _: &ConfirmCompletionReplace,
 6401        window: &mut Window,
 6402        cx: &mut Context<Self>,
 6403    ) -> Option<Task<Result<()>>> {
 6404        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6405        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6406    }
 6407
 6408    pub fn compose_completion(
 6409        &mut self,
 6410        action: &ComposeCompletion,
 6411        window: &mut Window,
 6412        cx: &mut Context<Self>,
 6413    ) -> Option<Task<Result<()>>> {
 6414        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6415        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6416    }
 6417
 6418    fn do_completion(
 6419        &mut self,
 6420        item_ix: Option<usize>,
 6421        intent: CompletionIntent,
 6422        window: &mut Window,
 6423        cx: &mut Context<Editor>,
 6424    ) -> Option<Task<Result<()>>> {
 6425        use language::ToOffset as _;
 6426
 6427        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6428        else {
 6429            return None;
 6430        };
 6431
 6432        let candidate_id = {
 6433            let entries = completions_menu.entries.borrow();
 6434            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6435            if self.show_edit_predictions_in_menu() {
 6436                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6437            }
 6438            mat.candidate_id
 6439        };
 6440
 6441        let completion = completions_menu
 6442            .completions
 6443            .borrow()
 6444            .get(candidate_id)?
 6445            .clone();
 6446        cx.stop_propagation();
 6447
 6448        let buffer_handle = completions_menu.buffer.clone();
 6449
 6450        let CompletionEdit {
 6451            new_text,
 6452            snippet,
 6453            replace_range,
 6454        } = process_completion_for_edit(
 6455            &completion,
 6456            intent,
 6457            &buffer_handle,
 6458            &completions_menu.initial_position.text_anchor,
 6459            cx,
 6460        );
 6461
 6462        let buffer = buffer_handle.read(cx);
 6463        let snapshot = self.buffer.read(cx).snapshot(cx);
 6464        let newest_anchor = self.selections.newest_anchor();
 6465        let replace_range_multibuffer = {
 6466            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6467            excerpt.map_range_from_buffer(replace_range.clone())
 6468        };
 6469        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6470            return None;
 6471        }
 6472
 6473        let old_text = buffer
 6474            .text_for_range(replace_range.clone())
 6475            .collect::<String>();
 6476        let lookbehind = newest_anchor
 6477            .start
 6478            .text_anchor
 6479            .to_offset(buffer)
 6480            .saturating_sub(replace_range.start.0);
 6481        let lookahead = replace_range
 6482            .end
 6483            .0
 6484            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6485        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6486        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6487
 6488        let selections = self
 6489            .selections
 6490            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6491        let mut ranges = Vec::new();
 6492        let mut linked_edits = LinkedEdits::new();
 6493
 6494        let text: Arc<str> = new_text.clone().into();
 6495        for selection in &selections {
 6496            let range = if selection.id == newest_anchor.id {
 6497                replace_range_multibuffer.clone()
 6498            } else {
 6499                let mut range = selection.range();
 6500
 6501                // if prefix is present, don't duplicate it
 6502                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6503                    range.start = range.start.saturating_sub_usize(lookbehind);
 6504
 6505                    // if suffix is also present, mimic the newest cursor and replace it
 6506                    if selection.id != newest_anchor.id
 6507                        && snapshot.contains_str_at(range.end, suffix)
 6508                    {
 6509                        range.end += lookahead;
 6510                    }
 6511                }
 6512                range
 6513            };
 6514
 6515            ranges.push(range.clone());
 6516
 6517            if !self.linked_edit_ranges.is_empty() {
 6518                let start_anchor = snapshot.anchor_before(range.start);
 6519                let end_anchor = snapshot.anchor_after(range.end);
 6520                let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6521                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6522            }
 6523        }
 6524
 6525        let common_prefix_len = old_text
 6526            .chars()
 6527            .zip(new_text.chars())
 6528            .take_while(|(a, b)| a == b)
 6529            .map(|(a, _)| a.len_utf8())
 6530            .sum::<usize>();
 6531
 6532        cx.emit(EditorEvent::InputHandled {
 6533            utf16_range_to_replace: None,
 6534            text: new_text[common_prefix_len..].into(),
 6535        });
 6536
 6537        self.transact(window, cx, |editor, window, cx| {
 6538            if let Some(mut snippet) = snippet {
 6539                snippet.text = new_text.to_string();
 6540                editor
 6541                    .insert_snippet(&ranges, snippet, window, cx)
 6542                    .log_err();
 6543            } else {
 6544                editor.buffer.update(cx, |multi_buffer, cx| {
 6545                    let auto_indent = match completion.insert_text_mode {
 6546                        Some(InsertTextMode::AS_IS) => None,
 6547                        _ => editor.autoindent_mode.clone(),
 6548                    };
 6549                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6550                    multi_buffer.edit(edits, auto_indent, cx);
 6551                });
 6552            }
 6553            linked_edits.apply(cx);
 6554            editor.refresh_edit_prediction(true, false, window, cx);
 6555        });
 6556        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6557
 6558        let show_new_completions_on_confirm = completion
 6559            .confirm
 6560            .as_ref()
 6561            .is_some_and(|confirm| confirm(intent, window, cx));
 6562        if show_new_completions_on_confirm {
 6563            self.open_or_update_completions_menu(None, None, false, window, cx);
 6564        }
 6565
 6566        let provider = self.completion_provider.as_ref()?;
 6567
 6568        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6569        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6570            let CompletionSource::Lsp {
 6571                lsp_completion,
 6572                server_id,
 6573                ..
 6574            } = &completion.source
 6575            else {
 6576                return None;
 6577            };
 6578            let lsp_command = lsp_completion.command.as_ref()?;
 6579            let available_commands = lsp_store
 6580                .read(cx)
 6581                .lsp_server_capabilities
 6582                .get(server_id)
 6583                .and_then(|server_capabilities| {
 6584                    server_capabilities
 6585                        .execute_command_provider
 6586                        .as_ref()
 6587                        .map(|options| options.commands.as_slice())
 6588                })?;
 6589            if available_commands.contains(&lsp_command.command) {
 6590                Some(CodeAction {
 6591                    server_id: *server_id,
 6592                    range: language::Anchor::MIN..language::Anchor::MIN,
 6593                    lsp_action: LspAction::Command(lsp_command.clone()),
 6594                    resolved: false,
 6595                })
 6596            } else {
 6597                None
 6598            }
 6599        });
 6600
 6601        drop(completion);
 6602        let apply_edits = provider.apply_additional_edits_for_completion(
 6603            buffer_handle.clone(),
 6604            completions_menu.completions.clone(),
 6605            candidate_id,
 6606            true,
 6607            cx,
 6608        );
 6609
 6610        let editor_settings = EditorSettings::get_global(cx);
 6611        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6612            // After the code completion is finished, users often want to know what signatures are needed.
 6613            // so we should automatically call signature_help
 6614            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6615        }
 6616
 6617        Some(cx.spawn_in(window, async move |editor, cx| {
 6618            apply_edits.await?;
 6619
 6620            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6621                let title = command.lsp_action.title().to_owned();
 6622                let project_transaction = lsp_store
 6623                    .update(cx, |lsp_store, cx| {
 6624                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6625                    })
 6626                    .await
 6627                    .context("applying post-completion command")?;
 6628                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6629                    Self::open_project_transaction(
 6630                        &editor,
 6631                        workspace.downgrade(),
 6632                        project_transaction,
 6633                        title,
 6634                        cx,
 6635                    )
 6636                    .await?;
 6637                }
 6638            }
 6639
 6640            Ok(())
 6641        }))
 6642    }
 6643
 6644    pub fn toggle_code_actions(
 6645        &mut self,
 6646        action: &ToggleCodeActions,
 6647        window: &mut Window,
 6648        cx: &mut Context<Self>,
 6649    ) {
 6650        let quick_launch = action.quick_launch;
 6651        let mut context_menu = self.context_menu.borrow_mut();
 6652        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6653            if code_actions.deployed_from == action.deployed_from {
 6654                // Toggle if we're selecting the same one
 6655                *context_menu = None;
 6656                cx.notify();
 6657                return;
 6658            } else {
 6659                // Otherwise, clear it and start a new one
 6660                *context_menu = None;
 6661                cx.notify();
 6662            }
 6663        }
 6664        drop(context_menu);
 6665        let snapshot = self.snapshot(window, cx);
 6666        let deployed_from = action.deployed_from.clone();
 6667        let action = action.clone();
 6668        self.completion_tasks.clear();
 6669        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6670
 6671        let multibuffer_point = match &action.deployed_from {
 6672            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6673                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6674            }
 6675            _ => self
 6676                .selections
 6677                .newest::<Point>(&snapshot.display_snapshot)
 6678                .head(),
 6679        };
 6680        let Some((buffer, buffer_row)) = snapshot
 6681            .buffer_snapshot()
 6682            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6683            .and_then(|(buffer_snapshot, range)| {
 6684                self.buffer()
 6685                    .read(cx)
 6686                    .buffer(buffer_snapshot.remote_id())
 6687                    .map(|buffer| (buffer, range.start.row))
 6688            })
 6689        else {
 6690            return;
 6691        };
 6692        let buffer_id = buffer.read(cx).remote_id();
 6693        let tasks = self
 6694            .tasks
 6695            .get(&(buffer_id, buffer_row))
 6696            .map(|t| Arc::new(t.to_owned()));
 6697
 6698        if !self.focus_handle.is_focused(window) {
 6699            return;
 6700        }
 6701        let project = self.project.clone();
 6702
 6703        let code_actions_task = match deployed_from {
 6704            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6705            _ => self.code_actions(buffer_row, window, cx),
 6706        };
 6707
 6708        let runnable_task = match deployed_from {
 6709            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6710            _ => {
 6711                let mut task_context_task = Task::ready(None);
 6712                if let Some(tasks) = &tasks
 6713                    && let Some(project) = project
 6714                {
 6715                    task_context_task =
 6716                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6717                }
 6718
 6719                cx.spawn_in(window, {
 6720                    let buffer = buffer.clone();
 6721                    async move |editor, cx| {
 6722                        let task_context = task_context_task.await;
 6723
 6724                        let resolved_tasks =
 6725                            tasks
 6726                                .zip(task_context.clone())
 6727                                .map(|(tasks, task_context)| ResolvedTasks {
 6728                                    templates: tasks.resolve(&task_context).collect(),
 6729                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6730                                        multibuffer_point.row,
 6731                                        tasks.column,
 6732                                    )),
 6733                                });
 6734                        let debug_scenarios = editor
 6735                            .update(cx, |editor, cx| {
 6736                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6737                            })?
 6738                            .await;
 6739                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6740                    }
 6741                })
 6742            }
 6743        };
 6744
 6745        cx.spawn_in(window, async move |editor, cx| {
 6746            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6747            let code_actions = code_actions_task.await;
 6748            let spawn_straight_away = quick_launch
 6749                && resolved_tasks
 6750                    .as_ref()
 6751                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6752                && code_actions
 6753                    .as_ref()
 6754                    .is_none_or(|actions| actions.is_empty())
 6755                && debug_scenarios.is_empty();
 6756
 6757            editor.update_in(cx, |editor, window, cx| {
 6758                crate::hover_popover::hide_hover(editor, cx);
 6759                let actions = CodeActionContents::new(
 6760                    resolved_tasks,
 6761                    code_actions,
 6762                    debug_scenarios,
 6763                    task_context.unwrap_or_default(),
 6764                );
 6765
 6766                // Don't show the menu if there are no actions available
 6767                if actions.is_empty() {
 6768                    cx.notify();
 6769                    return Task::ready(Ok(()));
 6770                }
 6771
 6772                *editor.context_menu.borrow_mut() =
 6773                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6774                        buffer,
 6775                        actions,
 6776                        selected_item: Default::default(),
 6777                        scroll_handle: UniformListScrollHandle::default(),
 6778                        deployed_from,
 6779                    }));
 6780                cx.notify();
 6781                if spawn_straight_away
 6782                    && let Some(task) = editor.confirm_code_action(
 6783                        &ConfirmCodeAction { item_ix: Some(0) },
 6784                        window,
 6785                        cx,
 6786                    )
 6787                {
 6788                    return task;
 6789                }
 6790
 6791                Task::ready(Ok(()))
 6792            })
 6793        })
 6794        .detach_and_log_err(cx);
 6795    }
 6796
 6797    fn debug_scenarios(
 6798        &mut self,
 6799        resolved_tasks: &Option<ResolvedTasks>,
 6800        buffer: &Entity<Buffer>,
 6801        cx: &mut App,
 6802    ) -> Task<Vec<task::DebugScenario>> {
 6803        maybe!({
 6804            let project = self.project()?;
 6805            let dap_store = project.read(cx).dap_store();
 6806            let mut scenarios = vec![];
 6807            let resolved_tasks = resolved_tasks.as_ref()?;
 6808            let buffer = buffer.read(cx);
 6809            let language = buffer.language()?;
 6810            let file = buffer.file();
 6811            let debug_adapter = language_settings(language.name().into(), file, cx)
 6812                .debuggers
 6813                .first()
 6814                .map(SharedString::from)
 6815                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6816
 6817            dap_store.update(cx, |dap_store, cx| {
 6818                for (_, task) in &resolved_tasks.templates {
 6819                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6820                        task.original_task().clone(),
 6821                        debug_adapter.clone().into(),
 6822                        task.display_label().to_owned().into(),
 6823                        cx,
 6824                    );
 6825                    scenarios.push(maybe_scenario);
 6826                }
 6827            });
 6828            Some(cx.background_spawn(async move {
 6829                futures::future::join_all(scenarios)
 6830                    .await
 6831                    .into_iter()
 6832                    .flatten()
 6833                    .collect::<Vec<_>>()
 6834            }))
 6835        })
 6836        .unwrap_or_else(|| Task::ready(vec![]))
 6837    }
 6838
 6839    fn code_actions(
 6840        &mut self,
 6841        buffer_row: u32,
 6842        window: &mut Window,
 6843        cx: &mut Context<Self>,
 6844    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6845        let mut task = self.code_actions_task.take();
 6846        cx.spawn_in(window, async move |editor, cx| {
 6847            while let Some(prev_task) = task {
 6848                prev_task.await.log_err();
 6849                task = editor
 6850                    .update(cx, |this, _| this.code_actions_task.take())
 6851                    .ok()?;
 6852            }
 6853
 6854            editor
 6855                .update(cx, |editor, cx| {
 6856                    editor
 6857                        .available_code_actions
 6858                        .clone()
 6859                        .and_then(|(location, code_actions)| {
 6860                            let snapshot = location.buffer.read(cx).snapshot();
 6861                            let point_range = location.range.to_point(&snapshot);
 6862                            let point_range = point_range.start.row..=point_range.end.row;
 6863                            if point_range.contains(&buffer_row) {
 6864                                Some(code_actions)
 6865                            } else {
 6866                                None
 6867                            }
 6868                        })
 6869                })
 6870                .ok()
 6871                .flatten()
 6872        })
 6873    }
 6874
 6875    pub fn confirm_code_action(
 6876        &mut self,
 6877        action: &ConfirmCodeAction,
 6878        window: &mut Window,
 6879        cx: &mut Context<Self>,
 6880    ) -> Option<Task<Result<()>>> {
 6881        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6882
 6883        let actions_menu =
 6884            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6885                menu
 6886            } else {
 6887                return None;
 6888            };
 6889
 6890        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6891        let action = actions_menu.actions.get(action_ix)?;
 6892        let title = action.label();
 6893        let buffer = actions_menu.buffer;
 6894        let workspace = self.workspace()?;
 6895
 6896        match action {
 6897            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6898                workspace.update(cx, |workspace, cx| {
 6899                    workspace.schedule_resolved_task(
 6900                        task_source_kind,
 6901                        resolved_task,
 6902                        false,
 6903                        window,
 6904                        cx,
 6905                    );
 6906
 6907                    Some(Task::ready(Ok(())))
 6908                })
 6909            }
 6910            CodeActionsItem::CodeAction {
 6911                excerpt_id,
 6912                action,
 6913                provider,
 6914            } => {
 6915                let apply_code_action =
 6916                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6917                let workspace = workspace.downgrade();
 6918                Some(cx.spawn_in(window, async move |editor, cx| {
 6919                    let project_transaction = apply_code_action.await?;
 6920                    Self::open_project_transaction(
 6921                        &editor,
 6922                        workspace,
 6923                        project_transaction,
 6924                        title,
 6925                        cx,
 6926                    )
 6927                    .await
 6928                }))
 6929            }
 6930            CodeActionsItem::DebugScenario(scenario) => {
 6931                let context = actions_menu.actions.context.into();
 6932
 6933                workspace.update(cx, |workspace, cx| {
 6934                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6935                    workspace.start_debug_session(
 6936                        scenario,
 6937                        context,
 6938                        Some(buffer),
 6939                        None,
 6940                        window,
 6941                        cx,
 6942                    );
 6943                });
 6944                Some(Task::ready(Ok(())))
 6945            }
 6946        }
 6947    }
 6948
 6949    fn open_transaction_for_hidden_buffers(
 6950        workspace: Entity<Workspace>,
 6951        transaction: ProjectTransaction,
 6952        title: String,
 6953        window: &mut Window,
 6954        cx: &mut Context<Self>,
 6955    ) {
 6956        if transaction.0.is_empty() {
 6957            return;
 6958        }
 6959
 6960        let edited_buffers_already_open = {
 6961            let other_editors: Vec<Entity<Editor>> = workspace
 6962                .read(cx)
 6963                .panes()
 6964                .iter()
 6965                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6966                .filter(|editor| editor.entity_id() != cx.entity_id())
 6967                .collect();
 6968
 6969            transaction.0.keys().all(|buffer| {
 6970                other_editors.iter().any(|editor| {
 6971                    let multi_buffer = editor.read(cx).buffer();
 6972                    multi_buffer.read(cx).is_singleton()
 6973                        && multi_buffer
 6974                            .read(cx)
 6975                            .as_singleton()
 6976                            .map_or(false, |singleton| {
 6977                                singleton.entity_id() == buffer.entity_id()
 6978                            })
 6979                })
 6980            })
 6981        };
 6982        if !edited_buffers_already_open {
 6983            let workspace = workspace.downgrade();
 6984            cx.defer_in(window, move |_, window, cx| {
 6985                cx.spawn_in(window, async move |editor, cx| {
 6986                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6987                        .await
 6988                        .ok()
 6989                })
 6990                .detach();
 6991            });
 6992        }
 6993    }
 6994
 6995    pub async fn open_project_transaction(
 6996        editor: &WeakEntity<Editor>,
 6997        workspace: WeakEntity<Workspace>,
 6998        transaction: ProjectTransaction,
 6999        title: String,
 7000        cx: &mut AsyncWindowContext,
 7001    ) -> Result<()> {
 7002        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7003        cx.update(|_, cx| {
 7004            entries.sort_unstable_by_key(|(buffer, _)| {
 7005                buffer.read(cx).file().map(|f| f.path().clone())
 7006            });
 7007        })?;
 7008        if entries.is_empty() {
 7009            return Ok(());
 7010        }
 7011
 7012        // If the project transaction's edits are all contained within this editor, then
 7013        // avoid opening a new editor to display them.
 7014
 7015        if let [(buffer, transaction)] = &*entries {
 7016            let excerpt = editor.update(cx, |editor, cx| {
 7017                editor
 7018                    .buffer()
 7019                    .read(cx)
 7020                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7021            })?;
 7022            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7023                && excerpted_buffer == *buffer
 7024            {
 7025                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7026                    let excerpt_range = excerpt_range.to_offset(buffer);
 7027                    buffer
 7028                        .edited_ranges_for_transaction::<usize>(transaction)
 7029                        .all(|range| {
 7030                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7031                        })
 7032                });
 7033
 7034                if all_edits_within_excerpt {
 7035                    return Ok(());
 7036                }
 7037            }
 7038        }
 7039
 7040        let mut ranges_to_highlight = Vec::new();
 7041        let excerpt_buffer = cx.new(|cx| {
 7042            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7043            for (buffer_handle, transaction) in &entries {
 7044                let edited_ranges = buffer_handle
 7045                    .read(cx)
 7046                    .edited_ranges_for_transaction::<Point>(transaction)
 7047                    .collect::<Vec<_>>();
 7048                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7049                    PathKey::for_buffer(buffer_handle, cx),
 7050                    buffer_handle.clone(),
 7051                    edited_ranges,
 7052                    multibuffer_context_lines(cx),
 7053                    cx,
 7054                );
 7055
 7056                ranges_to_highlight.extend(ranges);
 7057            }
 7058            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7059            multibuffer
 7060        });
 7061
 7062        workspace.update_in(cx, |workspace, window, cx| {
 7063            let project = workspace.project().clone();
 7064            let editor =
 7065                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7066            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7067            editor.update(cx, |editor, cx| {
 7068                editor.highlight_background(
 7069                    HighlightKey::Editor,
 7070                    &ranges_to_highlight,
 7071                    |_, theme| theme.colors().editor_highlighted_line_background,
 7072                    cx,
 7073                );
 7074            });
 7075        })?;
 7076
 7077        Ok(())
 7078    }
 7079
 7080    pub fn clear_code_action_providers(&mut self) {
 7081        self.code_action_providers.clear();
 7082        self.available_code_actions.take();
 7083    }
 7084
 7085    pub fn add_code_action_provider(
 7086        &mut self,
 7087        provider: Rc<dyn CodeActionProvider>,
 7088        window: &mut Window,
 7089        cx: &mut Context<Self>,
 7090    ) {
 7091        if self
 7092            .code_action_providers
 7093            .iter()
 7094            .any(|existing_provider| existing_provider.id() == provider.id())
 7095        {
 7096            return;
 7097        }
 7098
 7099        self.code_action_providers.push(provider);
 7100        self.refresh_code_actions(window, cx);
 7101    }
 7102
 7103    pub fn remove_code_action_provider(
 7104        &mut self,
 7105        id: Arc<str>,
 7106        window: &mut Window,
 7107        cx: &mut Context<Self>,
 7108    ) {
 7109        self.code_action_providers
 7110            .retain(|provider| provider.id() != id);
 7111        self.refresh_code_actions(window, cx);
 7112    }
 7113
 7114    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7115        !self.code_action_providers.is_empty()
 7116            && EditorSettings::get_global(cx).toolbar.code_actions
 7117    }
 7118
 7119    pub fn has_available_code_actions(&self) -> bool {
 7120        self.available_code_actions
 7121            .as_ref()
 7122            .is_some_and(|(_, actions)| !actions.is_empty())
 7123    }
 7124
 7125    fn render_inline_code_actions(
 7126        &self,
 7127        icon_size: ui::IconSize,
 7128        display_row: DisplayRow,
 7129        is_active: bool,
 7130        cx: &mut Context<Self>,
 7131    ) -> AnyElement {
 7132        let show_tooltip = !self.context_menu_visible();
 7133        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7134            .icon_size(icon_size)
 7135            .shape(ui::IconButtonShape::Square)
 7136            .icon_color(ui::Color::Hidden)
 7137            .toggle_state(is_active)
 7138            .when(show_tooltip, |this| {
 7139                this.tooltip({
 7140                    let focus_handle = self.focus_handle.clone();
 7141                    move |_window, cx| {
 7142                        Tooltip::for_action_in(
 7143                            "Toggle Code Actions",
 7144                            &ToggleCodeActions {
 7145                                deployed_from: None,
 7146                                quick_launch: false,
 7147                            },
 7148                            &focus_handle,
 7149                            cx,
 7150                        )
 7151                    }
 7152                })
 7153            })
 7154            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7155                window.focus(&editor.focus_handle(cx), cx);
 7156                editor.toggle_code_actions(
 7157                    &crate::actions::ToggleCodeActions {
 7158                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7159                            display_row,
 7160                        )),
 7161                        quick_launch: false,
 7162                    },
 7163                    window,
 7164                    cx,
 7165                );
 7166            }))
 7167            .into_any_element()
 7168    }
 7169
 7170    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7171        &self.context_menu
 7172    }
 7173
 7174    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7175        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7176            cx.background_executor()
 7177                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7178                .await;
 7179
 7180            let (start_buffer, start, _, end, newest_selection) = this
 7181                .update(cx, |this, cx| {
 7182                    let newest_selection = this.selections.newest_anchor().clone();
 7183                    if newest_selection.head().diff_base_anchor.is_some() {
 7184                        return None;
 7185                    }
 7186                    let display_snapshot = this.display_snapshot(cx);
 7187                    let newest_selection_adjusted =
 7188                        this.selections.newest_adjusted(&display_snapshot);
 7189                    let buffer = this.buffer.read(cx);
 7190
 7191                    let (start_buffer, start) =
 7192                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7193                    let (end_buffer, end) =
 7194                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7195
 7196                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7197                })?
 7198                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7199                .context(
 7200                    "Expected selection to lie in a single buffer when refreshing code actions",
 7201                )?;
 7202            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7203                let providers = this.code_action_providers.clone();
 7204                let tasks = this
 7205                    .code_action_providers
 7206                    .iter()
 7207                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7208                    .collect::<Vec<_>>();
 7209                (providers, tasks)
 7210            })?;
 7211
 7212            let mut actions = Vec::new();
 7213            for (provider, provider_actions) in
 7214                providers.into_iter().zip(future::join_all(tasks).await)
 7215            {
 7216                if let Some(provider_actions) = provider_actions.log_err() {
 7217                    actions.extend(provider_actions.into_iter().map(|action| {
 7218                        AvailableCodeAction {
 7219                            excerpt_id: newest_selection.start.excerpt_id,
 7220                            action,
 7221                            provider: provider.clone(),
 7222                        }
 7223                    }));
 7224                }
 7225            }
 7226
 7227            this.update(cx, |this, cx| {
 7228                this.available_code_actions = if actions.is_empty() {
 7229                    None
 7230                } else {
 7231                    Some((
 7232                        Location {
 7233                            buffer: start_buffer,
 7234                            range: start..end,
 7235                        },
 7236                        actions.into(),
 7237                    ))
 7238                };
 7239                cx.notify();
 7240            })
 7241        }));
 7242    }
 7243
 7244    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7245        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7246            self.show_git_blame_inline = false;
 7247
 7248            self.show_git_blame_inline_delay_task =
 7249                Some(cx.spawn_in(window, async move |this, cx| {
 7250                    cx.background_executor().timer(delay).await;
 7251
 7252                    this.update(cx, |this, cx| {
 7253                        this.show_git_blame_inline = true;
 7254                        cx.notify();
 7255                    })
 7256                    .log_err();
 7257                }));
 7258        }
 7259    }
 7260
 7261    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7262        let snapshot = self.snapshot(window, cx);
 7263        let cursor = self
 7264            .selections
 7265            .newest::<Point>(&snapshot.display_snapshot)
 7266            .head();
 7267        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7268        else {
 7269            return;
 7270        };
 7271
 7272        if self.blame.is_none() {
 7273            self.start_git_blame(true, window, cx);
 7274        }
 7275        let Some(blame) = self.blame.as_ref() else {
 7276            return;
 7277        };
 7278
 7279        let row_info = RowInfo {
 7280            buffer_id: Some(buffer.remote_id()),
 7281            buffer_row: Some(point.row),
 7282            ..Default::default()
 7283        };
 7284        let Some((buffer, blame_entry)) = blame
 7285            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7286            .flatten()
 7287        else {
 7288            return;
 7289        };
 7290
 7291        let anchor = self.selections.newest_anchor().head();
 7292        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7293        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7294            self.show_blame_popover(
 7295                buffer,
 7296                &blame_entry,
 7297                position + last_bounds.origin,
 7298                true,
 7299                cx,
 7300            );
 7301        };
 7302    }
 7303
 7304    fn show_blame_popover(
 7305        &mut self,
 7306        buffer: BufferId,
 7307        blame_entry: &BlameEntry,
 7308        position: gpui::Point<Pixels>,
 7309        ignore_timeout: bool,
 7310        cx: &mut Context<Self>,
 7311    ) {
 7312        if let Some(state) = &mut self.inline_blame_popover {
 7313            state.hide_task.take();
 7314        } else {
 7315            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7316            let blame_entry = blame_entry.clone();
 7317            let show_task = cx.spawn(async move |editor, cx| {
 7318                if !ignore_timeout {
 7319                    cx.background_executor()
 7320                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7321                        .await;
 7322                }
 7323                editor
 7324                    .update(cx, |editor, cx| {
 7325                        editor.inline_blame_popover_show_task.take();
 7326                        let Some(blame) = editor.blame.as_ref() else {
 7327                            return;
 7328                        };
 7329                        let blame = blame.read(cx);
 7330                        let details = blame.details_for_entry(buffer, &blame_entry);
 7331                        let markdown = cx.new(|cx| {
 7332                            Markdown::new(
 7333                                details
 7334                                    .as_ref()
 7335                                    .map(|message| message.message.clone())
 7336                                    .unwrap_or_default(),
 7337                                None,
 7338                                None,
 7339                                cx,
 7340                            )
 7341                        });
 7342                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7343                            position,
 7344                            hide_task: None,
 7345                            popover_bounds: None,
 7346                            popover_state: InlineBlamePopoverState {
 7347                                scroll_handle: ScrollHandle::new(),
 7348                                commit_message: details,
 7349                                markdown,
 7350                            },
 7351                            keyboard_grace: ignore_timeout,
 7352                        });
 7353                        cx.notify();
 7354                    })
 7355                    .ok();
 7356            });
 7357            self.inline_blame_popover_show_task = Some(show_task);
 7358        }
 7359    }
 7360
 7361    pub fn has_mouse_context_menu(&self) -> bool {
 7362        self.mouse_context_menu.is_some()
 7363    }
 7364
 7365    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7366        self.inline_blame_popover_show_task.take();
 7367        if let Some(state) = &mut self.inline_blame_popover {
 7368            let hide_task = cx.spawn(async move |editor, cx| {
 7369                if !ignore_timeout {
 7370                    cx.background_executor()
 7371                        .timer(std::time::Duration::from_millis(100))
 7372                        .await;
 7373                }
 7374                editor
 7375                    .update(cx, |editor, cx| {
 7376                        editor.inline_blame_popover.take();
 7377                        cx.notify();
 7378                    })
 7379                    .ok();
 7380            });
 7381            state.hide_task = Some(hide_task);
 7382            true
 7383        } else {
 7384            false
 7385        }
 7386    }
 7387
 7388    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7389        if self.pending_rename.is_some() {
 7390            return None;
 7391        }
 7392
 7393        let provider = self.semantics_provider.clone()?;
 7394        let buffer = self.buffer.read(cx);
 7395        let newest_selection = self.selections.newest_anchor().clone();
 7396        let cursor_position = newest_selection.head();
 7397        let (cursor_buffer, cursor_buffer_position) =
 7398            buffer.text_anchor_for_position(cursor_position, cx)?;
 7399        let (tail_buffer, tail_buffer_position) =
 7400            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7401        if cursor_buffer != tail_buffer {
 7402            return None;
 7403        }
 7404
 7405        let snapshot = cursor_buffer.read(cx).snapshot();
 7406        let word_ranges = cx.background_spawn(async move {
 7407            // this might look odd to put on the background thread, but
 7408            // `surrounding_word` can be quite expensive as it calls into
 7409            // tree-sitter language scopes
 7410            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7411            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7412            (start_word_range, end_word_range)
 7413        });
 7414
 7415        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7416        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7417            let (start_word_range, end_word_range) = word_ranges.await;
 7418            if start_word_range != end_word_range {
 7419                this.update(cx, |this, cx| {
 7420                    this.document_highlights_task.take();
 7421                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7422                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7423                })
 7424                .ok();
 7425                return;
 7426            }
 7427            cx.background_executor()
 7428                .timer(Duration::from_millis(debounce))
 7429                .await;
 7430
 7431            let highlights = if let Some(highlights) = cx.update(|cx| {
 7432                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7433            }) {
 7434                highlights.await.log_err()
 7435            } else {
 7436                None
 7437            };
 7438
 7439            if let Some(highlights) = highlights {
 7440                this.update(cx, |this, cx| {
 7441                    if this.pending_rename.is_some() {
 7442                        return;
 7443                    }
 7444
 7445                    let buffer = this.buffer.read(cx);
 7446                    if buffer
 7447                        .text_anchor_for_position(cursor_position, cx)
 7448                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7449                    {
 7450                        return;
 7451                    }
 7452
 7453                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7454                    let mut write_ranges = Vec::new();
 7455                    let mut read_ranges = Vec::new();
 7456                    for highlight in highlights {
 7457                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7458                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7459                        {
 7460                            let start = highlight
 7461                                .range
 7462                                .start
 7463                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7464                            let end = highlight
 7465                                .range
 7466                                .end
 7467                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7468                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7469                                continue;
 7470                            }
 7471
 7472                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7473                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7474                                write_ranges.push(range);
 7475                            } else {
 7476                                read_ranges.push(range);
 7477                            }
 7478                        }
 7479                    }
 7480
 7481                    this.highlight_background(
 7482                        HighlightKey::DocumentHighlightRead,
 7483                        &read_ranges,
 7484                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7485                        cx,
 7486                    );
 7487                    this.highlight_background(
 7488                        HighlightKey::DocumentHighlightWrite,
 7489                        &write_ranges,
 7490                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7491                        cx,
 7492                    );
 7493                    cx.notify();
 7494                })
 7495                .log_err();
 7496            }
 7497        }));
 7498        None
 7499    }
 7500
 7501    fn prepare_highlight_query_from_selection(
 7502        &mut self,
 7503        window: &Window,
 7504        cx: &mut Context<Editor>,
 7505    ) -> Option<(String, Range<Anchor>)> {
 7506        if matches!(self.mode, EditorMode::SingleLine) {
 7507            return None;
 7508        }
 7509        if !EditorSettings::get_global(cx).selection_highlight {
 7510            return None;
 7511        }
 7512        if self.selections.count() != 1 || self.selections.line_mode() {
 7513            return None;
 7514        }
 7515        let snapshot = self.snapshot(window, cx);
 7516        let selection = self.selections.newest::<Point>(&snapshot);
 7517        // If the selection spans multiple rows OR it is empty
 7518        if selection.start.row != selection.end.row
 7519            || selection.start.column == selection.end.column
 7520        {
 7521            return None;
 7522        }
 7523        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7524        let query = snapshot
 7525            .buffer_snapshot()
 7526            .text_for_range(selection_anchor_range.clone())
 7527            .collect::<String>();
 7528        if query.trim().is_empty() {
 7529            return None;
 7530        }
 7531        Some((query, selection_anchor_range))
 7532    }
 7533
 7534    #[ztracing::instrument(skip_all)]
 7535    fn update_selection_occurrence_highlights(
 7536        &mut self,
 7537        query_text: String,
 7538        query_range: Range<Anchor>,
 7539        multi_buffer_range_to_query: Range<Point>,
 7540        use_debounce: bool,
 7541        window: &mut Window,
 7542        cx: &mut Context<Editor>,
 7543    ) -> Task<()> {
 7544        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7545        cx.spawn_in(window, async move |editor, cx| {
 7546            if use_debounce {
 7547                cx.background_executor()
 7548                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7549                    .await;
 7550            }
 7551            let match_task = cx.background_spawn(async move {
 7552                let buffer_ranges = multi_buffer_snapshot
 7553                    .range_to_buffer_ranges(
 7554                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7555                    )
 7556                    .into_iter()
 7557                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7558                let mut match_ranges = Vec::new();
 7559                let Ok(regex) = project::search::SearchQuery::text(
 7560                    query_text.clone(),
 7561                    false,
 7562                    false,
 7563                    false,
 7564                    Default::default(),
 7565                    Default::default(),
 7566                    false,
 7567                    None,
 7568                ) else {
 7569                    return Vec::default();
 7570                };
 7571                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7572                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7573                    match_ranges.extend(
 7574                        regex
 7575                            .search(
 7576                                buffer_snapshot,
 7577                                Some(search_range.start.0..search_range.end.0),
 7578                            )
 7579                            .await
 7580                            .into_iter()
 7581                            .filter_map(|match_range| {
 7582                                let match_start = buffer_snapshot
 7583                                    .anchor_after(search_range.start + match_range.start);
 7584                                let match_end = buffer_snapshot
 7585                                    .anchor_before(search_range.start + match_range.end);
 7586                                let match_anchor_range =
 7587                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7588                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7589                            }),
 7590                    );
 7591                }
 7592                match_ranges
 7593            });
 7594            let match_ranges = match_task.await;
 7595            editor
 7596                .update_in(cx, |editor, _, cx| {
 7597                    if use_debounce {
 7598                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7599                        editor.debounced_selection_highlight_complete = true;
 7600                    } else if editor.debounced_selection_highlight_complete {
 7601                        return;
 7602                    }
 7603                    if !match_ranges.is_empty() {
 7604                        editor.highlight_background(
 7605                            HighlightKey::SelectedTextHighlight,
 7606                            &match_ranges,
 7607                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7608                            cx,
 7609                        )
 7610                    }
 7611                })
 7612                .log_err();
 7613        })
 7614    }
 7615
 7616    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7617        struct NewlineFold;
 7618        let type_id = std::any::TypeId::of::<NewlineFold>();
 7619        if !self.mode.is_single_line() {
 7620            return;
 7621        }
 7622        let snapshot = self.snapshot(window, cx);
 7623        if snapshot.buffer_snapshot().max_point().row == 0 {
 7624            return;
 7625        }
 7626        let task = cx.background_spawn(async move {
 7627            let new_newlines = snapshot
 7628                .buffer_chars_at(MultiBufferOffset(0))
 7629                .filter_map(|(c, i)| {
 7630                    if c == '\n' {
 7631                        Some(
 7632                            snapshot.buffer_snapshot().anchor_after(i)
 7633                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7634                        )
 7635                    } else {
 7636                        None
 7637                    }
 7638                })
 7639                .collect::<Vec<_>>();
 7640            let existing_newlines = snapshot
 7641                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7642                .filter_map(|fold| {
 7643                    if fold.placeholder.type_tag == Some(type_id) {
 7644                        Some(fold.range.start..fold.range.end)
 7645                    } else {
 7646                        None
 7647                    }
 7648                })
 7649                .collect::<Vec<_>>();
 7650
 7651            (new_newlines, existing_newlines)
 7652        });
 7653        self.folding_newlines = cx.spawn(async move |this, cx| {
 7654            let (new_newlines, existing_newlines) = task.await;
 7655            if new_newlines == existing_newlines {
 7656                return;
 7657            }
 7658            let placeholder = FoldPlaceholder {
 7659                render: Arc::new(move |_, _, cx| {
 7660                    div()
 7661                        .bg(cx.theme().status().hint_background)
 7662                        .border_b_1()
 7663                        .size_full()
 7664                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7665                        .border_color(cx.theme().status().hint)
 7666                        .child("\\n")
 7667                        .into_any()
 7668                }),
 7669                constrain_width: false,
 7670                merge_adjacent: false,
 7671                type_tag: Some(type_id),
 7672                collapsed_text: None,
 7673            };
 7674            let creases = new_newlines
 7675                .into_iter()
 7676                .map(|range| Crease::simple(range, placeholder.clone()))
 7677                .collect();
 7678            this.update(cx, |this, cx| {
 7679                this.display_map.update(cx, |display_map, cx| {
 7680                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7681                    display_map.fold(creases, cx);
 7682                });
 7683            })
 7684            .ok();
 7685        });
 7686    }
 7687
 7688    #[ztracing::instrument(skip_all)]
 7689    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7690        if !self.mode.is_full() {
 7691            return;
 7692        }
 7693        let cursor = self.selections.newest_anchor().head();
 7694        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7695
 7696        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7697            self.outline_symbols_at_cursor =
 7698                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7699            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7700            cx.notify();
 7701        } else {
 7702            let syntax = cx.theme().syntax().clone();
 7703            let background_task = cx.background_spawn(async move {
 7704                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7705            });
 7706            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7707                cx.spawn(async move |this, cx| {
 7708                    let symbols = background_task.await;
 7709                    this.update(cx, |this, cx| {
 7710                        this.outline_symbols_at_cursor = symbols;
 7711                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7712                        cx.notify();
 7713                    })
 7714                    .ok();
 7715                });
 7716        }
 7717    }
 7718
 7719    #[ztracing::instrument(skip_all)]
 7720    fn refresh_selected_text_highlights(
 7721        &mut self,
 7722        on_buffer_edit: bool,
 7723        window: &mut Window,
 7724        cx: &mut Context<Editor>,
 7725    ) {
 7726        let Some((query_text, query_range)) =
 7727            self.prepare_highlight_query_from_selection(window, cx)
 7728        else {
 7729            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7730            self.quick_selection_highlight_task.take();
 7731            self.debounced_selection_highlight_task.take();
 7732            self.debounced_selection_highlight_complete = false;
 7733            return;
 7734        };
 7735        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7736        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7737        let query_changed = self
 7738            .quick_selection_highlight_task
 7739            .as_ref()
 7740            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7741        if query_changed {
 7742            self.debounced_selection_highlight_complete = false;
 7743        }
 7744        if on_buffer_edit || query_changed {
 7745            let multi_buffer_visible_start = self
 7746                .scroll_manager
 7747                .native_anchor(&display_snapshot, cx)
 7748                .anchor
 7749                .to_point(&multi_buffer_snapshot);
 7750            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7751                multi_buffer_visible_start
 7752                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7753                Bias::Left,
 7754            );
 7755            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7756            self.quick_selection_highlight_task = Some((
 7757                query_range.clone(),
 7758                self.update_selection_occurrence_highlights(
 7759                    query_text.clone(),
 7760                    query_range.clone(),
 7761                    multi_buffer_visible_range,
 7762                    false,
 7763                    window,
 7764                    cx,
 7765                ),
 7766            ));
 7767        }
 7768        if on_buffer_edit
 7769            || self
 7770                .debounced_selection_highlight_task
 7771                .as_ref()
 7772                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7773        {
 7774            let multi_buffer_start = multi_buffer_snapshot
 7775                .anchor_before(MultiBufferOffset(0))
 7776                .to_point(&multi_buffer_snapshot);
 7777            let multi_buffer_end = multi_buffer_snapshot
 7778                .anchor_after(multi_buffer_snapshot.len())
 7779                .to_point(&multi_buffer_snapshot);
 7780            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7781            self.debounced_selection_highlight_task = Some((
 7782                query_range.clone(),
 7783                self.update_selection_occurrence_highlights(
 7784                    query_text,
 7785                    query_range,
 7786                    multi_buffer_full_range,
 7787                    true,
 7788                    window,
 7789                    cx,
 7790                ),
 7791            ));
 7792        }
 7793    }
 7794
 7795    pub fn refresh_edit_prediction(
 7796        &mut self,
 7797        debounce: bool,
 7798        user_requested: bool,
 7799        window: &mut Window,
 7800        cx: &mut Context<Self>,
 7801    ) -> Option<()> {
 7802        let provider = self.edit_prediction_provider()?;
 7803        let cursor = self.selections.newest_anchor().head();
 7804        let (buffer, cursor_buffer_position) =
 7805            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7806
 7807        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7808            return None;
 7809        }
 7810
 7811        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7812            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7813            return None;
 7814        }
 7815
 7816        self.update_visible_edit_prediction(window, cx);
 7817
 7818        if !user_requested
 7819            && (!self.should_show_edit_predictions()
 7820                || !self.is_focused(window)
 7821                || buffer.read(cx).is_empty())
 7822        {
 7823            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7824            return None;
 7825        }
 7826
 7827        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7828        Some(())
 7829    }
 7830
 7831    fn show_edit_predictions_in_menu(&self) -> bool {
 7832        match self.edit_prediction_settings {
 7833            EditPredictionSettings::Disabled => false,
 7834            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7835        }
 7836    }
 7837
 7838    pub fn edit_predictions_enabled(&self) -> bool {
 7839        match self.edit_prediction_settings {
 7840            EditPredictionSettings::Disabled => false,
 7841            EditPredictionSettings::Enabled { .. } => true,
 7842        }
 7843    }
 7844
 7845    fn edit_prediction_requires_modifier(&self) -> bool {
 7846        match self.edit_prediction_settings {
 7847            EditPredictionSettings::Disabled => false,
 7848            EditPredictionSettings::Enabled {
 7849                preview_requires_modifier,
 7850                ..
 7851            } => preview_requires_modifier,
 7852        }
 7853    }
 7854
 7855    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7856        if self.edit_prediction_provider.is_none() {
 7857            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7858            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7859            return;
 7860        }
 7861
 7862        let selection = self.selections.newest_anchor();
 7863        let cursor = selection.head();
 7864
 7865        if let Some((buffer, cursor_buffer_position)) =
 7866            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7867        {
 7868            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7869                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7870                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7871                return;
 7872            }
 7873            self.edit_prediction_settings =
 7874                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7875        }
 7876    }
 7877
 7878    fn edit_prediction_settings_at_position(
 7879        &self,
 7880        buffer: &Entity<Buffer>,
 7881        buffer_position: language::Anchor,
 7882        cx: &App,
 7883    ) -> EditPredictionSettings {
 7884        if !self.mode.is_full()
 7885            || !self.show_edit_predictions_override.unwrap_or(true)
 7886            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7887        {
 7888            return EditPredictionSettings::Disabled;
 7889        }
 7890
 7891        let buffer = buffer.read(cx);
 7892
 7893        let file = buffer.file();
 7894
 7895        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7896            return EditPredictionSettings::Disabled;
 7897        };
 7898
 7899        let by_provider = matches!(
 7900            self.menu_edit_predictions_policy,
 7901            MenuEditPredictionsPolicy::ByProvider
 7902        );
 7903
 7904        let show_in_menu = by_provider
 7905            && self
 7906                .edit_prediction_provider
 7907                .as_ref()
 7908                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7909
 7910        let preview_requires_modifier =
 7911            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7912
 7913        EditPredictionSettings::Enabled {
 7914            show_in_menu,
 7915            preview_requires_modifier,
 7916        }
 7917    }
 7918
 7919    fn should_show_edit_predictions(&self) -> bool {
 7920        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7921    }
 7922
 7923    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7924        matches!(
 7925            self.edit_prediction_preview,
 7926            EditPredictionPreview::Active { .. }
 7927        )
 7928    }
 7929
 7930    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7931        let cursor = self.selections.newest_anchor().head();
 7932        if let Some((buffer, cursor_position)) =
 7933            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7934        {
 7935            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7936        } else {
 7937            false
 7938        }
 7939    }
 7940
 7941    pub fn supports_minimap(&self, cx: &App) -> bool {
 7942        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7943    }
 7944
 7945    fn edit_predictions_enabled_in_buffer(
 7946        &self,
 7947        buffer: &Entity<Buffer>,
 7948        buffer_position: language::Anchor,
 7949        cx: &App,
 7950    ) -> bool {
 7951        maybe!({
 7952            if self.read_only(cx) {
 7953                return Some(false);
 7954            }
 7955            let provider = self.edit_prediction_provider()?;
 7956            if !provider.is_enabled(buffer, buffer_position, cx) {
 7957                return Some(false);
 7958            }
 7959            let buffer = buffer.read(cx);
 7960            let Some(file) = buffer.file() else {
 7961                return Some(true);
 7962            };
 7963            let settings = all_language_settings(Some(file), cx);
 7964            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7965        })
 7966        .unwrap_or(false)
 7967    }
 7968
 7969    pub fn show_edit_prediction(
 7970        &mut self,
 7971        _: &ShowEditPrediction,
 7972        window: &mut Window,
 7973        cx: &mut Context<Self>,
 7974    ) {
 7975        if !self.has_active_edit_prediction() {
 7976            self.refresh_edit_prediction(false, true, window, cx);
 7977            return;
 7978        }
 7979
 7980        self.update_visible_edit_prediction(window, cx);
 7981    }
 7982
 7983    pub fn display_cursor_names(
 7984        &mut self,
 7985        _: &DisplayCursorNames,
 7986        window: &mut Window,
 7987        cx: &mut Context<Self>,
 7988    ) {
 7989        self.show_cursor_names(window, cx);
 7990    }
 7991
 7992    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7993        self.show_cursor_names = true;
 7994        cx.notify();
 7995        cx.spawn_in(window, async move |this, cx| {
 7996            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7997            this.update(cx, |this, cx| {
 7998                this.show_cursor_names = false;
 7999                cx.notify()
 8000            })
 8001            .ok()
 8002        })
 8003        .detach();
 8004    }
 8005
 8006    pub fn accept_partial_edit_prediction(
 8007        &mut self,
 8008        granularity: EditPredictionGranularity,
 8009        window: &mut Window,
 8010        cx: &mut Context<Self>,
 8011    ) {
 8012        if self.show_edit_predictions_in_menu() {
 8013            self.hide_context_menu(window, cx);
 8014        }
 8015
 8016        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8017            return;
 8018        };
 8019
 8020        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8021            return;
 8022        }
 8023
 8024        match &active_edit_prediction.completion {
 8025            EditPrediction::MoveWithin { target, .. } => {
 8026                let target = *target;
 8027
 8028                if matches!(granularity, EditPredictionGranularity::Full) {
 8029                    if let Some(position_map) = &self.last_position_map {
 8030                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8031                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8032
 8033                        if is_visible || !self.edit_prediction_requires_modifier() {
 8034                            self.unfold_ranges(&[target..target], true, false, cx);
 8035                            self.change_selections(
 8036                                SelectionEffects::scroll(Autoscroll::newest()),
 8037                                window,
 8038                                cx,
 8039                                |selections| {
 8040                                    selections.select_anchor_ranges([target..target]);
 8041                                },
 8042                            );
 8043                            self.clear_row_highlights::<EditPredictionPreview>();
 8044                            self.edit_prediction_preview
 8045                                .set_previous_scroll_position(None);
 8046                        } else {
 8047                            // Highlight and request scroll
 8048                            self.edit_prediction_preview
 8049                                .set_previous_scroll_position(Some(
 8050                                    position_map.snapshot.scroll_anchor,
 8051                                ));
 8052                            self.highlight_rows::<EditPredictionPreview>(
 8053                                target..target,
 8054                                cx.theme().colors().editor_highlighted_line_background,
 8055                                RowHighlightOptions {
 8056                                    autoscroll: true,
 8057                                    ..Default::default()
 8058                                },
 8059                                cx,
 8060                            );
 8061                            self.request_autoscroll(Autoscroll::fit(), cx);
 8062                        }
 8063                    }
 8064                } else {
 8065                    self.change_selections(
 8066                        SelectionEffects::scroll(Autoscroll::newest()),
 8067                        window,
 8068                        cx,
 8069                        |selections| {
 8070                            selections.select_anchor_ranges([target..target]);
 8071                        },
 8072                    );
 8073                }
 8074            }
 8075            EditPrediction::MoveOutside { snapshot, target } => {
 8076                if let Some(workspace) = self.workspace() {
 8077                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8078                        .detach_and_log_err(cx);
 8079                }
 8080            }
 8081            EditPrediction::Edit {
 8082                edits,
 8083                cursor_position,
 8084                ..
 8085            } => {
 8086                self.report_edit_prediction_event(
 8087                    active_edit_prediction.completion_id.clone(),
 8088                    true,
 8089                    cx,
 8090                );
 8091
 8092                match granularity {
 8093                    EditPredictionGranularity::Full => {
 8094                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8095
 8096                        // Compute fallback cursor position BEFORE applying the edit,
 8097                        // so the anchor tracks through the edit correctly
 8098                        let fallback_cursor_target = {
 8099                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8100                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8101                        };
 8102
 8103                        self.buffer.update(cx, |buffer, cx| {
 8104                            buffer.edit(edits.iter().cloned(), None, cx)
 8105                        });
 8106
 8107                        if let Some(provider) = self.edit_prediction_provider() {
 8108                            provider.accept(cx);
 8109                        }
 8110
 8111                        // Resolve cursor position after the edit is applied
 8112                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8113                            // The anchor tracks through the edit, then we add the offset
 8114                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8115                            let base_offset = anchor.to_offset(&snapshot).0;
 8116                            let target_offset =
 8117                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8118                            snapshot.anchor_after(target_offset)
 8119                        } else {
 8120                            fallback_cursor_target
 8121                        };
 8122
 8123                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8124                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8125                        });
 8126
 8127                        let selections = self.selections.disjoint_anchors_arc();
 8128                        if let Some(transaction_id_now) =
 8129                            self.buffer.read(cx).last_transaction_id(cx)
 8130                        {
 8131                            if transaction_id_prev != Some(transaction_id_now) {
 8132                                self.selection_history
 8133                                    .insert_transaction(transaction_id_now, selections);
 8134                            }
 8135                        }
 8136
 8137                        self.update_visible_edit_prediction(window, cx);
 8138                        if self.active_edit_prediction.is_none() {
 8139                            self.refresh_edit_prediction(true, true, window, cx);
 8140                        }
 8141                        cx.notify();
 8142                    }
 8143                    _ => {
 8144                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8145                        let cursor_offset = self
 8146                            .selections
 8147                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8148                            .head();
 8149
 8150                        let insertion = edits.iter().find_map(|(range, text)| {
 8151                            let range = range.to_offset(&snapshot);
 8152                            if range.is_empty() && range.start == cursor_offset {
 8153                                Some(text)
 8154                            } else {
 8155                                None
 8156                            }
 8157                        });
 8158
 8159                        if let Some(text) = insertion {
 8160                            let text_to_insert = match granularity {
 8161                                EditPredictionGranularity::Word => {
 8162                                    let mut partial = text
 8163                                        .chars()
 8164                                        .by_ref()
 8165                                        .take_while(|c| c.is_alphabetic())
 8166                                        .collect::<String>();
 8167                                    if partial.is_empty() {
 8168                                        partial = text
 8169                                            .chars()
 8170                                            .by_ref()
 8171                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8172                                            .collect::<String>();
 8173                                    }
 8174                                    partial
 8175                                }
 8176                                EditPredictionGranularity::Line => {
 8177                                    if let Some(line) = text.split_inclusive('\n').next() {
 8178                                        line.to_string()
 8179                                    } else {
 8180                                        text.to_string()
 8181                                    }
 8182                                }
 8183                                EditPredictionGranularity::Full => unreachable!(),
 8184                            };
 8185
 8186                            cx.emit(EditorEvent::InputHandled {
 8187                                utf16_range_to_replace: None,
 8188                                text: text_to_insert.clone().into(),
 8189                            });
 8190
 8191                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8192                            self.refresh_edit_prediction(true, true, window, cx);
 8193                            cx.notify();
 8194                        } else {
 8195                            self.accept_partial_edit_prediction(
 8196                                EditPredictionGranularity::Full,
 8197                                window,
 8198                                cx,
 8199                            );
 8200                        }
 8201                    }
 8202                }
 8203            }
 8204        }
 8205
 8206        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8207    }
 8208
 8209    pub fn accept_next_word_edit_prediction(
 8210        &mut self,
 8211        _: &AcceptNextWordEditPrediction,
 8212        window: &mut Window,
 8213        cx: &mut Context<Self>,
 8214    ) {
 8215        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8216    }
 8217
 8218    pub fn accept_next_line_edit_prediction(
 8219        &mut self,
 8220        _: &AcceptNextLineEditPrediction,
 8221        window: &mut Window,
 8222        cx: &mut Context<Self>,
 8223    ) {
 8224        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8225    }
 8226
 8227    pub fn accept_edit_prediction(
 8228        &mut self,
 8229        _: &AcceptEditPrediction,
 8230        window: &mut Window,
 8231        cx: &mut Context<Self>,
 8232    ) {
 8233        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8234    }
 8235
 8236    fn discard_edit_prediction(
 8237        &mut self,
 8238        reason: EditPredictionDiscardReason,
 8239        cx: &mut Context<Self>,
 8240    ) -> bool {
 8241        if reason == EditPredictionDiscardReason::Rejected {
 8242            let completion_id = self
 8243                .active_edit_prediction
 8244                .as_ref()
 8245                .and_then(|active_completion| active_completion.completion_id.clone());
 8246
 8247            self.report_edit_prediction_event(completion_id, false, cx);
 8248        }
 8249
 8250        if let Some(provider) = self.edit_prediction_provider() {
 8251            provider.discard(reason, cx);
 8252        }
 8253
 8254        self.take_active_edit_prediction(cx)
 8255    }
 8256
 8257    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8258        let Some(provider) = self.edit_prediction_provider() else {
 8259            return;
 8260        };
 8261
 8262        let Some((_, buffer, _)) = self
 8263            .buffer
 8264            .read(cx)
 8265            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8266        else {
 8267            return;
 8268        };
 8269
 8270        let extension = buffer
 8271            .read(cx)
 8272            .file()
 8273            .and_then(|file| Some(file.path().extension()?.to_string()));
 8274
 8275        let event_type = match accepted {
 8276            true => "Edit Prediction Accepted",
 8277            false => "Edit Prediction Discarded",
 8278        };
 8279        telemetry::event!(
 8280            event_type,
 8281            provider = provider.name(),
 8282            prediction_id = id,
 8283            suggestion_accepted = accepted,
 8284            file_extension = extension,
 8285        );
 8286    }
 8287
 8288    fn open_editor_at_anchor(
 8289        snapshot: &language::BufferSnapshot,
 8290        target: language::Anchor,
 8291        workspace: &Entity<Workspace>,
 8292        window: &mut Window,
 8293        cx: &mut App,
 8294    ) -> Task<Result<()>> {
 8295        workspace.update(cx, |workspace, cx| {
 8296            let path = snapshot.file().map(|file| file.full_path(cx));
 8297            let Some(path) =
 8298                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8299            else {
 8300                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8301            };
 8302            let target = text::ToPoint::to_point(&target, snapshot);
 8303            let item = workspace.open_path(path, None, true, window, cx);
 8304            window.spawn(cx, async move |cx| {
 8305                let Some(editor) = item.await?.downcast::<Editor>() else {
 8306                    return Ok(());
 8307                };
 8308                editor
 8309                    .update_in(cx, |editor, window, cx| {
 8310                        editor.go_to_singleton_buffer_point(target, window, cx);
 8311                    })
 8312                    .ok();
 8313                anyhow::Ok(())
 8314            })
 8315        })
 8316    }
 8317
 8318    pub fn has_active_edit_prediction(&self) -> bool {
 8319        self.active_edit_prediction.is_some()
 8320    }
 8321
 8322    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8323        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8324            return false;
 8325        };
 8326
 8327        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8328        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8329        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8330        true
 8331    }
 8332
 8333    /// Returns true when we're displaying the edit prediction popover below the cursor
 8334    /// like we are not previewing and the LSP autocomplete menu is visible
 8335    /// or we are in `when_holding_modifier` mode.
 8336    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8337        if self.edit_prediction_preview_is_active()
 8338            || !self.show_edit_predictions_in_menu()
 8339            || !self.edit_predictions_enabled()
 8340        {
 8341            return false;
 8342        }
 8343
 8344        if self.has_visible_completions_menu() {
 8345            return true;
 8346        }
 8347
 8348        has_completion && self.edit_prediction_requires_modifier()
 8349    }
 8350
 8351    fn handle_modifiers_changed(
 8352        &mut self,
 8353        modifiers: Modifiers,
 8354        position_map: &PositionMap,
 8355        window: &mut Window,
 8356        cx: &mut Context<Self>,
 8357    ) {
 8358        // Ensure that the edit prediction preview is updated, even when not
 8359        // enabled, if there's an active edit prediction preview.
 8360        if self.show_edit_predictions_in_menu()
 8361            || matches!(
 8362                self.edit_prediction_preview,
 8363                EditPredictionPreview::Active { .. }
 8364            )
 8365        {
 8366            self.update_edit_prediction_preview(&modifiers, window, cx);
 8367        }
 8368
 8369        self.update_selection_mode(&modifiers, position_map, window, cx);
 8370
 8371        let mouse_position = window.mouse_position();
 8372        if !position_map.text_hitbox.is_hovered(window) {
 8373            return;
 8374        }
 8375
 8376        self.update_hovered_link(
 8377            position_map.point_for_position(mouse_position),
 8378            &position_map.snapshot,
 8379            modifiers,
 8380            window,
 8381            cx,
 8382        )
 8383    }
 8384
 8385    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8386        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8387            MultiCursorModifier::Alt => modifiers.secondary(),
 8388            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8389        }
 8390    }
 8391
 8392    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8393        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8394            MultiCursorModifier::Alt => modifiers.alt,
 8395            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8396        }
 8397    }
 8398
 8399    fn columnar_selection_mode(
 8400        modifiers: &Modifiers,
 8401        cx: &mut Context<Self>,
 8402    ) -> Option<ColumnarMode> {
 8403        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8404            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8405                Some(ColumnarMode::FromMouse)
 8406            } else if Self::is_alt_pressed(modifiers, cx) {
 8407                Some(ColumnarMode::FromSelection)
 8408            } else {
 8409                None
 8410            }
 8411        } else {
 8412            None
 8413        }
 8414    }
 8415
 8416    fn update_selection_mode(
 8417        &mut self,
 8418        modifiers: &Modifiers,
 8419        position_map: &PositionMap,
 8420        window: &mut Window,
 8421        cx: &mut Context<Self>,
 8422    ) {
 8423        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8424            return;
 8425        };
 8426        if self.selections.pending_anchor().is_none() {
 8427            return;
 8428        }
 8429
 8430        let mouse_position = window.mouse_position();
 8431        let point_for_position = position_map.point_for_position(mouse_position);
 8432        let position = point_for_position.previous_valid;
 8433
 8434        self.select(
 8435            SelectPhase::BeginColumnar {
 8436                position,
 8437                reset: false,
 8438                mode,
 8439                goal_column: point_for_position.exact_unclipped.column(),
 8440            },
 8441            window,
 8442            cx,
 8443        );
 8444    }
 8445
 8446    fn update_edit_prediction_preview(
 8447        &mut self,
 8448        modifiers: &Modifiers,
 8449        window: &mut Window,
 8450        cx: &mut Context<Self>,
 8451    ) {
 8452        let mut modifiers_held = false;
 8453
 8454        // Check bindings for all granularities.
 8455        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8456        let granularities = [
 8457            EditPredictionGranularity::Full,
 8458            EditPredictionGranularity::Line,
 8459            EditPredictionGranularity::Word,
 8460        ];
 8461
 8462        for granularity in granularities {
 8463            if let Some(keystroke) = self
 8464                .accept_edit_prediction_keybind(granularity, window, cx)
 8465                .keystroke()
 8466            {
 8467                modifiers_held = modifiers_held
 8468                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8469            }
 8470        }
 8471
 8472        if modifiers_held {
 8473            if matches!(
 8474                self.edit_prediction_preview,
 8475                EditPredictionPreview::Inactive { .. }
 8476            ) {
 8477                self.edit_prediction_preview = EditPredictionPreview::Active {
 8478                    previous_scroll_position: None,
 8479                    since: Instant::now(),
 8480                };
 8481
 8482                self.update_visible_edit_prediction(window, cx);
 8483                cx.notify();
 8484            }
 8485        } else if let EditPredictionPreview::Active {
 8486            previous_scroll_position,
 8487            since,
 8488        } = self.edit_prediction_preview
 8489        {
 8490            if let (Some(previous_scroll_position), Some(position_map)) =
 8491                (previous_scroll_position, self.last_position_map.as_ref())
 8492            {
 8493                self.set_scroll_position(
 8494                    previous_scroll_position
 8495                        .scroll_position(&position_map.snapshot.display_snapshot),
 8496                    window,
 8497                    cx,
 8498                );
 8499            }
 8500
 8501            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8502                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8503            };
 8504            self.clear_row_highlights::<EditPredictionPreview>();
 8505            self.update_visible_edit_prediction(window, cx);
 8506            cx.notify();
 8507        }
 8508    }
 8509
 8510    fn update_visible_edit_prediction(
 8511        &mut self,
 8512        _window: &mut Window,
 8513        cx: &mut Context<Self>,
 8514    ) -> Option<()> {
 8515        if self.ime_transaction.is_some() {
 8516            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8517            return None;
 8518        }
 8519
 8520        let selection = self.selections.newest_anchor();
 8521        let cursor = selection.head();
 8522        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8523
 8524        // Check project-level disable_ai setting for the current buffer
 8525        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8526            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8527                return None;
 8528            }
 8529        }
 8530        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8531        let excerpt_id = cursor.excerpt_id;
 8532
 8533        let show_in_menu = self.show_edit_predictions_in_menu();
 8534        let completions_menu_has_precedence = !show_in_menu
 8535            && (self.context_menu.borrow().is_some()
 8536                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8537
 8538        if completions_menu_has_precedence
 8539            || !offset_selection.is_empty()
 8540            || self
 8541                .active_edit_prediction
 8542                .as_ref()
 8543                .is_some_and(|completion| {
 8544                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8545                        return false;
 8546                    };
 8547                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8548                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8549                    !invalidation_range.contains(&offset_selection.head())
 8550                })
 8551        {
 8552            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8553            return None;
 8554        }
 8555
 8556        self.take_active_edit_prediction(cx);
 8557        let Some(provider) = self.edit_prediction_provider() else {
 8558            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8559            return None;
 8560        };
 8561
 8562        let (buffer, cursor_buffer_position) =
 8563            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8564
 8565        self.edit_prediction_settings =
 8566            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8567
 8568        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8569
 8570        if self.edit_prediction_indent_conflict {
 8571            let cursor_point = cursor.to_point(&multibuffer);
 8572            let mut suggested_indent = None;
 8573            multibuffer.suggested_indents_callback(
 8574                cursor_point.row..cursor_point.row + 1,
 8575                &mut |_, indent| {
 8576                    suggested_indent = Some(indent);
 8577                    ControlFlow::Break(())
 8578                },
 8579                cx,
 8580            );
 8581
 8582            if let Some(indent) = suggested_indent
 8583                && indent.len == cursor_point.column
 8584            {
 8585                self.edit_prediction_indent_conflict = false;
 8586            }
 8587        }
 8588
 8589        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8590
 8591        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8592        {
 8593            edit_prediction_types::EditPrediction::Local {
 8594                id,
 8595                edits,
 8596                cursor_position,
 8597                edit_preview,
 8598            } => (id, edits, cursor_position, edit_preview),
 8599            edit_prediction_types::EditPrediction::Jump {
 8600                id,
 8601                snapshot,
 8602                target,
 8603            } => {
 8604                if let Some(provider) = &self.edit_prediction_provider {
 8605                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8606                }
 8607                self.stale_edit_prediction_in_menu = None;
 8608                self.active_edit_prediction = Some(EditPredictionState {
 8609                    inlay_ids: vec![],
 8610                    completion: EditPrediction::MoveOutside { snapshot, target },
 8611                    completion_id: id,
 8612                    invalidation_range: None,
 8613                });
 8614                cx.notify();
 8615                return Some(());
 8616            }
 8617        };
 8618
 8619        let edits = edits
 8620            .into_iter()
 8621            .flat_map(|(range, new_text)| {
 8622                Some((
 8623                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8624                    new_text,
 8625                ))
 8626            })
 8627            .collect::<Vec<_>>();
 8628        if edits.is_empty() {
 8629            return None;
 8630        }
 8631
 8632        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8633            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8634            Some((anchor, predicted.offset))
 8635        });
 8636
 8637        let first_edit_start = edits.first().unwrap().0.start;
 8638        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8639        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8640
 8641        let last_edit_end = edits.last().unwrap().0.end;
 8642        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8643        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8644
 8645        let cursor_row = cursor.to_point(&multibuffer).row;
 8646
 8647        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8648
 8649        let mut inlay_ids = Vec::new();
 8650        let invalidation_row_range;
 8651        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8652            Some(cursor_row..edit_end_row)
 8653        } else if cursor_row > edit_end_row {
 8654            Some(edit_start_row..cursor_row)
 8655        } else {
 8656            None
 8657        };
 8658        let supports_jump = self
 8659            .edit_prediction_provider
 8660            .as_ref()
 8661            .map(|provider| provider.provider.supports_jump_to_edit())
 8662            .unwrap_or(true);
 8663
 8664        let is_move = supports_jump
 8665            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8666        let completion = if is_move {
 8667            if let Some(provider) = &self.edit_prediction_provider {
 8668                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8669            }
 8670            invalidation_row_range =
 8671                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8672            let target = first_edit_start;
 8673            EditPrediction::MoveWithin { target, snapshot }
 8674        } else {
 8675            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8676                && !self.edit_predictions_hidden_for_vim_mode;
 8677
 8678            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8679                if provider.show_tab_accept_marker() {
 8680                    EditDisplayMode::TabAccept
 8681                } else {
 8682                    EditDisplayMode::Inline
 8683                }
 8684            } else {
 8685                EditDisplayMode::DiffPopover
 8686            };
 8687
 8688            if show_completions_in_buffer {
 8689                if let Some(provider) = &self.edit_prediction_provider {
 8690                    let suggestion_display_type = match display_mode {
 8691                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8692                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8693                            SuggestionDisplayType::GhostText
 8694                        }
 8695                    };
 8696                    provider.provider.did_show(suggestion_display_type, cx);
 8697                }
 8698                if edits
 8699                    .iter()
 8700                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8701                {
 8702                    let mut inlays = Vec::new();
 8703                    for (range, new_text) in &edits {
 8704                        let inlay = Inlay::edit_prediction(
 8705                            post_inc(&mut self.next_inlay_id),
 8706                            range.start,
 8707                            new_text.as_ref(),
 8708                        );
 8709                        inlay_ids.push(inlay.id);
 8710                        inlays.push(inlay);
 8711                    }
 8712
 8713                    self.splice_inlays(&[], inlays, cx);
 8714                } else {
 8715                    let background_color = cx.theme().status().deleted_background;
 8716                    self.highlight_text(
 8717                        HighlightKey::EditPredictionHighlight,
 8718                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8719                        HighlightStyle {
 8720                            background_color: Some(background_color),
 8721                            ..Default::default()
 8722                        },
 8723                        cx,
 8724                    );
 8725                }
 8726            }
 8727
 8728            invalidation_row_range = edit_start_row..edit_end_row;
 8729
 8730            EditPrediction::Edit {
 8731                edits,
 8732                cursor_position,
 8733                edit_preview,
 8734                display_mode,
 8735                snapshot,
 8736            }
 8737        };
 8738
 8739        let invalidation_range = multibuffer
 8740            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8741            ..multibuffer.anchor_after(Point::new(
 8742                invalidation_row_range.end,
 8743                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8744            ));
 8745
 8746        self.stale_edit_prediction_in_menu = None;
 8747        self.active_edit_prediction = Some(EditPredictionState {
 8748            inlay_ids,
 8749            completion,
 8750            completion_id,
 8751            invalidation_range: Some(invalidation_range),
 8752        });
 8753
 8754        cx.notify();
 8755
 8756        Some(())
 8757    }
 8758
 8759    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8760        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8761    }
 8762
 8763    fn clear_tasks(&mut self) {
 8764        self.tasks.clear()
 8765    }
 8766
 8767    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8768        if self.tasks.insert(key, value).is_some() {
 8769            // This case should hopefully be rare, but just in case...
 8770            log::error!(
 8771                "multiple different run targets found on a single line, only the last target will be rendered"
 8772            )
 8773        }
 8774    }
 8775
 8776    /// Get all display points of breakpoints that will be rendered within editor
 8777    ///
 8778    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8779    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8780    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8781    fn active_breakpoints(
 8782        &self,
 8783        range: Range<DisplayRow>,
 8784        window: &mut Window,
 8785        cx: &mut Context<Self>,
 8786    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8787        let mut breakpoint_display_points = HashMap::default();
 8788
 8789        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8790            return breakpoint_display_points;
 8791        };
 8792
 8793        let snapshot = self.snapshot(window, cx);
 8794
 8795        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8796        let Some(project) = self.project() else {
 8797            return breakpoint_display_points;
 8798        };
 8799
 8800        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8801            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8802
 8803        for (buffer_snapshot, range, excerpt_id) in
 8804            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8805        {
 8806            let Some(buffer) = project
 8807                .read(cx)
 8808                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8809            else {
 8810                continue;
 8811            };
 8812            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8813                &buffer,
 8814                Some(
 8815                    buffer_snapshot.anchor_before(range.start)
 8816                        ..buffer_snapshot.anchor_after(range.end),
 8817                ),
 8818                buffer_snapshot,
 8819                cx,
 8820            );
 8821            for (breakpoint, state) in breakpoints {
 8822                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8823                let position = multi_buffer_anchor
 8824                    .to_point(&multi_buffer_snapshot)
 8825                    .to_display_point(&snapshot);
 8826
 8827                breakpoint_display_points.insert(
 8828                    position.row(),
 8829                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8830                );
 8831            }
 8832        }
 8833
 8834        breakpoint_display_points
 8835    }
 8836
 8837    fn breakpoint_context_menu(
 8838        &self,
 8839        anchor: Anchor,
 8840        window: &mut Window,
 8841        cx: &mut Context<Self>,
 8842    ) -> Entity<ui::ContextMenu> {
 8843        let weak_editor = cx.weak_entity();
 8844        let focus_handle = self.focus_handle(cx);
 8845
 8846        let row = self
 8847            .buffer
 8848            .read(cx)
 8849            .snapshot(cx)
 8850            .summary_for_anchor::<Point>(&anchor)
 8851            .row;
 8852
 8853        let breakpoint = self
 8854            .breakpoint_at_row(row, window, cx)
 8855            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8856
 8857        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8858            "Edit Log Breakpoint"
 8859        } else {
 8860            "Set Log Breakpoint"
 8861        };
 8862
 8863        let condition_breakpoint_msg = if breakpoint
 8864            .as_ref()
 8865            .is_some_and(|bp| bp.1.condition.is_some())
 8866        {
 8867            "Edit Condition Breakpoint"
 8868        } else {
 8869            "Set Condition Breakpoint"
 8870        };
 8871
 8872        let hit_condition_breakpoint_msg = if breakpoint
 8873            .as_ref()
 8874            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8875        {
 8876            "Edit Hit Condition Breakpoint"
 8877        } else {
 8878            "Set Hit Condition Breakpoint"
 8879        };
 8880
 8881        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8882            "Unset Breakpoint"
 8883        } else {
 8884            "Set Breakpoint"
 8885        };
 8886
 8887        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8888
 8889        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8890            BreakpointState::Enabled => Some("Disable"),
 8891            BreakpointState::Disabled => Some("Enable"),
 8892        });
 8893
 8894        let (anchor, breakpoint) =
 8895            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8896
 8897        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8898            menu.on_blur_subscription(Subscription::new(|| {}))
 8899                .context(focus_handle)
 8900                .when(run_to_cursor, |this| {
 8901                    let weak_editor = weak_editor.clone();
 8902                    this.entry("Run to Cursor", None, move |window, cx| {
 8903                        weak_editor
 8904                            .update(cx, |editor, cx| {
 8905                                editor.change_selections(
 8906                                    SelectionEffects::no_scroll(),
 8907                                    window,
 8908                                    cx,
 8909                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8910                                );
 8911                            })
 8912                            .ok();
 8913
 8914                        window.dispatch_action(Box::new(RunToCursor), cx);
 8915                    })
 8916                    .separator()
 8917                })
 8918                .when_some(toggle_state_msg, |this, msg| {
 8919                    this.entry(msg, None, {
 8920                        let weak_editor = weak_editor.clone();
 8921                        let breakpoint = breakpoint.clone();
 8922                        move |_window, cx| {
 8923                            weak_editor
 8924                                .update(cx, |this, cx| {
 8925                                    this.edit_breakpoint_at_anchor(
 8926                                        anchor,
 8927                                        breakpoint.as_ref().clone(),
 8928                                        BreakpointEditAction::InvertState,
 8929                                        cx,
 8930                                    );
 8931                                })
 8932                                .log_err();
 8933                        }
 8934                    })
 8935                })
 8936                .entry(set_breakpoint_msg, None, {
 8937                    let weak_editor = weak_editor.clone();
 8938                    let breakpoint = breakpoint.clone();
 8939                    move |_window, cx| {
 8940                        weak_editor
 8941                            .update(cx, |this, cx| {
 8942                                this.edit_breakpoint_at_anchor(
 8943                                    anchor,
 8944                                    breakpoint.as_ref().clone(),
 8945                                    BreakpointEditAction::Toggle,
 8946                                    cx,
 8947                                );
 8948                            })
 8949                            .log_err();
 8950                    }
 8951                })
 8952                .entry(log_breakpoint_msg, None, {
 8953                    let breakpoint = breakpoint.clone();
 8954                    let weak_editor = weak_editor.clone();
 8955                    move |window, cx| {
 8956                        weak_editor
 8957                            .update(cx, |this, cx| {
 8958                                this.add_edit_breakpoint_block(
 8959                                    anchor,
 8960                                    breakpoint.as_ref(),
 8961                                    BreakpointPromptEditAction::Log,
 8962                                    window,
 8963                                    cx,
 8964                                );
 8965                            })
 8966                            .log_err();
 8967                    }
 8968                })
 8969                .entry(condition_breakpoint_msg, None, {
 8970                    let breakpoint = breakpoint.clone();
 8971                    let weak_editor = weak_editor.clone();
 8972                    move |window, cx| {
 8973                        weak_editor
 8974                            .update(cx, |this, cx| {
 8975                                this.add_edit_breakpoint_block(
 8976                                    anchor,
 8977                                    breakpoint.as_ref(),
 8978                                    BreakpointPromptEditAction::Condition,
 8979                                    window,
 8980                                    cx,
 8981                                );
 8982                            })
 8983                            .log_err();
 8984                    }
 8985                })
 8986                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8987                    weak_editor
 8988                        .update(cx, |this, cx| {
 8989                            this.add_edit_breakpoint_block(
 8990                                anchor,
 8991                                breakpoint.as_ref(),
 8992                                BreakpointPromptEditAction::HitCondition,
 8993                                window,
 8994                                cx,
 8995                            );
 8996                        })
 8997                        .log_err();
 8998                })
 8999        })
 9000    }
 9001
 9002    fn render_breakpoint(
 9003        &self,
 9004        position: Anchor,
 9005        row: DisplayRow,
 9006        breakpoint: &Breakpoint,
 9007        state: Option<BreakpointSessionState>,
 9008        cx: &mut Context<Self>,
 9009    ) -> IconButton {
 9010        let is_rejected = state.is_some_and(|s| !s.verified);
 9011        // Is it a breakpoint that shows up when hovering over gutter?
 9012        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9013            (false, false),
 9014            |PhantomBreakpointIndicator {
 9015                 is_active,
 9016                 display_row,
 9017                 collides_with_existing_breakpoint,
 9018             }| {
 9019                (
 9020                    is_active && display_row == row,
 9021                    collides_with_existing_breakpoint,
 9022                )
 9023            },
 9024        );
 9025
 9026        let (color, icon) = {
 9027            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9028                (false, false) => ui::IconName::DebugBreakpoint,
 9029                (true, false) => ui::IconName::DebugLogBreakpoint,
 9030                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9031                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9032            };
 9033
 9034            let theme_colors = cx.theme().colors();
 9035
 9036            let color = if is_phantom {
 9037                if collides_with_existing {
 9038                    Color::Custom(
 9039                        theme_colors
 9040                            .debugger_accent
 9041                            .blend(theme_colors.text.opacity(0.6)),
 9042                    )
 9043                } else {
 9044                    Color::Hint
 9045                }
 9046            } else if is_rejected {
 9047                Color::Disabled
 9048            } else {
 9049                Color::Debugger
 9050            };
 9051
 9052            (color, icon)
 9053        };
 9054
 9055        let breakpoint = Arc::from(breakpoint.clone());
 9056
 9057        let alt_as_text = gpui::Keystroke {
 9058            modifiers: Modifiers::secondary_key(),
 9059            ..Default::default()
 9060        };
 9061        let primary_action_text = if breakpoint.is_disabled() {
 9062            "Enable breakpoint"
 9063        } else if is_phantom && !collides_with_existing {
 9064            "Set breakpoint"
 9065        } else {
 9066            "Unset breakpoint"
 9067        };
 9068        let focus_handle = self.focus_handle.clone();
 9069
 9070        let meta = if is_rejected {
 9071            SharedString::from("No executable code is associated with this line.")
 9072        } else if collides_with_existing && !breakpoint.is_disabled() {
 9073            SharedString::from(format!(
 9074                "{alt_as_text}-click to disable,\nright-click for more options."
 9075            ))
 9076        } else {
 9077            SharedString::from("Right-click for more options.")
 9078        };
 9079        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9080            .icon_size(IconSize::XSmall)
 9081            .size(ui::ButtonSize::None)
 9082            .when(is_rejected, |this| {
 9083                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9084            })
 9085            .icon_color(color)
 9086            .style(ButtonStyle::Transparent)
 9087            .on_click(cx.listener({
 9088                move |editor, event: &ClickEvent, window, cx| {
 9089                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9090                        BreakpointEditAction::InvertState
 9091                    } else {
 9092                        BreakpointEditAction::Toggle
 9093                    };
 9094
 9095                    window.focus(&editor.focus_handle(cx), cx);
 9096                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9097                    editor.edit_breakpoint_at_anchor(
 9098                        position,
 9099                        breakpoint.as_ref().clone(),
 9100                        edit_action,
 9101                        cx,
 9102                    );
 9103                }
 9104            }))
 9105            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9106                editor.set_breakpoint_context_menu(
 9107                    row,
 9108                    Some(position),
 9109                    event.position(),
 9110                    window,
 9111                    cx,
 9112                );
 9113            }))
 9114            .tooltip(move |_window, cx| {
 9115                Tooltip::with_meta_in(
 9116                    primary_action_text,
 9117                    Some(&ToggleBreakpoint),
 9118                    meta.clone(),
 9119                    &focus_handle,
 9120                    cx,
 9121                )
 9122            })
 9123    }
 9124
 9125    fn build_tasks_context(
 9126        project: &Entity<Project>,
 9127        buffer: &Entity<Buffer>,
 9128        buffer_row: u32,
 9129        tasks: &Arc<RunnableTasks>,
 9130        cx: &mut Context<Self>,
 9131    ) -> Task<Option<task::TaskContext>> {
 9132        let position = Point::new(buffer_row, tasks.column);
 9133        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9134        let location = Location {
 9135            buffer: buffer.clone(),
 9136            range: range_start..range_start,
 9137        };
 9138        // Fill in the environmental variables from the tree-sitter captures
 9139        let mut captured_task_variables = TaskVariables::default();
 9140        for (capture_name, value) in tasks.extra_variables.clone() {
 9141            captured_task_variables.insert(
 9142                task::VariableName::Custom(capture_name.into()),
 9143                value.clone(),
 9144            );
 9145        }
 9146        project.update(cx, |project, cx| {
 9147            project.task_store().update(cx, |task_store, cx| {
 9148                task_store.task_context_for_location(captured_task_variables, location, cx)
 9149            })
 9150        })
 9151    }
 9152
 9153    pub fn spawn_nearest_task(
 9154        &mut self,
 9155        action: &SpawnNearestTask,
 9156        window: &mut Window,
 9157        cx: &mut Context<Self>,
 9158    ) {
 9159        let Some((workspace, _)) = self.workspace.clone() else {
 9160            return;
 9161        };
 9162        let Some(project) = self.project.clone() else {
 9163            return;
 9164        };
 9165
 9166        // Try to find a closest, enclosing node using tree-sitter that has a task
 9167        let Some((buffer, buffer_row, tasks)) = self
 9168            .find_enclosing_node_task(cx)
 9169            // Or find the task that's closest in row-distance.
 9170            .or_else(|| self.find_closest_task(cx))
 9171        else {
 9172            return;
 9173        };
 9174
 9175        let reveal_strategy = action.reveal;
 9176        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9177        cx.spawn_in(window, async move |_, cx| {
 9178            let context = task_context.await?;
 9179            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9180
 9181            let resolved = &mut resolved_task.resolved;
 9182            resolved.reveal = reveal_strategy;
 9183
 9184            workspace
 9185                .update_in(cx, |workspace, window, cx| {
 9186                    workspace.schedule_resolved_task(
 9187                        task_source_kind,
 9188                        resolved_task,
 9189                        false,
 9190                        window,
 9191                        cx,
 9192                    );
 9193                })
 9194                .ok()
 9195        })
 9196        .detach();
 9197    }
 9198
 9199    fn find_closest_task(
 9200        &mut self,
 9201        cx: &mut Context<Self>,
 9202    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9203        let cursor_row = self
 9204            .selections
 9205            .newest_adjusted(&self.display_snapshot(cx))
 9206            .head()
 9207            .row;
 9208
 9209        let ((buffer_id, row), tasks) = self
 9210            .tasks
 9211            .iter()
 9212            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9213
 9214        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9215        let tasks = Arc::new(tasks.to_owned());
 9216        Some((buffer, *row, tasks))
 9217    }
 9218
 9219    fn find_enclosing_node_task(
 9220        &mut self,
 9221        cx: &mut Context<Self>,
 9222    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9223        let snapshot = self.buffer.read(cx).snapshot(cx);
 9224        let offset = self
 9225            .selections
 9226            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9227            .head();
 9228        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9229        let offset = excerpt.map_offset_to_buffer(offset);
 9230        let buffer_id = excerpt.buffer().remote_id();
 9231
 9232        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9233        let mut cursor = layer.node().walk();
 9234
 9235        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9236            if cursor.node().end_byte() == offset.0 {
 9237                cursor.goto_next_sibling();
 9238            }
 9239        }
 9240
 9241        // Ascend to the smallest ancestor that contains the range and has a task.
 9242        loop {
 9243            let node = cursor.node();
 9244            let node_range = node.byte_range();
 9245            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9246
 9247            // Check if this node contains our offset
 9248            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9249                // If it contains offset, check for task
 9250                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9251                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9252                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9253                }
 9254            }
 9255
 9256            if !cursor.goto_parent() {
 9257                break;
 9258            }
 9259        }
 9260        None
 9261    }
 9262
 9263    fn render_run_indicator(
 9264        &self,
 9265        _style: &EditorStyle,
 9266        is_active: bool,
 9267        row: DisplayRow,
 9268        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9269        cx: &mut Context<Self>,
 9270    ) -> IconButton {
 9271        let color = Color::Muted;
 9272        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9273
 9274        IconButton::new(
 9275            ("run_indicator", row.0 as usize),
 9276            ui::IconName::PlayOutlined,
 9277        )
 9278        .shape(ui::IconButtonShape::Square)
 9279        .icon_size(IconSize::XSmall)
 9280        .icon_color(color)
 9281        .toggle_state(is_active)
 9282        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9283            let quick_launch = match e {
 9284                ClickEvent::Keyboard(_) => true,
 9285                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9286            };
 9287
 9288            window.focus(&editor.focus_handle(cx), cx);
 9289            editor.toggle_code_actions(
 9290                &ToggleCodeActions {
 9291                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9292                    quick_launch,
 9293                },
 9294                window,
 9295                cx,
 9296            );
 9297        }))
 9298        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9299            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9300        }))
 9301    }
 9302
 9303    pub fn context_menu_visible(&self) -> bool {
 9304        !self.edit_prediction_preview_is_active()
 9305            && self
 9306                .context_menu
 9307                .borrow()
 9308                .as_ref()
 9309                .is_some_and(|menu| menu.visible())
 9310    }
 9311
 9312    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9313        self.context_menu
 9314            .borrow()
 9315            .as_ref()
 9316            .map(|menu| menu.origin())
 9317    }
 9318
 9319    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9320        self.context_menu_options = Some(options);
 9321    }
 9322
 9323    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9324    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9325
 9326    fn render_edit_prediction_popover(
 9327        &mut self,
 9328        text_bounds: &Bounds<Pixels>,
 9329        content_origin: gpui::Point<Pixels>,
 9330        right_margin: Pixels,
 9331        editor_snapshot: &EditorSnapshot,
 9332        visible_row_range: Range<DisplayRow>,
 9333        scroll_top: ScrollOffset,
 9334        scroll_bottom: ScrollOffset,
 9335        line_layouts: &[LineWithInvisibles],
 9336        line_height: Pixels,
 9337        scroll_position: gpui::Point<ScrollOffset>,
 9338        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9339        newest_selection_head: Option<DisplayPoint>,
 9340        editor_width: Pixels,
 9341        style: &EditorStyle,
 9342        window: &mut Window,
 9343        cx: &mut App,
 9344    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9345        if self.mode().is_minimap() {
 9346            return None;
 9347        }
 9348        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9349
 9350        if self.edit_prediction_visible_in_cursor_popover(true) {
 9351            return None;
 9352        }
 9353
 9354        match &active_edit_prediction.completion {
 9355            EditPrediction::MoveWithin { target, .. } => {
 9356                let target_display_point = target.to_display_point(editor_snapshot);
 9357
 9358                if self.edit_prediction_requires_modifier() {
 9359                    if !self.edit_prediction_preview_is_active() {
 9360                        return None;
 9361                    }
 9362
 9363                    self.render_edit_prediction_modifier_jump_popover(
 9364                        text_bounds,
 9365                        content_origin,
 9366                        visible_row_range,
 9367                        line_layouts,
 9368                        line_height,
 9369                        scroll_pixel_position,
 9370                        newest_selection_head,
 9371                        target_display_point,
 9372                        window,
 9373                        cx,
 9374                    )
 9375                } else {
 9376                    self.render_edit_prediction_eager_jump_popover(
 9377                        text_bounds,
 9378                        content_origin,
 9379                        editor_snapshot,
 9380                        visible_row_range,
 9381                        scroll_top,
 9382                        scroll_bottom,
 9383                        line_height,
 9384                        scroll_pixel_position,
 9385                        target_display_point,
 9386                        editor_width,
 9387                        window,
 9388                        cx,
 9389                    )
 9390                }
 9391            }
 9392            EditPrediction::Edit {
 9393                display_mode: EditDisplayMode::Inline,
 9394                ..
 9395            } => None,
 9396            EditPrediction::Edit {
 9397                display_mode: EditDisplayMode::TabAccept,
 9398                edits,
 9399                ..
 9400            } => {
 9401                let range = &edits.first()?.0;
 9402                let target_display_point = range.end.to_display_point(editor_snapshot);
 9403
 9404                self.render_edit_prediction_end_of_line_popover(
 9405                    "Accept",
 9406                    editor_snapshot,
 9407                    visible_row_range,
 9408                    target_display_point,
 9409                    line_height,
 9410                    scroll_pixel_position,
 9411                    content_origin,
 9412                    editor_width,
 9413                    window,
 9414                    cx,
 9415                )
 9416            }
 9417            EditPrediction::Edit {
 9418                edits,
 9419                edit_preview,
 9420                display_mode: EditDisplayMode::DiffPopover,
 9421                snapshot,
 9422                ..
 9423            } => self.render_edit_prediction_diff_popover(
 9424                text_bounds,
 9425                content_origin,
 9426                right_margin,
 9427                editor_snapshot,
 9428                visible_row_range,
 9429                line_layouts,
 9430                line_height,
 9431                scroll_position,
 9432                scroll_pixel_position,
 9433                newest_selection_head,
 9434                editor_width,
 9435                style,
 9436                edits,
 9437                edit_preview,
 9438                snapshot,
 9439                window,
 9440                cx,
 9441            ),
 9442            EditPrediction::MoveOutside { snapshot, .. } => {
 9443                let mut element = self
 9444                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9445                    .into_any();
 9446
 9447                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9448                let origin_x = text_bounds.size.width - size.width - px(30.);
 9449                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9450                element.prepaint_at(origin, window, cx);
 9451
 9452                Some((element, origin))
 9453            }
 9454        }
 9455    }
 9456
 9457    fn render_edit_prediction_modifier_jump_popover(
 9458        &mut self,
 9459        text_bounds: &Bounds<Pixels>,
 9460        content_origin: gpui::Point<Pixels>,
 9461        visible_row_range: Range<DisplayRow>,
 9462        line_layouts: &[LineWithInvisibles],
 9463        line_height: Pixels,
 9464        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9465        newest_selection_head: Option<DisplayPoint>,
 9466        target_display_point: DisplayPoint,
 9467        window: &mut Window,
 9468        cx: &mut App,
 9469    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9470        let scrolled_content_origin =
 9471            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9472
 9473        const SCROLL_PADDING_Y: Pixels = px(12.);
 9474
 9475        if target_display_point.row() < visible_row_range.start {
 9476            return self.render_edit_prediction_scroll_popover(
 9477                &|_| SCROLL_PADDING_Y,
 9478                IconName::ArrowUp,
 9479                visible_row_range,
 9480                line_layouts,
 9481                newest_selection_head,
 9482                scrolled_content_origin,
 9483                window,
 9484                cx,
 9485            );
 9486        } else if target_display_point.row() >= visible_row_range.end {
 9487            return self.render_edit_prediction_scroll_popover(
 9488                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9489                IconName::ArrowDown,
 9490                visible_row_range,
 9491                line_layouts,
 9492                newest_selection_head,
 9493                scrolled_content_origin,
 9494                window,
 9495                cx,
 9496            );
 9497        }
 9498
 9499        const POLE_WIDTH: Pixels = px(2.);
 9500
 9501        let line_layout =
 9502            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9503        let target_column = target_display_point.column() as usize;
 9504
 9505        let target_x = line_layout.x_for_index(target_column);
 9506        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9507            - scroll_pixel_position.y;
 9508
 9509        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9510
 9511        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9512        border_color.l += 0.001;
 9513
 9514        let mut element = v_flex()
 9515            .items_end()
 9516            .when(flag_on_right, |el| el.items_start())
 9517            .child(if flag_on_right {
 9518                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9519                    .rounded_bl(px(0.))
 9520                    .rounded_tl(px(0.))
 9521                    .border_l_2()
 9522                    .border_color(border_color)
 9523            } else {
 9524                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9525                    .rounded_br(px(0.))
 9526                    .rounded_tr(px(0.))
 9527                    .border_r_2()
 9528                    .border_color(border_color)
 9529            })
 9530            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9531            .into_any();
 9532
 9533        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9534
 9535        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9536            - point(
 9537                if flag_on_right {
 9538                    POLE_WIDTH
 9539                } else {
 9540                    size.width - POLE_WIDTH
 9541                },
 9542                size.height - line_height,
 9543            );
 9544
 9545        origin.x = origin.x.max(content_origin.x);
 9546
 9547        element.prepaint_at(origin, window, cx);
 9548
 9549        Some((element, origin))
 9550    }
 9551
 9552    fn render_edit_prediction_scroll_popover(
 9553        &mut self,
 9554        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9555        scroll_icon: IconName,
 9556        visible_row_range: Range<DisplayRow>,
 9557        line_layouts: &[LineWithInvisibles],
 9558        newest_selection_head: Option<DisplayPoint>,
 9559        scrolled_content_origin: gpui::Point<Pixels>,
 9560        window: &mut Window,
 9561        cx: &mut App,
 9562    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9563        let mut element = self
 9564            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9565            .into_any();
 9566
 9567        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9568
 9569        let cursor = newest_selection_head?;
 9570        let cursor_row_layout =
 9571            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9572        let cursor_column = cursor.column() as usize;
 9573
 9574        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9575
 9576        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9577
 9578        element.prepaint_at(origin, window, cx);
 9579        Some((element, origin))
 9580    }
 9581
 9582    fn render_edit_prediction_eager_jump_popover(
 9583        &mut self,
 9584        text_bounds: &Bounds<Pixels>,
 9585        content_origin: gpui::Point<Pixels>,
 9586        editor_snapshot: &EditorSnapshot,
 9587        visible_row_range: Range<DisplayRow>,
 9588        scroll_top: ScrollOffset,
 9589        scroll_bottom: ScrollOffset,
 9590        line_height: Pixels,
 9591        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9592        target_display_point: DisplayPoint,
 9593        editor_width: Pixels,
 9594        window: &mut Window,
 9595        cx: &mut App,
 9596    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9597        if target_display_point.row().as_f64() < scroll_top {
 9598            let mut element = self
 9599                .render_edit_prediction_line_popover(
 9600                    "Jump to Edit",
 9601                    Some(IconName::ArrowUp),
 9602                    window,
 9603                    cx,
 9604                )
 9605                .into_any();
 9606
 9607            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9608            let offset = point(
 9609                (text_bounds.size.width - size.width) / 2.,
 9610                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9611            );
 9612
 9613            let origin = text_bounds.origin + offset;
 9614            element.prepaint_at(origin, window, cx);
 9615            Some((element, origin))
 9616        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9617            let mut element = self
 9618                .render_edit_prediction_line_popover(
 9619                    "Jump to Edit",
 9620                    Some(IconName::ArrowDown),
 9621                    window,
 9622                    cx,
 9623                )
 9624                .into_any();
 9625
 9626            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9627            let offset = point(
 9628                (text_bounds.size.width - size.width) / 2.,
 9629                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9630            );
 9631
 9632            let origin = text_bounds.origin + offset;
 9633            element.prepaint_at(origin, window, cx);
 9634            Some((element, origin))
 9635        } else {
 9636            self.render_edit_prediction_end_of_line_popover(
 9637                "Jump to Edit",
 9638                editor_snapshot,
 9639                visible_row_range,
 9640                target_display_point,
 9641                line_height,
 9642                scroll_pixel_position,
 9643                content_origin,
 9644                editor_width,
 9645                window,
 9646                cx,
 9647            )
 9648        }
 9649    }
 9650
 9651    fn render_edit_prediction_end_of_line_popover(
 9652        self: &mut Editor,
 9653        label: &'static str,
 9654        editor_snapshot: &EditorSnapshot,
 9655        visible_row_range: Range<DisplayRow>,
 9656        target_display_point: DisplayPoint,
 9657        line_height: Pixels,
 9658        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9659        content_origin: gpui::Point<Pixels>,
 9660        editor_width: Pixels,
 9661        window: &mut Window,
 9662        cx: &mut App,
 9663    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9664        let target_line_end = DisplayPoint::new(
 9665            target_display_point.row(),
 9666            editor_snapshot.line_len(target_display_point.row()),
 9667        );
 9668
 9669        let mut element = self
 9670            .render_edit_prediction_line_popover(label, None, window, cx)
 9671            .into_any();
 9672
 9673        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9674
 9675        let line_origin =
 9676            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9677
 9678        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9679        let mut origin = start_point
 9680            + line_origin
 9681            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9682        origin.x = origin.x.max(content_origin.x);
 9683
 9684        let max_x = content_origin.x + editor_width - size.width;
 9685
 9686        if origin.x > max_x {
 9687            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9688
 9689            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9690                origin.y += offset;
 9691                IconName::ArrowUp
 9692            } else {
 9693                origin.y -= offset;
 9694                IconName::ArrowDown
 9695            };
 9696
 9697            element = self
 9698                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9699                .into_any();
 9700
 9701            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9702
 9703            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9704        }
 9705
 9706        element.prepaint_at(origin, window, cx);
 9707        Some((element, origin))
 9708    }
 9709
 9710    fn render_edit_prediction_diff_popover(
 9711        self: &Editor,
 9712        text_bounds: &Bounds<Pixels>,
 9713        content_origin: gpui::Point<Pixels>,
 9714        right_margin: Pixels,
 9715        editor_snapshot: &EditorSnapshot,
 9716        visible_row_range: Range<DisplayRow>,
 9717        line_layouts: &[LineWithInvisibles],
 9718        line_height: Pixels,
 9719        scroll_position: gpui::Point<ScrollOffset>,
 9720        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9721        newest_selection_head: Option<DisplayPoint>,
 9722        editor_width: Pixels,
 9723        style: &EditorStyle,
 9724        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9725        edit_preview: &Option<language::EditPreview>,
 9726        snapshot: &language::BufferSnapshot,
 9727        window: &mut Window,
 9728        cx: &mut App,
 9729    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9730        let edit_start = edits
 9731            .first()
 9732            .unwrap()
 9733            .0
 9734            .start
 9735            .to_display_point(editor_snapshot);
 9736        let edit_end = edits
 9737            .last()
 9738            .unwrap()
 9739            .0
 9740            .end
 9741            .to_display_point(editor_snapshot);
 9742
 9743        let is_visible = visible_row_range.contains(&edit_start.row())
 9744            || visible_row_range.contains(&edit_end.row());
 9745        if !is_visible {
 9746            return None;
 9747        }
 9748
 9749        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9750            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9751        } else {
 9752            // Fallback for providers without edit_preview
 9753            crate::edit_prediction_fallback_text(edits, cx)
 9754        };
 9755
 9756        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9757        let line_count = highlighted_edits.text.lines().count();
 9758
 9759        const BORDER_WIDTH: Pixels = px(1.);
 9760
 9761        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9762        let has_keybind = keybind.is_some();
 9763
 9764        let mut element = h_flex()
 9765            .items_start()
 9766            .child(
 9767                h_flex()
 9768                    .bg(cx.theme().colors().editor_background)
 9769                    .border(BORDER_WIDTH)
 9770                    .shadow_xs()
 9771                    .border_color(cx.theme().colors().border)
 9772                    .rounded_l_lg()
 9773                    .when(line_count > 1, |el| el.rounded_br_lg())
 9774                    .pr_1()
 9775                    .child(styled_text),
 9776            )
 9777            .child(
 9778                h_flex()
 9779                    .h(line_height + BORDER_WIDTH * 2.)
 9780                    .px_1p5()
 9781                    .gap_1()
 9782                    // Workaround: For some reason, there's a gap if we don't do this
 9783                    .ml(-BORDER_WIDTH)
 9784                    .shadow(vec![gpui::BoxShadow {
 9785                        color: gpui::black().opacity(0.05),
 9786                        offset: point(px(1.), px(1.)),
 9787                        blur_radius: px(2.),
 9788                        spread_radius: px(0.),
 9789                    }])
 9790                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9791                    .border(BORDER_WIDTH)
 9792                    .border_color(cx.theme().colors().border)
 9793                    .rounded_r_lg()
 9794                    .id("edit_prediction_diff_popover_keybind")
 9795                    .when(!has_keybind, |el| {
 9796                        let status_colors = cx.theme().status();
 9797
 9798                        el.bg(status_colors.error_background)
 9799                            .border_color(status_colors.error.opacity(0.6))
 9800                            .child(Icon::new(IconName::Info).color(Color::Error))
 9801                            .cursor_default()
 9802                            .hoverable_tooltip(move |_window, cx| {
 9803                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9804                            })
 9805                    })
 9806                    .children(keybind),
 9807            )
 9808            .into_any();
 9809
 9810        let longest_row =
 9811            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9812        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9813            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9814        } else {
 9815            layout_line(
 9816                longest_row,
 9817                editor_snapshot,
 9818                style,
 9819                editor_width,
 9820                |_| false,
 9821                window,
 9822                cx,
 9823            )
 9824            .width
 9825        };
 9826
 9827        let viewport_bounds =
 9828            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9829                right: -right_margin,
 9830                ..Default::default()
 9831            });
 9832
 9833        let x_after_longest = Pixels::from(
 9834            ScrollPixelOffset::from(
 9835                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9836            ) - scroll_pixel_position.x,
 9837        );
 9838
 9839        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9840
 9841        // Fully visible if it can be displayed within the window (allow overlapping other
 9842        // panes). However, this is only allowed if the popover starts within text_bounds.
 9843        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9844            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9845
 9846        let mut origin = if can_position_to_the_right {
 9847            point(
 9848                x_after_longest,
 9849                text_bounds.origin.y
 9850                    + Pixels::from(
 9851                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9852                            - scroll_pixel_position.y,
 9853                    ),
 9854            )
 9855        } else {
 9856            let cursor_row = newest_selection_head.map(|head| head.row());
 9857            let above_edit = edit_start
 9858                .row()
 9859                .0
 9860                .checked_sub(line_count as u32)
 9861                .map(DisplayRow);
 9862            let below_edit = Some(edit_end.row() + 1);
 9863            let above_cursor =
 9864                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9865            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9866
 9867            // Place the edit popover adjacent to the edit if there is a location
 9868            // available that is onscreen and does not obscure the cursor. Otherwise,
 9869            // place it adjacent to the cursor.
 9870            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9871                .into_iter()
 9872                .flatten()
 9873                .find(|&start_row| {
 9874                    let end_row = start_row + line_count as u32;
 9875                    visible_row_range.contains(&start_row)
 9876                        && visible_row_range.contains(&end_row)
 9877                        && cursor_row
 9878                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9879                })?;
 9880
 9881            content_origin
 9882                + point(
 9883                    Pixels::from(-scroll_pixel_position.x),
 9884                    Pixels::from(
 9885                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9886                    ),
 9887                )
 9888        };
 9889
 9890        origin.x -= BORDER_WIDTH;
 9891
 9892        window.defer_draw(element, origin, 1);
 9893
 9894        // Do not return an element, since it will already be drawn due to defer_draw.
 9895        None
 9896    }
 9897
 9898    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9899        px(30.)
 9900    }
 9901
 9902    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9903        if self.read_only(cx) {
 9904            cx.theme().players().read_only()
 9905        } else {
 9906            self.style.as_ref().unwrap().local_player
 9907        }
 9908    }
 9909
 9910    fn render_edit_prediction_accept_keybind(
 9911        &self,
 9912        window: &mut Window,
 9913        cx: &mut App,
 9914    ) -> Option<AnyElement> {
 9915        let accept_binding =
 9916            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9917        let accept_keystroke = accept_binding.keystroke()?;
 9918
 9919        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9920
 9921        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9922            Color::Accent
 9923        } else {
 9924            Color::Muted
 9925        };
 9926
 9927        h_flex()
 9928            .px_0p5()
 9929            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9930            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9931            .text_size(TextSize::XSmall.rems(cx))
 9932            .child(h_flex().children(ui::render_modifiers(
 9933                accept_keystroke.modifiers(),
 9934                PlatformStyle::platform(),
 9935                Some(modifiers_color),
 9936                Some(IconSize::XSmall.rems().into()),
 9937                true,
 9938            )))
 9939            .when(is_platform_style_mac, |parent| {
 9940                parent.child(accept_keystroke.key().to_string())
 9941            })
 9942            .when(!is_platform_style_mac, |parent| {
 9943                parent.child(
 9944                    Key::new(
 9945                        util::capitalize(accept_keystroke.key()),
 9946                        Some(Color::Default),
 9947                    )
 9948                    .size(Some(IconSize::XSmall.rems().into())),
 9949                )
 9950            })
 9951            .into_any()
 9952            .into()
 9953    }
 9954
 9955    fn render_edit_prediction_line_popover(
 9956        &self,
 9957        label: impl Into<SharedString>,
 9958        icon: Option<IconName>,
 9959        window: &mut Window,
 9960        cx: &mut App,
 9961    ) -> Stateful<Div> {
 9962        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9963
 9964        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9965        let has_keybind = keybind.is_some();
 9966        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9967
 9968        h_flex()
 9969            .id("ep-line-popover")
 9970            .py_0p5()
 9971            .pl_1()
 9972            .pr(padding_right)
 9973            .gap_1()
 9974            .rounded_md()
 9975            .border_1()
 9976            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9977            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9978            .shadow_xs()
 9979            .when(!has_keybind, |el| {
 9980                let status_colors = cx.theme().status();
 9981
 9982                el.bg(status_colors.error_background)
 9983                    .border_color(status_colors.error.opacity(0.6))
 9984                    .pl_2()
 9985                    .child(Icon::new(icons.error).color(Color::Error))
 9986                    .cursor_default()
 9987                    .hoverable_tooltip(move |_window, cx| {
 9988                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9989                    })
 9990            })
 9991            .children(keybind)
 9992            .child(
 9993                Label::new(label)
 9994                    .size(LabelSize::Small)
 9995                    .when(!has_keybind, |el| {
 9996                        el.color(cx.theme().status().error.into()).strikethrough()
 9997                    }),
 9998            )
 9999            .when(!has_keybind, |el| {
10000                el.child(
10001                    h_flex().ml_1().child(
10002                        Icon::new(IconName::Info)
10003                            .size(IconSize::Small)
10004                            .color(cx.theme().status().error.into()),
10005                    ),
10006                )
10007            })
10008            .when_some(icon, |element, icon| {
10009                element.child(
10010                    div()
10011                        .mt(px(1.5))
10012                        .child(Icon::new(icon).size(IconSize::Small)),
10013                )
10014            })
10015    }
10016
10017    fn render_edit_prediction_jump_outside_popover(
10018        &self,
10019        snapshot: &BufferSnapshot,
10020        window: &mut Window,
10021        cx: &mut App,
10022    ) -> Stateful<Div> {
10023        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
10024        let has_keybind = keybind.is_some();
10025        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10026
10027        let file_name = snapshot
10028            .file()
10029            .map(|file| SharedString::new(file.file_name(cx)))
10030            .unwrap_or(SharedString::new_static("untitled"));
10031
10032        h_flex()
10033            .id("ep-jump-outside-popover")
10034            .py_1()
10035            .px_2()
10036            .gap_1()
10037            .rounded_md()
10038            .border_1()
10039            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10040            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10041            .shadow_xs()
10042            .when(!has_keybind, |el| {
10043                let status_colors = cx.theme().status();
10044
10045                el.bg(status_colors.error_background)
10046                    .border_color(status_colors.error.opacity(0.6))
10047                    .pl_2()
10048                    .child(Icon::new(icons.error).color(Color::Error))
10049                    .cursor_default()
10050                    .hoverable_tooltip(move |_window, cx| {
10051                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10052                    })
10053            })
10054            .children(keybind)
10055            .child(
10056                Label::new(file_name)
10057                    .size(LabelSize::Small)
10058                    .buffer_font(cx)
10059                    .when(!has_keybind, |el| {
10060                        el.color(cx.theme().status().error.into()).strikethrough()
10061                    }),
10062            )
10063            .when(!has_keybind, |el| {
10064                el.child(
10065                    h_flex().ml_1().child(
10066                        Icon::new(IconName::Info)
10067                            .size(IconSize::Small)
10068                            .color(cx.theme().status().error.into()),
10069                    ),
10070                )
10071            })
10072            .child(
10073                div()
10074                    .mt(px(1.5))
10075                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10076            )
10077    }
10078
10079    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10080        let accent_color = cx.theme().colors().text_accent;
10081        let editor_bg_color = cx.theme().colors().editor_background;
10082        editor_bg_color.blend(accent_color.opacity(0.1))
10083    }
10084
10085    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10086        let accent_color = cx.theme().colors().text_accent;
10087        let editor_bg_color = cx.theme().colors().editor_background;
10088        editor_bg_color.blend(accent_color.opacity(0.6))
10089    }
10090    fn get_prediction_provider_icons(
10091        provider: &Option<RegisteredEditPredictionDelegate>,
10092        cx: &App,
10093    ) -> edit_prediction_types::EditPredictionIconSet {
10094        match provider {
10095            Some(provider) => provider.provider.icons(cx),
10096            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10097        }
10098    }
10099
10100    fn render_edit_prediction_cursor_popover(
10101        &self,
10102        min_width: Pixels,
10103        max_width: Pixels,
10104        cursor_point: Point,
10105        style: &EditorStyle,
10106        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10107        _window: &Window,
10108        cx: &mut Context<Editor>,
10109    ) -> Option<AnyElement> {
10110        let provider = self.edit_prediction_provider.as_ref()?;
10111        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10112
10113        let is_refreshing = provider.provider.is_refreshing(cx);
10114
10115        fn pending_completion_container(icon: IconName) -> Div {
10116            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10117        }
10118
10119        let completion = match &self.active_edit_prediction {
10120            Some(prediction) => {
10121                if !self.has_visible_completions_menu() {
10122                    const RADIUS: Pixels = px(6.);
10123                    const BORDER_WIDTH: Pixels = px(1.);
10124
10125                    return Some(
10126                        h_flex()
10127                            .elevation_2(cx)
10128                            .border(BORDER_WIDTH)
10129                            .border_color(cx.theme().colors().border)
10130                            .when(accept_keystroke.is_none(), |el| {
10131                                el.border_color(cx.theme().status().error)
10132                            })
10133                            .rounded(RADIUS)
10134                            .rounded_tl(px(0.))
10135                            .overflow_hidden()
10136                            .child(div().px_1p5().child(match &prediction.completion {
10137                                EditPrediction::MoveWithin { target, snapshot } => {
10138                                    use text::ToPoint as _;
10139                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10140                                    {
10141                                        Icon::new(icons.down)
10142                                    } else {
10143                                        Icon::new(icons.up)
10144                                    }
10145                                }
10146                                EditPrediction::MoveOutside { .. } => {
10147                                    // TODO [zeta2] custom icon for external jump?
10148                                    Icon::new(icons.base)
10149                                }
10150                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10151                            }))
10152                            .child(
10153                                h_flex()
10154                                    .gap_1()
10155                                    .py_1()
10156                                    .px_2()
10157                                    .rounded_r(RADIUS - BORDER_WIDTH)
10158                                    .border_l_1()
10159                                    .border_color(cx.theme().colors().border)
10160                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10161                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10162                                        el.child(
10163                                            Label::new("Hold")
10164                                                .size(LabelSize::Small)
10165                                                .when(accept_keystroke.is_none(), |el| {
10166                                                    el.strikethrough()
10167                                                })
10168                                                .line_height_style(LineHeightStyle::UiLabel),
10169                                        )
10170                                    })
10171                                    .id("edit_prediction_cursor_popover_keybind")
10172                                    .when(accept_keystroke.is_none(), |el| {
10173                                        let status_colors = cx.theme().status();
10174
10175                                        el.bg(status_colors.error_background)
10176                                            .border_color(status_colors.error.opacity(0.6))
10177                                            .child(Icon::new(IconName::Info).color(Color::Error))
10178                                            .cursor_default()
10179                                            .hoverable_tooltip(move |_window, cx| {
10180                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10181                                                    .into()
10182                                            })
10183                                    })
10184                                    .when_some(
10185                                        accept_keystroke.as_ref(),
10186                                        |el, accept_keystroke| {
10187                                            el.child(h_flex().children(ui::render_modifiers(
10188                                                accept_keystroke.modifiers(),
10189                                                PlatformStyle::platform(),
10190                                                Some(Color::Default),
10191                                                Some(IconSize::XSmall.rems().into()),
10192                                                false,
10193                                            )))
10194                                        },
10195                                    ),
10196                            )
10197                            .into_any(),
10198                    );
10199                }
10200
10201                self.render_edit_prediction_cursor_popover_preview(
10202                    prediction,
10203                    cursor_point,
10204                    style,
10205                    cx,
10206                )?
10207            }
10208
10209            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10210                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10211                    stale_completion,
10212                    cursor_point,
10213                    style,
10214                    cx,
10215                )?,
10216
10217                None => pending_completion_container(icons.base)
10218                    .child(Label::new("...").size(LabelSize::Small)),
10219            },
10220
10221            None => pending_completion_container(icons.base)
10222                .child(Label::new("...").size(LabelSize::Small)),
10223        };
10224
10225        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10226            completion
10227                .with_animation(
10228                    "loading-completion",
10229                    Animation::new(Duration::from_secs(2))
10230                        .repeat()
10231                        .with_easing(pulsating_between(0.4, 0.8)),
10232                    |label, delta| label.opacity(delta),
10233                )
10234                .into_any_element()
10235        } else {
10236            completion.into_any_element()
10237        };
10238
10239        let has_completion = self.active_edit_prediction.is_some();
10240
10241        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10242        Some(
10243            h_flex()
10244                .min_w(min_width)
10245                .max_w(max_width)
10246                .flex_1()
10247                .elevation_2(cx)
10248                .border_color(cx.theme().colors().border)
10249                .child(
10250                    div()
10251                        .flex_1()
10252                        .py_1()
10253                        .px_2()
10254                        .overflow_hidden()
10255                        .child(completion),
10256                )
10257                .when_some(accept_keystroke, |el, accept_keystroke| {
10258                    if !accept_keystroke.modifiers().modified() {
10259                        return el;
10260                    }
10261
10262                    el.child(
10263                        h_flex()
10264                            .h_full()
10265                            .border_l_1()
10266                            .rounded_r_lg()
10267                            .border_color(cx.theme().colors().border)
10268                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10269                            .gap_1()
10270                            .py_1()
10271                            .px_2()
10272                            .child(
10273                                h_flex()
10274                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10275                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10276                                    .child(h_flex().children(ui::render_modifiers(
10277                                        accept_keystroke.modifiers(),
10278                                        PlatformStyle::platform(),
10279                                        Some(if !has_completion {
10280                                            Color::Muted
10281                                        } else {
10282                                            Color::Default
10283                                        }),
10284                                        None,
10285                                        false,
10286                                    ))),
10287                            )
10288                            .child(Label::new("Preview").into_any_element())
10289                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10290                    )
10291                })
10292                .into_any(),
10293        )
10294    }
10295
10296    fn render_edit_prediction_cursor_popover_preview(
10297        &self,
10298        completion: &EditPredictionState,
10299        cursor_point: Point,
10300        style: &EditorStyle,
10301        cx: &mut Context<Editor>,
10302    ) -> Option<Div> {
10303        use text::ToPoint as _;
10304
10305        fn render_relative_row_jump(
10306            prefix: impl Into<String>,
10307            current_row: u32,
10308            target_row: u32,
10309        ) -> Div {
10310            let (row_diff, arrow) = if target_row < current_row {
10311                (current_row - target_row, IconName::ArrowUp)
10312            } else {
10313                (target_row - current_row, IconName::ArrowDown)
10314            };
10315
10316            h_flex()
10317                .child(
10318                    Label::new(format!("{}{}", prefix.into(), row_diff))
10319                        .color(Color::Muted)
10320                        .size(LabelSize::Small),
10321                )
10322                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10323        }
10324
10325        let supports_jump = self
10326            .edit_prediction_provider
10327            .as_ref()
10328            .map(|provider| provider.provider.supports_jump_to_edit())
10329            .unwrap_or(true);
10330
10331        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10332
10333        match &completion.completion {
10334            EditPrediction::MoveWithin {
10335                target, snapshot, ..
10336            } => {
10337                if !supports_jump {
10338                    return None;
10339                }
10340
10341                Some(
10342                    h_flex()
10343                        .px_2()
10344                        .gap_2()
10345                        .flex_1()
10346                        .child(
10347                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10348                                Icon::new(icons.down)
10349                            } else {
10350                                Icon::new(icons.up)
10351                            },
10352                        )
10353                        .child(Label::new("Jump to Edit")),
10354                )
10355            }
10356            EditPrediction::MoveOutside { snapshot, .. } => {
10357                let file_name = snapshot
10358                    .file()
10359                    .map(|file| file.file_name(cx))
10360                    .unwrap_or("untitled");
10361                Some(
10362                    h_flex()
10363                        .px_2()
10364                        .gap_2()
10365                        .flex_1()
10366                        .child(Icon::new(icons.base))
10367                        .child(Label::new(format!("Jump to {file_name}"))),
10368                )
10369            }
10370            EditPrediction::Edit {
10371                edits,
10372                edit_preview,
10373                snapshot,
10374                ..
10375            } => {
10376                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10377
10378                let (highlighted_edits, has_more_lines) =
10379                    if let Some(edit_preview) = edit_preview.as_ref() {
10380                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10381                            .first_line_preview()
10382                    } else {
10383                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10384                    };
10385
10386                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10387                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10388
10389                let preview = h_flex()
10390                    .gap_1()
10391                    .min_w_16()
10392                    .child(styled_text)
10393                    .when(has_more_lines, |parent| parent.child(""));
10394
10395                let left = if supports_jump && first_edit_row != cursor_point.row {
10396                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10397                        .into_any_element()
10398                } else {
10399                    Icon::new(icons.base).into_any_element()
10400                };
10401
10402                Some(
10403                    h_flex()
10404                        .h_full()
10405                        .flex_1()
10406                        .gap_2()
10407                        .pr_1()
10408                        .overflow_x_hidden()
10409                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10410                        .child(left)
10411                        .child(preview),
10412                )
10413            }
10414        }
10415    }
10416
10417    pub fn render_context_menu(
10418        &mut self,
10419        max_height_in_lines: u32,
10420        window: &mut Window,
10421        cx: &mut Context<Editor>,
10422    ) -> Option<AnyElement> {
10423        let menu = self.context_menu.borrow();
10424        let menu = menu.as_ref()?;
10425        if !menu.visible() {
10426            return None;
10427        };
10428        self.style
10429            .as_ref()
10430            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10431    }
10432
10433    fn render_context_menu_aside(
10434        &mut self,
10435        max_size: Size<Pixels>,
10436        window: &mut Window,
10437        cx: &mut Context<Editor>,
10438    ) -> Option<AnyElement> {
10439        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10440            if menu.visible() {
10441                menu.render_aside(max_size, window, cx)
10442            } else {
10443                None
10444            }
10445        })
10446    }
10447
10448    fn hide_context_menu(
10449        &mut self,
10450        window: &mut Window,
10451        cx: &mut Context<Self>,
10452    ) -> Option<CodeContextMenu> {
10453        cx.notify();
10454        self.completion_tasks.clear();
10455        let context_menu = self.context_menu.borrow_mut().take();
10456        self.stale_edit_prediction_in_menu.take();
10457        self.update_visible_edit_prediction(window, cx);
10458        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10459            && let Some(completion_provider) = &self.completion_provider
10460        {
10461            completion_provider.selection_changed(None, window, cx);
10462        }
10463        context_menu
10464    }
10465
10466    fn show_snippet_choices(
10467        &mut self,
10468        choices: &Vec<String>,
10469        selection: Range<Anchor>,
10470        cx: &mut Context<Self>,
10471    ) {
10472        let Some((_, buffer, _)) = self
10473            .buffer()
10474            .read(cx)
10475            .excerpt_containing(selection.start, cx)
10476        else {
10477            return;
10478        };
10479        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10480        else {
10481            return;
10482        };
10483        if buffer != end_buffer {
10484            log::error!("expected anchor range to have matching buffer IDs");
10485            return;
10486        }
10487
10488        let id = post_inc(&mut self.next_completion_id);
10489        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10490        let mut context_menu = self.context_menu.borrow_mut();
10491        let old_menu = context_menu.take();
10492        *context_menu = Some(CodeContextMenu::Completions(
10493            CompletionsMenu::new_snippet_choices(
10494                id,
10495                true,
10496                choices,
10497                selection,
10498                buffer,
10499                old_menu.map(|menu| menu.primary_scroll_handle()),
10500                snippet_sort_order,
10501            ),
10502        ));
10503    }
10504
10505    pub fn insert_snippet(
10506        &mut self,
10507        insertion_ranges: &[Range<MultiBufferOffset>],
10508        snippet: Snippet,
10509        window: &mut Window,
10510        cx: &mut Context<Self>,
10511    ) -> Result<()> {
10512        struct Tabstop<T> {
10513            is_end_tabstop: bool,
10514            ranges: Vec<Range<T>>,
10515            choices: Option<Vec<String>>,
10516        }
10517
10518        let tabstops = self.buffer.update(cx, |buffer, cx| {
10519            let snippet_text: Arc<str> = snippet.text.clone().into();
10520            let edits = insertion_ranges
10521                .iter()
10522                .cloned()
10523                .map(|range| (range, snippet_text.clone()));
10524            let autoindent_mode = AutoindentMode::Block {
10525                original_indent_columns: Vec::new(),
10526            };
10527            buffer.edit(edits, Some(autoindent_mode), cx);
10528
10529            let snapshot = &*buffer.read(cx);
10530            let snippet = &snippet;
10531            snippet
10532                .tabstops
10533                .iter()
10534                .map(|tabstop| {
10535                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10536                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10537                    });
10538                    let mut tabstop_ranges = tabstop
10539                        .ranges
10540                        .iter()
10541                        .flat_map(|tabstop_range| {
10542                            let mut delta = 0_isize;
10543                            insertion_ranges.iter().map(move |insertion_range| {
10544                                let insertion_start = insertion_range.start + delta;
10545                                delta += snippet.text.len() as isize
10546                                    - (insertion_range.end - insertion_range.start) as isize;
10547
10548                                let start =
10549                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10550                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10551                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10552                            })
10553                        })
10554                        .collect::<Vec<_>>();
10555                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10556
10557                    Tabstop {
10558                        is_end_tabstop,
10559                        ranges: tabstop_ranges,
10560                        choices: tabstop.choices.clone(),
10561                    }
10562                })
10563                .collect::<Vec<_>>()
10564        });
10565        if let Some(tabstop) = tabstops.first() {
10566            self.change_selections(Default::default(), window, cx, |s| {
10567                // Reverse order so that the first range is the newest created selection.
10568                // Completions will use it and autoscroll will prioritize it.
10569                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10570            });
10571
10572            if let Some(choices) = &tabstop.choices
10573                && let Some(selection) = tabstop.ranges.first()
10574            {
10575                self.show_snippet_choices(choices, selection.clone(), cx)
10576            }
10577
10578            // If we're already at the last tabstop and it's at the end of the snippet,
10579            // we're done, we don't need to keep the state around.
10580            if !tabstop.is_end_tabstop {
10581                let choices = tabstops
10582                    .iter()
10583                    .map(|tabstop| tabstop.choices.clone())
10584                    .collect();
10585
10586                let ranges = tabstops
10587                    .into_iter()
10588                    .map(|tabstop| tabstop.ranges)
10589                    .collect::<Vec<_>>();
10590
10591                self.snippet_stack.push(SnippetState {
10592                    active_index: 0,
10593                    ranges,
10594                    choices,
10595                });
10596            }
10597
10598            // Check whether the just-entered snippet ends with an auto-closable bracket.
10599            if self.autoclose_regions.is_empty() {
10600                let snapshot = self.buffer.read(cx).snapshot(cx);
10601                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10602                    let selection_head = selection.head();
10603                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10604                        continue;
10605                    };
10606
10607                    let mut bracket_pair = None;
10608                    let max_lookup_length = scope
10609                        .brackets()
10610                        .map(|(pair, _)| {
10611                            pair.start
10612                                .as_str()
10613                                .chars()
10614                                .count()
10615                                .max(pair.end.as_str().chars().count())
10616                        })
10617                        .max();
10618                    if let Some(max_lookup_length) = max_lookup_length {
10619                        let next_text = snapshot
10620                            .chars_at(selection_head)
10621                            .take(max_lookup_length)
10622                            .collect::<String>();
10623                        let prev_text = snapshot
10624                            .reversed_chars_at(selection_head)
10625                            .take(max_lookup_length)
10626                            .collect::<String>();
10627
10628                        for (pair, enabled) in scope.brackets() {
10629                            if enabled
10630                                && pair.close
10631                                && prev_text.starts_with(pair.start.as_str())
10632                                && next_text.starts_with(pair.end.as_str())
10633                            {
10634                                bracket_pair = Some(pair.clone());
10635                                break;
10636                            }
10637                        }
10638                    }
10639
10640                    if let Some(pair) = bracket_pair {
10641                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10642                        let autoclose_enabled =
10643                            self.use_autoclose && snapshot_settings.use_autoclose;
10644                        if autoclose_enabled {
10645                            let start = snapshot.anchor_after(selection_head);
10646                            let end = snapshot.anchor_after(selection_head);
10647                            self.autoclose_regions.push(AutocloseRegion {
10648                                selection_id: selection.id,
10649                                range: start..end,
10650                                pair,
10651                            });
10652                        }
10653                    }
10654                }
10655            }
10656        }
10657        Ok(())
10658    }
10659
10660    pub fn move_to_next_snippet_tabstop(
10661        &mut self,
10662        window: &mut Window,
10663        cx: &mut Context<Self>,
10664    ) -> bool {
10665        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10666    }
10667
10668    pub fn move_to_prev_snippet_tabstop(
10669        &mut self,
10670        window: &mut Window,
10671        cx: &mut Context<Self>,
10672    ) -> bool {
10673        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10674    }
10675
10676    pub fn move_to_snippet_tabstop(
10677        &mut self,
10678        bias: Bias,
10679        window: &mut Window,
10680        cx: &mut Context<Self>,
10681    ) -> bool {
10682        if let Some(mut snippet) = self.snippet_stack.pop() {
10683            match bias {
10684                Bias::Left => {
10685                    if snippet.active_index > 0 {
10686                        snippet.active_index -= 1;
10687                    } else {
10688                        self.snippet_stack.push(snippet);
10689                        return false;
10690                    }
10691                }
10692                Bias::Right => {
10693                    if snippet.active_index + 1 < snippet.ranges.len() {
10694                        snippet.active_index += 1;
10695                    } else {
10696                        self.snippet_stack.push(snippet);
10697                        return false;
10698                    }
10699                }
10700            }
10701            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10702                self.change_selections(Default::default(), window, cx, |s| {
10703                    // Reverse order so that the first range is the newest created selection.
10704                    // Completions will use it and autoscroll will prioritize it.
10705                    s.select_ranges(current_ranges.iter().rev().cloned())
10706                });
10707
10708                if let Some(choices) = &snippet.choices[snippet.active_index]
10709                    && let Some(selection) = current_ranges.first()
10710                {
10711                    self.show_snippet_choices(choices, selection.clone(), cx);
10712                }
10713
10714                // If snippet state is not at the last tabstop, push it back on the stack
10715                if snippet.active_index + 1 < snippet.ranges.len() {
10716                    self.snippet_stack.push(snippet);
10717                }
10718                return true;
10719            }
10720        }
10721
10722        false
10723    }
10724
10725    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10726        self.transact(window, cx, |this, window, cx| {
10727            this.select_all(&SelectAll, window, cx);
10728            this.insert("", window, cx);
10729        });
10730    }
10731
10732    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10733        if self.read_only(cx) {
10734            return;
10735        }
10736        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10737        self.transact(window, cx, |this, window, cx| {
10738            this.select_autoclose_pair(window, cx);
10739
10740            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10741
10742            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10743            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10744            for selection in &mut selections {
10745                if selection.is_empty() {
10746                    let old_head = selection.head();
10747                    let mut new_head =
10748                        movement::left(&display_map, old_head.to_display_point(&display_map))
10749                            .to_point(&display_map);
10750                    if let Some((buffer, line_buffer_range)) = display_map
10751                        .buffer_snapshot()
10752                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10753                    {
10754                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10755                        let indent_len = match indent_size.kind {
10756                            IndentKind::Space => {
10757                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10758                            }
10759                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10760                        };
10761                        if old_head.column <= indent_size.len && old_head.column > 0 {
10762                            let indent_len = indent_len.get();
10763                            new_head = cmp::min(
10764                                new_head,
10765                                MultiBufferPoint::new(
10766                                    old_head.row,
10767                                    ((old_head.column - 1) / indent_len) * indent_len,
10768                                ),
10769                            );
10770                        }
10771                    }
10772
10773                    selection.set_head(new_head, SelectionGoal::None);
10774                }
10775            }
10776
10777            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10778            this.insert("", window, cx);
10779            linked_edits.apply_with_left_expansion(cx);
10780            this.refresh_edit_prediction(true, false, window, cx);
10781            refresh_linked_ranges(this, window, cx);
10782        });
10783    }
10784
10785    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10786        if self.read_only(cx) {
10787            return;
10788        }
10789        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10790        self.transact(window, cx, |this, window, cx| {
10791            this.change_selections(Default::default(), window, cx, |s| {
10792                s.move_with(&mut |map, selection| {
10793                    if selection.is_empty() {
10794                        let cursor = movement::right(map, selection.head());
10795                        selection.end = cursor;
10796                        selection.reversed = true;
10797                        selection.goal = SelectionGoal::None;
10798                    }
10799                })
10800            });
10801            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10802            this.insert("", window, cx);
10803            linked_edits.apply(cx);
10804            this.refresh_edit_prediction(true, false, window, cx);
10805            refresh_linked_ranges(this, window, cx);
10806        });
10807    }
10808
10809    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10810        if self.mode.is_single_line() {
10811            cx.propagate();
10812            return;
10813        }
10814
10815        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10816        if self.move_to_prev_snippet_tabstop(window, cx) {
10817            return;
10818        }
10819        self.outdent(&Outdent, window, cx);
10820    }
10821
10822    pub fn next_snippet_tabstop(
10823        &mut self,
10824        _: &NextSnippetTabstop,
10825        window: &mut Window,
10826        cx: &mut Context<Self>,
10827    ) {
10828        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10829            cx.propagate();
10830            return;
10831        }
10832
10833        if self.move_to_next_snippet_tabstop(window, cx) {
10834            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10835            return;
10836        }
10837        cx.propagate();
10838    }
10839
10840    pub fn previous_snippet_tabstop(
10841        &mut self,
10842        _: &PreviousSnippetTabstop,
10843        window: &mut Window,
10844        cx: &mut Context<Self>,
10845    ) {
10846        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10847            cx.propagate();
10848            return;
10849        }
10850
10851        if self.move_to_prev_snippet_tabstop(window, cx) {
10852            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10853            return;
10854        }
10855        cx.propagate();
10856    }
10857
10858    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10859        if self.mode.is_single_line() {
10860            cx.propagate();
10861            return;
10862        }
10863
10864        if self.move_to_next_snippet_tabstop(window, cx) {
10865            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10866            return;
10867        }
10868        if self.read_only(cx) {
10869            return;
10870        }
10871        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10872        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10873        let buffer = self.buffer.read(cx);
10874        let snapshot = buffer.snapshot(cx);
10875        let rows_iter = selections.iter().map(|s| s.head().row);
10876        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10877
10878        let has_some_cursor_in_whitespace = selections
10879            .iter()
10880            .filter(|selection| selection.is_empty())
10881            .any(|selection| {
10882                let cursor = selection.head();
10883                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10884                cursor.column < current_indent.len
10885            });
10886
10887        let mut edits = Vec::new();
10888        let mut prev_edited_row = 0;
10889        let mut row_delta = 0;
10890        for selection in &mut selections {
10891            if selection.start.row != prev_edited_row {
10892                row_delta = 0;
10893            }
10894            prev_edited_row = selection.end.row;
10895
10896            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10897            if selection.is_empty() {
10898                let cursor = selection.head();
10899                let settings = buffer.language_settings_at(cursor, cx);
10900                if settings.indent_list_on_tab {
10901                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10902                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10903                            row_delta = Self::indent_selection(
10904                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10905                            );
10906                            continue;
10907                        }
10908                    }
10909                }
10910            }
10911
10912            // If the selection is non-empty, then increase the indentation of the selected lines.
10913            if !selection.is_empty() {
10914                row_delta =
10915                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10916                continue;
10917            }
10918
10919            let cursor = selection.head();
10920            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10921            if let Some(suggested_indent) =
10922                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10923            {
10924                // Don't do anything if already at suggested indent
10925                // and there is any other cursor which is not
10926                if has_some_cursor_in_whitespace
10927                    && cursor.column == current_indent.len
10928                    && current_indent.len == suggested_indent.len
10929                {
10930                    continue;
10931                }
10932
10933                // Adjust line and move cursor to suggested indent
10934                // if cursor is not at suggested indent
10935                if cursor.column < suggested_indent.len
10936                    && cursor.column <= current_indent.len
10937                    && current_indent.len <= suggested_indent.len
10938                {
10939                    selection.start = Point::new(cursor.row, suggested_indent.len);
10940                    selection.end = selection.start;
10941                    if row_delta == 0 {
10942                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10943                            cursor.row,
10944                            current_indent,
10945                            suggested_indent,
10946                        ));
10947                        row_delta = suggested_indent.len - current_indent.len;
10948                    }
10949                    continue;
10950                }
10951
10952                // If current indent is more than suggested indent
10953                // only move cursor to current indent and skip indent
10954                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10955                    selection.start = Point::new(cursor.row, current_indent.len);
10956                    selection.end = selection.start;
10957                    continue;
10958                }
10959            }
10960
10961            // Otherwise, insert a hard or soft tab.
10962            let settings = buffer.language_settings_at(cursor, cx);
10963            let tab_size = if settings.hard_tabs {
10964                IndentSize::tab()
10965            } else {
10966                let tab_size = settings.tab_size.get();
10967                let indent_remainder = snapshot
10968                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10969                    .flat_map(str::chars)
10970                    .fold(row_delta % tab_size, |counter: u32, c| {
10971                        if c == '\t' {
10972                            0
10973                        } else {
10974                            (counter + 1) % tab_size
10975                        }
10976                    });
10977
10978                let chars_to_next_tab_stop = tab_size - indent_remainder;
10979                IndentSize::spaces(chars_to_next_tab_stop)
10980            };
10981            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10982            selection.end = selection.start;
10983            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10984            row_delta += tab_size.len;
10985        }
10986
10987        self.transact(window, cx, |this, window, cx| {
10988            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10989            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10990            this.refresh_edit_prediction(true, false, window, cx);
10991        });
10992    }
10993
10994    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10995        if self.read_only(cx) {
10996            return;
10997        }
10998        if self.mode.is_single_line() {
10999            cx.propagate();
11000            return;
11001        }
11002
11003        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11004        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11005        let mut prev_edited_row = 0;
11006        let mut row_delta = 0;
11007        let mut edits = Vec::new();
11008        let buffer = self.buffer.read(cx);
11009        let snapshot = buffer.snapshot(cx);
11010        for selection in &mut selections {
11011            if selection.start.row != prev_edited_row {
11012                row_delta = 0;
11013            }
11014            prev_edited_row = selection.end.row;
11015
11016            row_delta =
11017                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11018        }
11019
11020        self.transact(window, cx, |this, window, cx| {
11021            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11022            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11023        });
11024    }
11025
11026    fn indent_selection(
11027        buffer: &MultiBuffer,
11028        snapshot: &MultiBufferSnapshot,
11029        selection: &mut Selection<Point>,
11030        edits: &mut Vec<(Range<Point>, String)>,
11031        delta_for_start_row: u32,
11032        cx: &App,
11033    ) -> u32 {
11034        let settings = buffer.language_settings_at(selection.start, cx);
11035        let tab_size = settings.tab_size.get();
11036        let indent_kind = if settings.hard_tabs {
11037            IndentKind::Tab
11038        } else {
11039            IndentKind::Space
11040        };
11041        let mut start_row = selection.start.row;
11042        let mut end_row = selection.end.row + 1;
11043
11044        // If a selection ends at the beginning of a line, don't indent
11045        // that last line.
11046        if selection.end.column == 0 && selection.end.row > selection.start.row {
11047            end_row -= 1;
11048        }
11049
11050        // Avoid re-indenting a row that has already been indented by a
11051        // previous selection, but still update this selection's column
11052        // to reflect that indentation.
11053        if delta_for_start_row > 0 {
11054            start_row += 1;
11055            selection.start.column += delta_for_start_row;
11056            if selection.end.row == selection.start.row {
11057                selection.end.column += delta_for_start_row;
11058            }
11059        }
11060
11061        let mut delta_for_end_row = 0;
11062        let has_multiple_rows = start_row + 1 != end_row;
11063        for row in start_row..end_row {
11064            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11065            let indent_delta = match (current_indent.kind, indent_kind) {
11066                (IndentKind::Space, IndentKind::Space) => {
11067                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11068                    IndentSize::spaces(columns_to_next_tab_stop)
11069                }
11070                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11071                (_, IndentKind::Tab) => IndentSize::tab(),
11072            };
11073
11074            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11075                0
11076            } else {
11077                selection.start.column
11078            };
11079            let row_start = Point::new(row, start);
11080            edits.push((
11081                row_start..row_start,
11082                indent_delta.chars().collect::<String>(),
11083            ));
11084
11085            // Update this selection's endpoints to reflect the indentation.
11086            if row == selection.start.row {
11087                selection.start.column += indent_delta.len;
11088            }
11089            if row == selection.end.row {
11090                selection.end.column += indent_delta.len;
11091                delta_for_end_row = indent_delta.len;
11092            }
11093        }
11094
11095        if selection.start.row == selection.end.row {
11096            delta_for_start_row + delta_for_end_row
11097        } else {
11098            delta_for_end_row
11099        }
11100    }
11101
11102    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11103        if self.read_only(cx) {
11104            return;
11105        }
11106        if self.mode.is_single_line() {
11107            cx.propagate();
11108            return;
11109        }
11110
11111        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11112        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11113        let selections = self.selections.all::<Point>(&display_map);
11114        let mut deletion_ranges = Vec::new();
11115        let mut last_outdent = None;
11116        {
11117            let buffer = self.buffer.read(cx);
11118            let snapshot = buffer.snapshot(cx);
11119            for selection in &selections {
11120                let settings = buffer.language_settings_at(selection.start, cx);
11121                let tab_size = settings.tab_size.get();
11122                let mut rows = selection.spanned_rows(false, &display_map);
11123
11124                // Avoid re-outdenting a row that has already been outdented by a
11125                // previous selection.
11126                if let Some(last_row) = last_outdent
11127                    && last_row == rows.start
11128                {
11129                    rows.start = rows.start.next_row();
11130                }
11131                let has_multiple_rows = rows.len() > 1;
11132                for row in rows.iter_rows() {
11133                    let indent_size = snapshot.indent_size_for_line(row);
11134                    if indent_size.len > 0 {
11135                        let deletion_len = match indent_size.kind {
11136                            IndentKind::Space => {
11137                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11138                                if columns_to_prev_tab_stop == 0 {
11139                                    tab_size
11140                                } else {
11141                                    columns_to_prev_tab_stop
11142                                }
11143                            }
11144                            IndentKind::Tab => 1,
11145                        };
11146                        let start = if has_multiple_rows
11147                            || deletion_len > selection.start.column
11148                            || indent_size.len < selection.start.column
11149                        {
11150                            0
11151                        } else {
11152                            selection.start.column - deletion_len
11153                        };
11154                        deletion_ranges.push(
11155                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11156                        );
11157                        last_outdent = Some(row);
11158                    }
11159                }
11160            }
11161        }
11162
11163        self.transact(window, cx, |this, window, cx| {
11164            this.buffer.update(cx, |buffer, cx| {
11165                let empty_str: Arc<str> = Arc::default();
11166                buffer.edit(
11167                    deletion_ranges
11168                        .into_iter()
11169                        .map(|range| (range, empty_str.clone())),
11170                    None,
11171                    cx,
11172                );
11173            });
11174            let selections = this
11175                .selections
11176                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11177            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11178        });
11179    }
11180
11181    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11182        if self.read_only(cx) {
11183            return;
11184        }
11185        if self.mode.is_single_line() {
11186            cx.propagate();
11187            return;
11188        }
11189
11190        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11191        let selections = self
11192            .selections
11193            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11194            .into_iter()
11195            .map(|s| s.range());
11196
11197        self.transact(window, cx, |this, window, cx| {
11198            this.buffer.update(cx, |buffer, cx| {
11199                buffer.autoindent_ranges(selections, cx);
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 delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11209        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11210        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11211        let selections = self.selections.all::<Point>(&display_map);
11212
11213        let mut new_cursors = Vec::new();
11214        let mut edit_ranges = Vec::new();
11215        let mut selections = selections.iter().peekable();
11216        while let Some(selection) = selections.next() {
11217            let mut rows = selection.spanned_rows(false, &display_map);
11218
11219            // Accumulate contiguous regions of rows that we want to delete.
11220            while let Some(next_selection) = selections.peek() {
11221                let next_rows = next_selection.spanned_rows(false, &display_map);
11222                if next_rows.start <= rows.end {
11223                    rows.end = next_rows.end;
11224                    selections.next().unwrap();
11225                } else {
11226                    break;
11227                }
11228            }
11229
11230            let buffer = display_map.buffer_snapshot();
11231            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11232            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11233                // If there's a line after the range, delete the \n from the end of the row range
11234                (
11235                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11236                    rows.end,
11237                )
11238            } else {
11239                // If there isn't a line after the range, delete the \n from the line before the
11240                // start of the row range
11241                edit_start = edit_start.saturating_sub_usize(1);
11242                (buffer.len(), rows.start.previous_row())
11243            };
11244
11245            let text_layout_details = self.text_layout_details(window, cx);
11246            let x = display_map.x_for_display_point(
11247                selection.head().to_display_point(&display_map),
11248                &text_layout_details,
11249            );
11250            let row = Point::new(target_row.0, 0)
11251                .to_display_point(&display_map)
11252                .row();
11253            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11254
11255            new_cursors.push((
11256                selection.id,
11257                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11258                SelectionGoal::None,
11259            ));
11260            edit_ranges.push(edit_start..edit_end);
11261        }
11262
11263        self.transact(window, cx, |this, window, cx| {
11264            let buffer = this.buffer.update(cx, |buffer, cx| {
11265                let empty_str: Arc<str> = Arc::default();
11266                buffer.edit(
11267                    edit_ranges
11268                        .into_iter()
11269                        .map(|range| (range, empty_str.clone())),
11270                    None,
11271                    cx,
11272                );
11273                buffer.snapshot(cx)
11274            });
11275            let new_selections = new_cursors
11276                .into_iter()
11277                .map(|(id, cursor, goal)| {
11278                    let cursor = cursor.to_point(&buffer);
11279                    Selection {
11280                        id,
11281                        start: cursor,
11282                        end: cursor,
11283                        reversed: false,
11284                        goal,
11285                    }
11286                })
11287                .collect();
11288
11289            this.change_selections(Default::default(), window, cx, |s| {
11290                s.select(new_selections);
11291            });
11292        });
11293    }
11294
11295    pub fn join_lines_impl(
11296        &mut self,
11297        insert_whitespace: bool,
11298        window: &mut Window,
11299        cx: &mut Context<Self>,
11300    ) {
11301        if self.read_only(cx) {
11302            return;
11303        }
11304        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11305        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11306            let start = MultiBufferRow(selection.start.row);
11307            // Treat single line selections as if they include the next line. Otherwise this action
11308            // would do nothing for single line selections individual cursors.
11309            let end = if selection.start.row == selection.end.row {
11310                MultiBufferRow(selection.start.row + 1)
11311            } else {
11312                MultiBufferRow(selection.end.row)
11313            };
11314
11315            if let Some(last_row_range) = row_ranges.last_mut()
11316                && start <= last_row_range.end
11317            {
11318                last_row_range.end = end;
11319                continue;
11320            }
11321            row_ranges.push(start..end);
11322        }
11323
11324        let snapshot = self.buffer.read(cx).snapshot(cx);
11325        let mut cursor_positions = Vec::new();
11326        for row_range in &row_ranges {
11327            let anchor = snapshot.anchor_before(Point::new(
11328                row_range.end.previous_row().0,
11329                snapshot.line_len(row_range.end.previous_row()),
11330            ));
11331            cursor_positions.push(anchor..anchor);
11332        }
11333
11334        self.transact(window, cx, |this, window, cx| {
11335            for row_range in row_ranges.into_iter().rev() {
11336                for row in row_range.iter_rows().rev() {
11337                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11338                    let next_line_row = row.next_row();
11339                    let indent = snapshot.indent_size_for_line(next_line_row);
11340                    let mut join_start_column = indent.len;
11341
11342                    if let Some(language_scope) =
11343                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11344                    {
11345                        let line_end =
11346                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11347                        let line_text_after_indent = snapshot
11348                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11349                            .collect::<String>();
11350
11351                        if !line_text_after_indent.is_empty() {
11352                            let block_prefix = language_scope
11353                                .block_comment()
11354                                .map(|c| c.prefix.as_ref())
11355                                .filter(|p| !p.is_empty());
11356                            let doc_prefix = language_scope
11357                                .documentation_comment()
11358                                .map(|c| c.prefix.as_ref())
11359                                .filter(|p| !p.is_empty());
11360                            let all_prefixes = language_scope
11361                                .line_comment_prefixes()
11362                                .iter()
11363                                .map(|p| p.as_ref())
11364                                .chain(block_prefix)
11365                                .chain(doc_prefix)
11366                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11367
11368                            let mut longest_prefix_len = None;
11369                            for prefix in all_prefixes {
11370                                let trimmed = prefix.trim_end();
11371                                if line_text_after_indent.starts_with(trimmed) {
11372                                    let candidate_len =
11373                                        if line_text_after_indent.starts_with(prefix) {
11374                                            prefix.len()
11375                                        } else {
11376                                            trimmed.len()
11377                                        };
11378                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11379                                        longest_prefix_len = Some(candidate_len);
11380                                    }
11381                                }
11382                            }
11383
11384                            if let Some(prefix_len) = longest_prefix_len {
11385                                join_start_column =
11386                                    join_start_column.saturating_add(prefix_len as u32);
11387                            }
11388                        }
11389                    }
11390
11391                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11392
11393                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11394                        && insert_whitespace
11395                    {
11396                        " "
11397                    } else {
11398                        ""
11399                    };
11400
11401                    this.buffer.update(cx, |buffer, cx| {
11402                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11403                    });
11404                }
11405            }
11406
11407            this.change_selections(Default::default(), window, cx, |s| {
11408                s.select_anchor_ranges(cursor_positions)
11409            });
11410        });
11411    }
11412
11413    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11414        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11415        self.join_lines_impl(true, window, cx);
11416    }
11417
11418    pub fn sort_lines_case_sensitive(
11419        &mut self,
11420        _: &SortLinesCaseSensitive,
11421        window: &mut Window,
11422        cx: &mut Context<Self>,
11423    ) {
11424        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11425    }
11426
11427    pub fn sort_lines_by_length(
11428        &mut self,
11429        _: &SortLinesByLength,
11430        window: &mut Window,
11431        cx: &mut Context<Self>,
11432    ) {
11433        self.manipulate_immutable_lines(window, cx, |lines| {
11434            lines.sort_by_key(|&line| line.chars().count())
11435        })
11436    }
11437
11438    pub fn sort_lines_case_insensitive(
11439        &mut self,
11440        _: &SortLinesCaseInsensitive,
11441        window: &mut Window,
11442        cx: &mut Context<Self>,
11443    ) {
11444        self.manipulate_immutable_lines(window, cx, |lines| {
11445            lines.sort_by_key(|line| line.to_lowercase())
11446        })
11447    }
11448
11449    pub fn unique_lines_case_insensitive(
11450        &mut self,
11451        _: &UniqueLinesCaseInsensitive,
11452        window: &mut Window,
11453        cx: &mut Context<Self>,
11454    ) {
11455        self.manipulate_immutable_lines(window, cx, |lines| {
11456            let mut seen = HashSet::default();
11457            lines.retain(|line| seen.insert(line.to_lowercase()));
11458        })
11459    }
11460
11461    pub fn unique_lines_case_sensitive(
11462        &mut self,
11463        _: &UniqueLinesCaseSensitive,
11464        window: &mut Window,
11465        cx: &mut Context<Self>,
11466    ) {
11467        self.manipulate_immutable_lines(window, cx, |lines| {
11468            let mut seen = HashSet::default();
11469            lines.retain(|line| seen.insert(*line));
11470        })
11471    }
11472
11473    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11474        let snapshot = self.buffer.read(cx).snapshot(cx);
11475        for selection in self.selections.disjoint_anchors_arc().iter() {
11476            if snapshot
11477                .language_at(selection.start)
11478                .and_then(|lang| lang.config().wrap_characters.as_ref())
11479                .is_some()
11480            {
11481                return true;
11482            }
11483        }
11484        false
11485    }
11486
11487    fn wrap_selections_in_tag(
11488        &mut self,
11489        _: &WrapSelectionsInTag,
11490        window: &mut Window,
11491        cx: &mut Context<Self>,
11492    ) {
11493        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11494
11495        let snapshot = self.buffer.read(cx).snapshot(cx);
11496
11497        let mut edits = Vec::new();
11498        let mut boundaries = Vec::new();
11499
11500        for selection in self
11501            .selections
11502            .all_adjusted(&self.display_snapshot(cx))
11503            .iter()
11504        {
11505            let Some(wrap_config) = snapshot
11506                .language_at(selection.start)
11507                .and_then(|lang| lang.config().wrap_characters.clone())
11508            else {
11509                continue;
11510            };
11511
11512            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11513            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11514
11515            let start_before = snapshot.anchor_before(selection.start);
11516            let end_after = snapshot.anchor_after(selection.end);
11517
11518            edits.push((start_before..start_before, open_tag));
11519            edits.push((end_after..end_after, close_tag));
11520
11521            boundaries.push((
11522                start_before,
11523                end_after,
11524                wrap_config.start_prefix.len(),
11525                wrap_config.end_suffix.len(),
11526            ));
11527        }
11528
11529        if edits.is_empty() {
11530            return;
11531        }
11532
11533        self.transact(window, cx, |this, window, cx| {
11534            let buffer = this.buffer.update(cx, |buffer, cx| {
11535                buffer.edit(edits, None, cx);
11536                buffer.snapshot(cx)
11537            });
11538
11539            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11540            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11541                boundaries.into_iter()
11542            {
11543                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11544                let close_offset = end_after
11545                    .to_offset(&buffer)
11546                    .saturating_sub_usize(end_suffix_len);
11547                new_selections.push(open_offset..open_offset);
11548                new_selections.push(close_offset..close_offset);
11549            }
11550
11551            this.change_selections(Default::default(), window, cx, |s| {
11552                s.select_ranges(new_selections);
11553            });
11554
11555            this.request_autoscroll(Autoscroll::fit(), cx);
11556        });
11557    }
11558
11559    pub fn toggle_read_only(
11560        &mut self,
11561        _: &workspace::ToggleReadOnlyFile,
11562        _: &mut Window,
11563        cx: &mut Context<Self>,
11564    ) {
11565        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11566            buffer.update(cx, |buffer, cx| {
11567                buffer.set_capability(
11568                    match buffer.capability() {
11569                        Capability::ReadWrite => Capability::Read,
11570                        Capability::Read => Capability::ReadWrite,
11571                        Capability::ReadOnly => Capability::ReadOnly,
11572                    },
11573                    cx,
11574                );
11575            })
11576        }
11577    }
11578
11579    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11580        let Some(project) = self.project.clone() else {
11581            return;
11582        };
11583        let task = self.reload(project, window, cx);
11584        self.detach_and_notify_err(task, window, cx);
11585    }
11586
11587    pub fn restore_file(
11588        &mut self,
11589        _: &::git::RestoreFile,
11590        window: &mut Window,
11591        cx: &mut Context<Self>,
11592    ) {
11593        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11594        let mut buffer_ids = HashSet::default();
11595        let snapshot = self.buffer().read(cx).snapshot(cx);
11596        for selection in self
11597            .selections
11598            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11599        {
11600            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11601        }
11602
11603        let buffer = self.buffer().read(cx);
11604        let ranges = buffer_ids
11605            .into_iter()
11606            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11607            .collect::<Vec<_>>();
11608
11609        self.restore_hunks_in_ranges(ranges, window, cx);
11610    }
11611
11612    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11613        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11614        let selections = self
11615            .selections
11616            .all(&self.display_snapshot(cx))
11617            .into_iter()
11618            .map(|s| s.range())
11619            .collect();
11620        self.restore_hunks_in_ranges(selections, window, cx);
11621    }
11622
11623    pub fn restore_hunks_in_ranges(
11624        &mut self,
11625        ranges: Vec<Range<Point>>,
11626        window: &mut Window,
11627        cx: &mut Context<Editor>,
11628    ) {
11629        if self.delegate_stage_and_restore {
11630            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11631            if !hunks.is_empty() {
11632                cx.emit(EditorEvent::RestoreRequested { hunks });
11633            }
11634            return;
11635        }
11636        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11637        self.transact(window, cx, |editor, window, cx| {
11638            editor.restore_diff_hunks(hunks, cx);
11639            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11640                selections.refresh()
11641            });
11642        });
11643    }
11644
11645    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11646        let mut revert_changes = HashMap::default();
11647        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11648        for (buffer_id, hunks) in &chunk_by {
11649            let hunks = hunks.collect::<Vec<_>>();
11650            for hunk in &hunks {
11651                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11652            }
11653            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11654        }
11655        if !revert_changes.is_empty() {
11656            self.buffer().update(cx, |multi_buffer, cx| {
11657                for (buffer_id, changes) in revert_changes {
11658                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11659                        buffer.update(cx, |buffer, cx| {
11660                            buffer.edit(
11661                                changes
11662                                    .into_iter()
11663                                    .map(|(range, text)| (range, text.to_string())),
11664                                None,
11665                                cx,
11666                            );
11667                        });
11668                    }
11669                }
11670            });
11671        }
11672    }
11673
11674    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11675        if let Some(status) = self
11676            .addons
11677            .iter()
11678            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11679        {
11680            return Some(status);
11681        }
11682        self.project
11683            .as_ref()?
11684            .read(cx)
11685            .status_for_buffer_id(buffer_id, cx)
11686    }
11687
11688    pub fn open_active_item_in_terminal(
11689        &mut self,
11690        _: &OpenInTerminal,
11691        window: &mut Window,
11692        cx: &mut Context<Self>,
11693    ) {
11694        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11695            let project_path = buffer.read(cx).project_path(cx)?;
11696            let project = self.project()?.read(cx);
11697            let entry = project.entry_for_path(&project_path, cx)?;
11698            let parent = match &entry.canonical_path {
11699                Some(canonical_path) => canonical_path.to_path_buf(),
11700                None => project.absolute_path(&project_path, cx)?,
11701            }
11702            .parent()?
11703            .to_path_buf();
11704            Some(parent)
11705        }) {
11706            window.dispatch_action(
11707                OpenTerminal {
11708                    working_directory,
11709                    local: false,
11710                }
11711                .boxed_clone(),
11712                cx,
11713            );
11714        }
11715    }
11716
11717    fn set_breakpoint_context_menu(
11718        &mut self,
11719        display_row: DisplayRow,
11720        position: Option<Anchor>,
11721        clicked_point: gpui::Point<Pixels>,
11722        window: &mut Window,
11723        cx: &mut Context<Self>,
11724    ) {
11725        let source = self
11726            .buffer
11727            .read(cx)
11728            .snapshot(cx)
11729            .anchor_before(Point::new(display_row.0, 0u32));
11730
11731        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11732
11733        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11734            self,
11735            source,
11736            clicked_point,
11737            context_menu,
11738            window,
11739            cx,
11740        );
11741    }
11742
11743    fn add_edit_breakpoint_block(
11744        &mut self,
11745        anchor: Anchor,
11746        breakpoint: &Breakpoint,
11747        edit_action: BreakpointPromptEditAction,
11748        window: &mut Window,
11749        cx: &mut Context<Self>,
11750    ) {
11751        let weak_editor = cx.weak_entity();
11752        let bp_prompt = cx.new(|cx| {
11753            BreakpointPromptEditor::new(
11754                weak_editor,
11755                anchor,
11756                breakpoint.clone(),
11757                edit_action,
11758                window,
11759                cx,
11760            )
11761        });
11762
11763        let height = bp_prompt.update(cx, |this, cx| {
11764            this.prompt
11765                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11766        });
11767        let cloned_prompt = bp_prompt.clone();
11768        let blocks = vec![BlockProperties {
11769            style: BlockStyle::Sticky,
11770            placement: BlockPlacement::Above(anchor),
11771            height: Some(height),
11772            render: Arc::new(move |cx| {
11773                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11774                cloned_prompt.clone().into_any_element()
11775            }),
11776            priority: 0,
11777        }];
11778
11779        let focus_handle = bp_prompt.focus_handle(cx);
11780        window.focus(&focus_handle, cx);
11781
11782        let block_ids = self.insert_blocks(blocks, None, cx);
11783        bp_prompt.update(cx, |prompt, _| {
11784            prompt.add_block_ids(block_ids);
11785        });
11786    }
11787
11788    pub(crate) fn breakpoint_at_row(
11789        &self,
11790        row: u32,
11791        window: &mut Window,
11792        cx: &mut Context<Self>,
11793    ) -> Option<(Anchor, Breakpoint)> {
11794        let snapshot = self.snapshot(window, cx);
11795        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11796
11797        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11798    }
11799
11800    pub(crate) fn breakpoint_at_anchor(
11801        &self,
11802        breakpoint_position: Anchor,
11803        snapshot: &EditorSnapshot,
11804        cx: &mut Context<Self>,
11805    ) -> Option<(Anchor, Breakpoint)> {
11806        let buffer = self
11807            .buffer
11808            .read(cx)
11809            .buffer_for_anchor(breakpoint_position, cx)?;
11810
11811        let enclosing_excerpt = breakpoint_position.excerpt_id;
11812        let buffer_snapshot = buffer.read(cx).snapshot();
11813
11814        let row = buffer_snapshot
11815            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11816            .row;
11817
11818        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11819        let anchor_end = snapshot
11820            .buffer_snapshot()
11821            .anchor_after(Point::new(row, line_len));
11822
11823        self.breakpoint_store
11824            .as_ref()?
11825            .read_with(cx, |breakpoint_store, cx| {
11826                breakpoint_store
11827                    .breakpoints(
11828                        &buffer,
11829                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11830                        &buffer_snapshot,
11831                        cx,
11832                    )
11833                    .next()
11834                    .and_then(|(bp, _)| {
11835                        let breakpoint_row = buffer_snapshot
11836                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11837                            .row;
11838
11839                        if breakpoint_row == row {
11840                            snapshot
11841                                .buffer_snapshot()
11842                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11843                                .map(|position| (position, bp.bp.clone()))
11844                        } else {
11845                            None
11846                        }
11847                    })
11848            })
11849    }
11850
11851    pub fn edit_log_breakpoint(
11852        &mut self,
11853        _: &EditLogBreakpoint,
11854        window: &mut Window,
11855        cx: &mut Context<Self>,
11856    ) {
11857        if self.breakpoint_store.is_none() {
11858            return;
11859        }
11860
11861        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11862            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11863                message: None,
11864                state: BreakpointState::Enabled,
11865                condition: None,
11866                hit_condition: None,
11867            });
11868
11869            self.add_edit_breakpoint_block(
11870                anchor,
11871                &breakpoint,
11872                BreakpointPromptEditAction::Log,
11873                window,
11874                cx,
11875            );
11876        }
11877    }
11878
11879    fn breakpoints_at_cursors(
11880        &self,
11881        window: &mut Window,
11882        cx: &mut Context<Self>,
11883    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11884        let snapshot = self.snapshot(window, cx);
11885        let cursors = self
11886            .selections
11887            .disjoint_anchors_arc()
11888            .iter()
11889            .map(|selection| {
11890                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11891
11892                let breakpoint_position = self
11893                    .breakpoint_at_row(cursor_position.row, window, cx)
11894                    .map(|bp| bp.0)
11895                    .unwrap_or_else(|| {
11896                        snapshot
11897                            .display_snapshot
11898                            .buffer_snapshot()
11899                            .anchor_after(Point::new(cursor_position.row, 0))
11900                    });
11901
11902                let breakpoint = self
11903                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11904                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11905
11906                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11907            })
11908            // 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.
11909            .collect::<HashMap<Anchor, _>>();
11910
11911        cursors.into_iter().collect()
11912    }
11913
11914    pub fn enable_breakpoint(
11915        &mut self,
11916        _: &crate::actions::EnableBreakpoint,
11917        window: &mut Window,
11918        cx: &mut Context<Self>,
11919    ) {
11920        if self.breakpoint_store.is_none() {
11921            return;
11922        }
11923
11924        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11925            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11926                continue;
11927            };
11928            self.edit_breakpoint_at_anchor(
11929                anchor,
11930                breakpoint,
11931                BreakpointEditAction::InvertState,
11932                cx,
11933            );
11934        }
11935    }
11936
11937    pub fn disable_breakpoint(
11938        &mut self,
11939        _: &crate::actions::DisableBreakpoint,
11940        window: &mut Window,
11941        cx: &mut Context<Self>,
11942    ) {
11943        if self.breakpoint_store.is_none() {
11944            return;
11945        }
11946
11947        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11948            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11949                continue;
11950            };
11951            self.edit_breakpoint_at_anchor(
11952                anchor,
11953                breakpoint,
11954                BreakpointEditAction::InvertState,
11955                cx,
11956            );
11957        }
11958    }
11959
11960    pub fn toggle_breakpoint(
11961        &mut self,
11962        _: &crate::actions::ToggleBreakpoint,
11963        window: &mut Window,
11964        cx: &mut Context<Self>,
11965    ) {
11966        if self.breakpoint_store.is_none() {
11967            return;
11968        }
11969
11970        let snapshot = self.snapshot(window, cx);
11971        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11972            if self.gutter_breakpoint_indicator.0.is_some() {
11973                let display_row = anchor
11974                    .to_point(snapshot.buffer_snapshot())
11975                    .to_display_point(&snapshot.display_snapshot)
11976                    .row();
11977                self.update_breakpoint_collision_on_toggle(
11978                    display_row,
11979                    &BreakpointEditAction::Toggle,
11980                );
11981            }
11982
11983            if let Some(breakpoint) = breakpoint {
11984                self.edit_breakpoint_at_anchor(
11985                    anchor,
11986                    breakpoint,
11987                    BreakpointEditAction::Toggle,
11988                    cx,
11989                );
11990            } else {
11991                self.edit_breakpoint_at_anchor(
11992                    anchor,
11993                    Breakpoint::new_standard(),
11994                    BreakpointEditAction::Toggle,
11995                    cx,
11996                );
11997            }
11998        }
11999    }
12000
12001    fn update_breakpoint_collision_on_toggle(
12002        &mut self,
12003        display_row: DisplayRow,
12004        edit_action: &BreakpointEditAction,
12005    ) {
12006        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12007            if breakpoint_indicator.display_row == display_row
12008                && matches!(edit_action, BreakpointEditAction::Toggle)
12009            {
12010                breakpoint_indicator.collides_with_existing_breakpoint =
12011                    !breakpoint_indicator.collides_with_existing_breakpoint;
12012            }
12013        }
12014    }
12015
12016    pub fn edit_breakpoint_at_anchor(
12017        &mut self,
12018        breakpoint_position: Anchor,
12019        breakpoint: Breakpoint,
12020        edit_action: BreakpointEditAction,
12021        cx: &mut Context<Self>,
12022    ) {
12023        let Some(breakpoint_store) = &self.breakpoint_store else {
12024            return;
12025        };
12026
12027        let Some(buffer) = self
12028            .buffer
12029            .read(cx)
12030            .buffer_for_anchor(breakpoint_position, cx)
12031        else {
12032            return;
12033        };
12034
12035        breakpoint_store.update(cx, |breakpoint_store, cx| {
12036            breakpoint_store.toggle_breakpoint(
12037                buffer,
12038                BreakpointWithPosition {
12039                    position: breakpoint_position.text_anchor,
12040                    bp: breakpoint,
12041                },
12042                edit_action,
12043                cx,
12044            );
12045        });
12046
12047        cx.notify();
12048    }
12049
12050    #[cfg(any(test, feature = "test-support"))]
12051    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12052        self.breakpoint_store.clone()
12053    }
12054
12055    pub fn prepare_restore_change(
12056        &self,
12057        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12058        hunk: &MultiBufferDiffHunk,
12059        cx: &mut App,
12060    ) -> Option<()> {
12061        if hunk.is_created_file() {
12062            return None;
12063        }
12064        let buffer = self.buffer.read(cx);
12065        let diff = buffer.diff_for(hunk.buffer_id)?;
12066        let buffer = buffer.buffer(hunk.buffer_id)?;
12067        let buffer = buffer.read(cx);
12068        let original_text = diff
12069            .read(cx)
12070            .base_text(cx)
12071            .as_rope()
12072            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12073        let buffer_snapshot = buffer.snapshot();
12074        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12075        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12076            probe
12077                .0
12078                .start
12079                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12080                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12081        }) {
12082            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12083            Some(())
12084        } else {
12085            None
12086        }
12087    }
12088
12089    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12090        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12091    }
12092
12093    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12094        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12095    }
12096
12097    pub fn rotate_selections_forward(
12098        &mut self,
12099        _: &RotateSelectionsForward,
12100        window: &mut Window,
12101        cx: &mut Context<Self>,
12102    ) {
12103        self.rotate_selections(window, cx, false)
12104    }
12105
12106    pub fn rotate_selections_backward(
12107        &mut self,
12108        _: &RotateSelectionsBackward,
12109        window: &mut Window,
12110        cx: &mut Context<Self>,
12111    ) {
12112        self.rotate_selections(window, cx, true)
12113    }
12114
12115    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12116        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12117        let display_snapshot = self.display_snapshot(cx);
12118        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12119
12120        if selections.len() < 2 {
12121            return;
12122        }
12123
12124        let (edits, new_selections) = {
12125            let buffer = self.buffer.read(cx).read(cx);
12126            let has_selections = selections.iter().any(|s| !s.is_empty());
12127            if has_selections {
12128                let mut selected_texts: Vec<String> = selections
12129                    .iter()
12130                    .map(|selection| {
12131                        buffer
12132                            .text_for_range(selection.start..selection.end)
12133                            .collect()
12134                    })
12135                    .collect();
12136
12137                if reverse {
12138                    selected_texts.rotate_left(1);
12139                } else {
12140                    selected_texts.rotate_right(1);
12141                }
12142
12143                let mut offset_delta: i64 = 0;
12144                let mut new_selections = Vec::new();
12145                let edits: Vec<_> = selections
12146                    .iter()
12147                    .zip(selected_texts.iter())
12148                    .map(|(selection, new_text)| {
12149                        let old_len = (selection.end.0 - selection.start.0) as i64;
12150                        let new_len = new_text.len() as i64;
12151                        let adjusted_start =
12152                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12153                        let adjusted_end =
12154                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12155
12156                        new_selections.push(Selection {
12157                            id: selection.id,
12158                            start: adjusted_start,
12159                            end: adjusted_end,
12160                            reversed: selection.reversed,
12161                            goal: selection.goal,
12162                        });
12163
12164                        offset_delta += new_len - old_len;
12165                        (selection.start..selection.end, new_text.clone())
12166                    })
12167                    .collect();
12168                (edits, new_selections)
12169            } else {
12170                let mut all_rows: Vec<u32> = selections
12171                    .iter()
12172                    .map(|selection| buffer.offset_to_point(selection.start).row)
12173                    .collect();
12174                all_rows.sort_unstable();
12175                all_rows.dedup();
12176
12177                if all_rows.len() < 2 {
12178                    return;
12179                }
12180
12181                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12182                    .iter()
12183                    .map(|&row| {
12184                        let start = Point::new(row, 0);
12185                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12186                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12187                    })
12188                    .collect();
12189
12190                let mut line_texts: Vec<String> = line_ranges
12191                    .iter()
12192                    .map(|range| buffer.text_for_range(range.clone()).collect())
12193                    .collect();
12194
12195                if reverse {
12196                    line_texts.rotate_left(1);
12197                } else {
12198                    line_texts.rotate_right(1);
12199                }
12200
12201                let edits = line_ranges
12202                    .iter()
12203                    .zip(line_texts.iter())
12204                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12205                    .collect();
12206
12207                let num_rows = all_rows.len();
12208                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12209                    .iter()
12210                    .enumerate()
12211                    .map(|(i, &row)| (row, i))
12212                    .collect();
12213
12214                // Compute new line start offsets after rotation (handles CRLF)
12215                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12216                let first_line_start = line_ranges[0].start.0;
12217                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12218                for text in line_texts.iter().take(num_rows - 1) {
12219                    let prev_start = *new_line_starts.last().unwrap();
12220                    new_line_starts.push(prev_start + text.len() + newline_len);
12221                }
12222
12223                let new_selections = selections
12224                    .iter()
12225                    .map(|selection| {
12226                        let point = buffer.offset_to_point(selection.start);
12227                        let old_index = row_to_index[&point.row];
12228                        let new_index = if reverse {
12229                            (old_index + num_rows - 1) % num_rows
12230                        } else {
12231                            (old_index + 1) % num_rows
12232                        };
12233                        let new_offset =
12234                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12235                        Selection {
12236                            id: selection.id,
12237                            start: new_offset,
12238                            end: new_offset,
12239                            reversed: selection.reversed,
12240                            goal: selection.goal,
12241                        }
12242                    })
12243                    .collect();
12244
12245                (edits, new_selections)
12246            }
12247        };
12248
12249        self.transact(window, cx, |this, window, cx| {
12250            this.buffer.update(cx, |buffer, cx| {
12251                buffer.edit(edits, None, cx);
12252            });
12253            this.change_selections(Default::default(), window, cx, |s| {
12254                s.select(new_selections);
12255            });
12256        });
12257    }
12258
12259    fn manipulate_lines<M>(
12260        &mut self,
12261        window: &mut Window,
12262        cx: &mut Context<Self>,
12263        mut manipulate: M,
12264    ) where
12265        M: FnMut(&str) -> LineManipulationResult,
12266    {
12267        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12268
12269        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12270        let buffer = self.buffer.read(cx).snapshot(cx);
12271
12272        let mut edits = Vec::new();
12273
12274        let selections = self.selections.all::<Point>(&display_map);
12275        let mut selections = selections.iter().peekable();
12276        let mut contiguous_row_selections = Vec::new();
12277        let mut new_selections = Vec::new();
12278        let mut added_lines = 0;
12279        let mut removed_lines = 0;
12280
12281        while let Some(selection) = selections.next() {
12282            let (start_row, end_row) = consume_contiguous_rows(
12283                &mut contiguous_row_selections,
12284                selection,
12285                &display_map,
12286                &mut selections,
12287            );
12288
12289            let start_point = Point::new(start_row.0, 0);
12290            let end_point = Point::new(
12291                end_row.previous_row().0,
12292                buffer.line_len(end_row.previous_row()),
12293            );
12294            let text = buffer
12295                .text_for_range(start_point..end_point)
12296                .collect::<String>();
12297
12298            let LineManipulationResult {
12299                new_text,
12300                line_count_before,
12301                line_count_after,
12302            } = manipulate(&text);
12303
12304            edits.push((start_point..end_point, new_text));
12305
12306            // Selections must change based on added and removed line count
12307            let start_row =
12308                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12309            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12310            new_selections.push(Selection {
12311                id: selection.id,
12312                start: start_row,
12313                end: end_row,
12314                goal: SelectionGoal::None,
12315                reversed: selection.reversed,
12316            });
12317
12318            if line_count_after > line_count_before {
12319                added_lines += line_count_after - line_count_before;
12320            } else if line_count_before > line_count_after {
12321                removed_lines += line_count_before - line_count_after;
12322            }
12323        }
12324
12325        self.transact(window, cx, |this, window, cx| {
12326            let buffer = this.buffer.update(cx, |buffer, cx| {
12327                buffer.edit(edits, None, cx);
12328                buffer.snapshot(cx)
12329            });
12330
12331            // Recalculate offsets on newly edited buffer
12332            let new_selections = new_selections
12333                .iter()
12334                .map(|s| {
12335                    let start_point = Point::new(s.start.0, 0);
12336                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12337                    Selection {
12338                        id: s.id,
12339                        start: buffer.point_to_offset(start_point),
12340                        end: buffer.point_to_offset(end_point),
12341                        goal: s.goal,
12342                        reversed: s.reversed,
12343                    }
12344                })
12345                .collect();
12346
12347            this.change_selections(Default::default(), window, cx, |s| {
12348                s.select(new_selections);
12349            });
12350
12351            this.request_autoscroll(Autoscroll::fit(), cx);
12352        });
12353    }
12354
12355    fn manipulate_immutable_lines<Fn>(
12356        &mut self,
12357        window: &mut Window,
12358        cx: &mut Context<Self>,
12359        mut callback: Fn,
12360    ) where
12361        Fn: FnMut(&mut Vec<&str>),
12362    {
12363        self.manipulate_lines(window, cx, |text| {
12364            let mut lines: Vec<&str> = text.split('\n').collect();
12365            let line_count_before = lines.len();
12366
12367            callback(&mut lines);
12368
12369            LineManipulationResult {
12370                new_text: lines.join("\n"),
12371                line_count_before,
12372                line_count_after: lines.len(),
12373            }
12374        });
12375    }
12376
12377    fn manipulate_mutable_lines<Fn>(
12378        &mut self,
12379        window: &mut Window,
12380        cx: &mut Context<Self>,
12381        mut callback: Fn,
12382    ) where
12383        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12384    {
12385        self.manipulate_lines(window, cx, |text| {
12386            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12387            let line_count_before = lines.len();
12388
12389            callback(&mut lines);
12390
12391            LineManipulationResult {
12392                new_text: lines.join("\n"),
12393                line_count_before,
12394                line_count_after: lines.len(),
12395            }
12396        });
12397    }
12398
12399    pub fn convert_indentation_to_spaces(
12400        &mut self,
12401        _: &ConvertIndentationToSpaces,
12402        window: &mut Window,
12403        cx: &mut Context<Self>,
12404    ) {
12405        let settings = self.buffer.read(cx).language_settings(cx);
12406        let tab_size = settings.tab_size.get() as usize;
12407
12408        self.manipulate_mutable_lines(window, cx, |lines| {
12409            // Allocates a reasonably sized scratch buffer once for the whole loop
12410            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12411            // Avoids recomputing spaces that could be inserted many times
12412            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12413                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12414                .collect();
12415
12416            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12417                let mut chars = line.as_ref().chars();
12418                let mut col = 0;
12419                let mut changed = false;
12420
12421                for ch in chars.by_ref() {
12422                    match ch {
12423                        ' ' => {
12424                            reindented_line.push(' ');
12425                            col += 1;
12426                        }
12427                        '\t' => {
12428                            // \t are converted to spaces depending on the current column
12429                            let spaces_len = tab_size - (col % tab_size);
12430                            reindented_line.extend(&space_cache[spaces_len - 1]);
12431                            col += spaces_len;
12432                            changed = true;
12433                        }
12434                        _ => {
12435                            // If we dont append before break, the character is consumed
12436                            reindented_line.push(ch);
12437                            break;
12438                        }
12439                    }
12440                }
12441
12442                if !changed {
12443                    reindented_line.clear();
12444                    continue;
12445                }
12446                // Append the rest of the line and replace old reference with new one
12447                reindented_line.extend(chars);
12448                *line = Cow::Owned(reindented_line.clone());
12449                reindented_line.clear();
12450            }
12451        });
12452    }
12453
12454    pub fn convert_indentation_to_tabs(
12455        &mut self,
12456        _: &ConvertIndentationToTabs,
12457        window: &mut Window,
12458        cx: &mut Context<Self>,
12459    ) {
12460        let settings = self.buffer.read(cx).language_settings(cx);
12461        let tab_size = settings.tab_size.get() as usize;
12462
12463        self.manipulate_mutable_lines(window, cx, |lines| {
12464            // Allocates a reasonably sized buffer once for the whole loop
12465            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12466            // Avoids recomputing spaces that could be inserted many times
12467            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12468                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12469                .collect();
12470
12471            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12472                let mut chars = line.chars();
12473                let mut spaces_count = 0;
12474                let mut first_non_indent_char = None;
12475                let mut changed = false;
12476
12477                for ch in chars.by_ref() {
12478                    match ch {
12479                        ' ' => {
12480                            // Keep track of spaces. Append \t when we reach tab_size
12481                            spaces_count += 1;
12482                            changed = true;
12483                            if spaces_count == tab_size {
12484                                reindented_line.push('\t');
12485                                spaces_count = 0;
12486                            }
12487                        }
12488                        '\t' => {
12489                            reindented_line.push('\t');
12490                            spaces_count = 0;
12491                        }
12492                        _ => {
12493                            // Dont append it yet, we might have remaining spaces
12494                            first_non_indent_char = Some(ch);
12495                            break;
12496                        }
12497                    }
12498                }
12499
12500                if !changed {
12501                    reindented_line.clear();
12502                    continue;
12503                }
12504                // Remaining spaces that didn't make a full tab stop
12505                if spaces_count > 0 {
12506                    reindented_line.extend(&space_cache[spaces_count - 1]);
12507                }
12508                // If we consume an extra character that was not indentation, add it back
12509                if let Some(extra_char) = first_non_indent_char {
12510                    reindented_line.push(extra_char);
12511                }
12512                // Append the rest of the line and replace old reference with new one
12513                reindented_line.extend(chars);
12514                *line = Cow::Owned(reindented_line.clone());
12515                reindented_line.clear();
12516            }
12517        });
12518    }
12519
12520    pub fn convert_to_upper_case(
12521        &mut self,
12522        _: &ConvertToUpperCase,
12523        window: &mut Window,
12524        cx: &mut Context<Self>,
12525    ) {
12526        self.manipulate_text(window, cx, |text| text.to_uppercase())
12527    }
12528
12529    pub fn convert_to_lower_case(
12530        &mut self,
12531        _: &ConvertToLowerCase,
12532        window: &mut Window,
12533        cx: &mut Context<Self>,
12534    ) {
12535        self.manipulate_text(window, cx, |text| text.to_lowercase())
12536    }
12537
12538    pub fn convert_to_title_case(
12539        &mut self,
12540        _: &ConvertToTitleCase,
12541        window: &mut Window,
12542        cx: &mut Context<Self>,
12543    ) {
12544        self.manipulate_text(window, cx, |text| {
12545            text.split('\n')
12546                .map(|line| line.to_case(Case::Title))
12547                .join("\n")
12548        })
12549    }
12550
12551    pub fn convert_to_snake_case(
12552        &mut self,
12553        _: &ConvertToSnakeCase,
12554        window: &mut Window,
12555        cx: &mut Context<Self>,
12556    ) {
12557        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12558    }
12559
12560    pub fn convert_to_kebab_case(
12561        &mut self,
12562        _: &ConvertToKebabCase,
12563        window: &mut Window,
12564        cx: &mut Context<Self>,
12565    ) {
12566        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12567    }
12568
12569    pub fn convert_to_upper_camel_case(
12570        &mut self,
12571        _: &ConvertToUpperCamelCase,
12572        window: &mut Window,
12573        cx: &mut Context<Self>,
12574    ) {
12575        self.manipulate_text(window, cx, |text| {
12576            text.split('\n')
12577                .map(|line| line.to_case(Case::UpperCamel))
12578                .join("\n")
12579        })
12580    }
12581
12582    pub fn convert_to_lower_camel_case(
12583        &mut self,
12584        _: &ConvertToLowerCamelCase,
12585        window: &mut Window,
12586        cx: &mut Context<Self>,
12587    ) {
12588        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12589    }
12590
12591    pub fn convert_to_opposite_case(
12592        &mut self,
12593        _: &ConvertToOppositeCase,
12594        window: &mut Window,
12595        cx: &mut Context<Self>,
12596    ) {
12597        self.manipulate_text(window, cx, |text| {
12598            text.chars()
12599                .fold(String::with_capacity(text.len()), |mut t, c| {
12600                    if c.is_uppercase() {
12601                        t.extend(c.to_lowercase());
12602                    } else {
12603                        t.extend(c.to_uppercase());
12604                    }
12605                    t
12606                })
12607        })
12608    }
12609
12610    pub fn convert_to_sentence_case(
12611        &mut self,
12612        _: &ConvertToSentenceCase,
12613        window: &mut Window,
12614        cx: &mut Context<Self>,
12615    ) {
12616        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12617    }
12618
12619    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12620        self.manipulate_text(window, cx, |text| {
12621            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12622            if has_upper_case_characters {
12623                text.to_lowercase()
12624            } else {
12625                text.to_uppercase()
12626            }
12627        })
12628    }
12629
12630    pub fn convert_to_rot13(
12631        &mut self,
12632        _: &ConvertToRot13,
12633        window: &mut Window,
12634        cx: &mut Context<Self>,
12635    ) {
12636        self.manipulate_text(window, cx, |text| {
12637            text.chars()
12638                .map(|c| match c {
12639                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12640                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12641                    _ => c,
12642                })
12643                .collect()
12644        })
12645    }
12646
12647    pub fn convert_to_rot47(
12648        &mut self,
12649        _: &ConvertToRot47,
12650        window: &mut Window,
12651        cx: &mut Context<Self>,
12652    ) {
12653        self.manipulate_text(window, cx, |text| {
12654            text.chars()
12655                .map(|c| {
12656                    let code_point = c as u32;
12657                    if code_point >= 33 && code_point <= 126 {
12658                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12659                    }
12660                    c
12661                })
12662                .collect()
12663        })
12664    }
12665
12666    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12667    where
12668        Fn: FnMut(&str) -> String,
12669    {
12670        let buffer = self.buffer.read(cx).snapshot(cx);
12671
12672        let mut new_selections = Vec::new();
12673        let mut edits = Vec::new();
12674        let mut selection_adjustment = 0isize;
12675
12676        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12677            let selection_is_empty = selection.is_empty();
12678
12679            let (start, end) = if selection_is_empty {
12680                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12681                (word_range.start, word_range.end)
12682            } else {
12683                (
12684                    buffer.point_to_offset(selection.start),
12685                    buffer.point_to_offset(selection.end),
12686                )
12687            };
12688
12689            let text = buffer.text_for_range(start..end).collect::<String>();
12690            let old_length = text.len() as isize;
12691            let text = callback(&text);
12692
12693            new_selections.push(Selection {
12694                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12695                end: MultiBufferOffset(
12696                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12697                ),
12698                goal: SelectionGoal::None,
12699                id: selection.id,
12700                reversed: selection.reversed,
12701            });
12702
12703            selection_adjustment += old_length - text.len() as isize;
12704
12705            edits.push((start..end, text));
12706        }
12707
12708        self.transact(window, cx, |this, window, cx| {
12709            this.buffer.update(cx, |buffer, cx| {
12710                buffer.edit(edits, None, cx);
12711            });
12712
12713            this.change_selections(Default::default(), window, cx, |s| {
12714                s.select(new_selections);
12715            });
12716
12717            this.request_autoscroll(Autoscroll::fit(), cx);
12718        });
12719    }
12720
12721    pub fn move_selection_on_drop(
12722        &mut self,
12723        selection: &Selection<Anchor>,
12724        target: DisplayPoint,
12725        is_cut: bool,
12726        window: &mut Window,
12727        cx: &mut Context<Self>,
12728    ) {
12729        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12730        let buffer = display_map.buffer_snapshot();
12731        let mut edits = Vec::new();
12732        let insert_point = display_map
12733            .clip_point(target, Bias::Left)
12734            .to_point(&display_map);
12735        let text = buffer
12736            .text_for_range(selection.start..selection.end)
12737            .collect::<String>();
12738        if is_cut {
12739            edits.push(((selection.start..selection.end), String::new()));
12740        }
12741        let insert_anchor = buffer.anchor_before(insert_point);
12742        edits.push(((insert_anchor..insert_anchor), text));
12743        let last_edit_start = insert_anchor.bias_left(buffer);
12744        let last_edit_end = insert_anchor.bias_right(buffer);
12745        self.transact(window, cx, |this, window, cx| {
12746            this.buffer.update(cx, |buffer, cx| {
12747                buffer.edit(edits, None, cx);
12748            });
12749            this.change_selections(Default::default(), window, cx, |s| {
12750                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12751            });
12752        });
12753    }
12754
12755    pub fn clear_selection_drag_state(&mut self) {
12756        self.selection_drag_state = SelectionDragState::None;
12757    }
12758
12759    pub fn duplicate(
12760        &mut self,
12761        upwards: bool,
12762        whole_lines: bool,
12763        window: &mut Window,
12764        cx: &mut Context<Self>,
12765    ) {
12766        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12767
12768        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12769        let buffer = display_map.buffer_snapshot();
12770        let selections = self.selections.all::<Point>(&display_map);
12771
12772        let mut edits = Vec::new();
12773        let mut selections_iter = selections.iter().peekable();
12774        while let Some(selection) = selections_iter.next() {
12775            let mut rows = selection.spanned_rows(false, &display_map);
12776            // duplicate line-wise
12777            if whole_lines || selection.start == selection.end {
12778                // Avoid duplicating the same lines twice.
12779                while let Some(next_selection) = selections_iter.peek() {
12780                    let next_rows = next_selection.spanned_rows(false, &display_map);
12781                    if next_rows.start < rows.end {
12782                        rows.end = next_rows.end;
12783                        selections_iter.next().unwrap();
12784                    } else {
12785                        break;
12786                    }
12787                }
12788
12789                // Copy the text from the selected row region and splice it either at the start
12790                // or end of the region.
12791                let start = Point::new(rows.start.0, 0);
12792                let end = Point::new(
12793                    rows.end.previous_row().0,
12794                    buffer.line_len(rows.end.previous_row()),
12795                );
12796
12797                let mut text = buffer.text_for_range(start..end).collect::<String>();
12798
12799                let insert_location = if upwards {
12800                    // When duplicating upward, we need to insert before the current line.
12801                    // If we're on the last line and it doesn't end with a newline,
12802                    // we need to add a newline before the duplicated content.
12803                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12804                        && buffer.max_point().column > 0
12805                        && !text.ends_with('\n');
12806
12807                    if needs_leading_newline {
12808                        text.insert(0, '\n');
12809                        end
12810                    } else {
12811                        text.push('\n');
12812                        Point::new(rows.start.0, 0)
12813                    }
12814                } else {
12815                    text.push('\n');
12816                    start
12817                };
12818                edits.push((insert_location..insert_location, text));
12819            } else {
12820                // duplicate character-wise
12821                let start = selection.start;
12822                let end = selection.end;
12823                let text = buffer.text_for_range(start..end).collect::<String>();
12824                edits.push((selection.end..selection.end, text));
12825            }
12826        }
12827
12828        self.transact(window, cx, |this, window, cx| {
12829            this.buffer.update(cx, |buffer, cx| {
12830                buffer.edit(edits, None, cx);
12831            });
12832
12833            // When duplicating upward with whole lines, move the cursor to the duplicated line
12834            if upwards && whole_lines {
12835                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12836
12837                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12838                    let mut new_ranges = Vec::new();
12839                    let selections = s.all::<Point>(&display_map);
12840                    let mut selections_iter = selections.iter().peekable();
12841
12842                    while let Some(first_selection) = selections_iter.next() {
12843                        // Group contiguous selections together to find the total row span
12844                        let mut group_selections = vec![first_selection];
12845                        let mut rows = first_selection.spanned_rows(false, &display_map);
12846
12847                        while let Some(next_selection) = selections_iter.peek() {
12848                            let next_rows = next_selection.spanned_rows(false, &display_map);
12849                            if next_rows.start < rows.end {
12850                                rows.end = next_rows.end;
12851                                group_selections.push(selections_iter.next().unwrap());
12852                            } else {
12853                                break;
12854                            }
12855                        }
12856
12857                        let row_count = rows.end.0 - rows.start.0;
12858
12859                        // Move all selections in this group up by the total number of duplicated rows
12860                        for selection in group_selections {
12861                            let new_start = Point::new(
12862                                selection.start.row.saturating_sub(row_count),
12863                                selection.start.column,
12864                            );
12865
12866                            let new_end = Point::new(
12867                                selection.end.row.saturating_sub(row_count),
12868                                selection.end.column,
12869                            );
12870
12871                            new_ranges.push(new_start..new_end);
12872                        }
12873                    }
12874
12875                    s.select_ranges(new_ranges);
12876                });
12877            }
12878
12879            this.request_autoscroll(Autoscroll::fit(), cx);
12880        });
12881    }
12882
12883    pub fn duplicate_line_up(
12884        &mut self,
12885        _: &DuplicateLineUp,
12886        window: &mut Window,
12887        cx: &mut Context<Self>,
12888    ) {
12889        self.duplicate(true, true, window, cx);
12890    }
12891
12892    pub fn duplicate_line_down(
12893        &mut self,
12894        _: &DuplicateLineDown,
12895        window: &mut Window,
12896        cx: &mut Context<Self>,
12897    ) {
12898        self.duplicate(false, true, window, cx);
12899    }
12900
12901    pub fn duplicate_selection(
12902        &mut self,
12903        _: &DuplicateSelection,
12904        window: &mut Window,
12905        cx: &mut Context<Self>,
12906    ) {
12907        self.duplicate(false, false, window, cx);
12908    }
12909
12910    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12911        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12912        if self.mode.is_single_line() {
12913            cx.propagate();
12914            return;
12915        }
12916
12917        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12918        let buffer = self.buffer.read(cx).snapshot(cx);
12919
12920        let mut edits = Vec::new();
12921        let mut unfold_ranges = Vec::new();
12922        let mut refold_creases = Vec::new();
12923
12924        let selections = self.selections.all::<Point>(&display_map);
12925        let mut selections = selections.iter().peekable();
12926        let mut contiguous_row_selections = Vec::new();
12927        let mut new_selections = Vec::new();
12928
12929        while let Some(selection) = selections.next() {
12930            // Find all the selections that span a contiguous row range
12931            let (start_row, end_row) = consume_contiguous_rows(
12932                &mut contiguous_row_selections,
12933                selection,
12934                &display_map,
12935                &mut selections,
12936            );
12937
12938            // Move the text spanned by the row range to be before the line preceding the row range
12939            if start_row.0 > 0 {
12940                let range_to_move = Point::new(
12941                    start_row.previous_row().0,
12942                    buffer.line_len(start_row.previous_row()),
12943                )
12944                    ..Point::new(
12945                        end_row.previous_row().0,
12946                        buffer.line_len(end_row.previous_row()),
12947                    );
12948                let insertion_point = display_map
12949                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12950                    .0;
12951
12952                // Don't move lines across excerpts
12953                if buffer
12954                    .excerpt_containing(insertion_point..range_to_move.end)
12955                    .is_some()
12956                {
12957                    let text = buffer
12958                        .text_for_range(range_to_move.clone())
12959                        .flat_map(|s| s.chars())
12960                        .skip(1)
12961                        .chain(['\n'])
12962                        .collect::<String>();
12963
12964                    edits.push((
12965                        buffer.anchor_after(range_to_move.start)
12966                            ..buffer.anchor_before(range_to_move.end),
12967                        String::new(),
12968                    ));
12969                    let insertion_anchor = buffer.anchor_after(insertion_point);
12970                    edits.push((insertion_anchor..insertion_anchor, text));
12971
12972                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12973
12974                    // Move selections up
12975                    new_selections.extend(contiguous_row_selections.drain(..).map(
12976                        |mut selection| {
12977                            selection.start.row -= row_delta;
12978                            selection.end.row -= row_delta;
12979                            selection
12980                        },
12981                    ));
12982
12983                    // Move folds up
12984                    unfold_ranges.push(range_to_move.clone());
12985                    for fold in display_map.folds_in_range(
12986                        buffer.anchor_before(range_to_move.start)
12987                            ..buffer.anchor_after(range_to_move.end),
12988                    ) {
12989                        let mut start = fold.range.start.to_point(&buffer);
12990                        let mut end = fold.range.end.to_point(&buffer);
12991                        start.row -= row_delta;
12992                        end.row -= row_delta;
12993                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12994                    }
12995                }
12996            }
12997
12998            // If we didn't move line(s), preserve the existing selections
12999            new_selections.append(&mut contiguous_row_selections);
13000        }
13001
13002        self.transact(window, cx, |this, window, cx| {
13003            this.unfold_ranges(&unfold_ranges, true, true, cx);
13004            this.buffer.update(cx, |buffer, cx| {
13005                for (range, text) in edits {
13006                    buffer.edit([(range, text)], None, cx);
13007                }
13008            });
13009            this.fold_creases(refold_creases, true, window, cx);
13010            this.change_selections(Default::default(), window, cx, |s| {
13011                s.select(new_selections);
13012            })
13013        });
13014    }
13015
13016    pub fn move_line_down(
13017        &mut self,
13018        _: &MoveLineDown,
13019        window: &mut Window,
13020        cx: &mut Context<Self>,
13021    ) {
13022        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13023        if self.mode.is_single_line() {
13024            cx.propagate();
13025            return;
13026        }
13027
13028        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13029        let buffer = self.buffer.read(cx).snapshot(cx);
13030
13031        let mut edits = Vec::new();
13032        let mut unfold_ranges = Vec::new();
13033        let mut refold_creases = Vec::new();
13034
13035        let selections = self.selections.all::<Point>(&display_map);
13036        let mut selections = selections.iter().peekable();
13037        let mut contiguous_row_selections = Vec::new();
13038        let mut new_selections = Vec::new();
13039
13040        while let Some(selection) = selections.next() {
13041            // Find all the selections that span a contiguous row range
13042            let (start_row, end_row) = consume_contiguous_rows(
13043                &mut contiguous_row_selections,
13044                selection,
13045                &display_map,
13046                &mut selections,
13047            );
13048
13049            // Move the text spanned by the row range to be after the last line of the row range
13050            if end_row.0 <= buffer.max_point().row {
13051                let range_to_move =
13052                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13053                let insertion_point = display_map
13054                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13055                    .0;
13056
13057                // Don't move lines across excerpt boundaries
13058                if buffer
13059                    .excerpt_containing(range_to_move.start..insertion_point)
13060                    .is_some()
13061                {
13062                    let mut text = String::from("\n");
13063                    text.extend(buffer.text_for_range(range_to_move.clone()));
13064                    text.pop(); // Drop trailing newline
13065                    edits.push((
13066                        buffer.anchor_after(range_to_move.start)
13067                            ..buffer.anchor_before(range_to_move.end),
13068                        String::new(),
13069                    ));
13070                    let insertion_anchor = buffer.anchor_after(insertion_point);
13071                    edits.push((insertion_anchor..insertion_anchor, text));
13072
13073                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13074
13075                    // Move selections down
13076                    new_selections.extend(contiguous_row_selections.drain(..).map(
13077                        |mut selection| {
13078                            selection.start.row += row_delta;
13079                            selection.end.row += row_delta;
13080                            selection
13081                        },
13082                    ));
13083
13084                    // Move folds down
13085                    unfold_ranges.push(range_to_move.clone());
13086                    for fold in display_map.folds_in_range(
13087                        buffer.anchor_before(range_to_move.start)
13088                            ..buffer.anchor_after(range_to_move.end),
13089                    ) {
13090                        let mut start = fold.range.start.to_point(&buffer);
13091                        let mut end = fold.range.end.to_point(&buffer);
13092                        start.row += row_delta;
13093                        end.row += row_delta;
13094                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13095                    }
13096                }
13097            }
13098
13099            // If we didn't move line(s), preserve the existing selections
13100            new_selections.append(&mut contiguous_row_selections);
13101        }
13102
13103        self.transact(window, cx, |this, window, cx| {
13104            this.unfold_ranges(&unfold_ranges, true, true, cx);
13105            this.buffer.update(cx, |buffer, cx| {
13106                for (range, text) in edits {
13107                    buffer.edit([(range, text)], None, cx);
13108                }
13109            });
13110            this.fold_creases(refold_creases, true, window, cx);
13111            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13112        });
13113    }
13114
13115    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13116        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13117        let text_layout_details = &self.text_layout_details(window, cx);
13118        self.transact(window, cx, |this, window, cx| {
13119            let edits = this.change_selections(Default::default(), window, cx, |s| {
13120                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13121                s.move_with(&mut |display_map, selection| {
13122                    if !selection.is_empty() {
13123                        return;
13124                    }
13125
13126                    let mut head = selection.head();
13127                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13128                    if head.column() == display_map.line_len(head.row()) {
13129                        transpose_offset = display_map
13130                            .buffer_snapshot()
13131                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13132                    }
13133
13134                    if transpose_offset == MultiBufferOffset(0) {
13135                        return;
13136                    }
13137
13138                    *head.column_mut() += 1;
13139                    head = display_map.clip_point(head, Bias::Right);
13140                    let goal = SelectionGoal::HorizontalPosition(
13141                        display_map
13142                            .x_for_display_point(head, text_layout_details)
13143                            .into(),
13144                    );
13145                    selection.collapse_to(head, goal);
13146
13147                    let transpose_start = display_map
13148                        .buffer_snapshot()
13149                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13150                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13151                        let transpose_end = display_map
13152                            .buffer_snapshot()
13153                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13154                        if let Some(ch) = display_map
13155                            .buffer_snapshot()
13156                            .chars_at(transpose_start)
13157                            .next()
13158                        {
13159                            edits.push((transpose_start..transpose_offset, String::new()));
13160                            edits.push((transpose_end..transpose_end, ch.to_string()));
13161                        }
13162                    }
13163                });
13164                edits
13165            });
13166            this.buffer
13167                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13168            let selections = this
13169                .selections
13170                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13171            this.change_selections(Default::default(), window, cx, |s| {
13172                s.select(selections);
13173            });
13174        });
13175    }
13176
13177    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13178        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13179        if self.mode.is_single_line() {
13180            cx.propagate();
13181            return;
13182        }
13183
13184        self.rewrap_impl(RewrapOptions::default(), cx)
13185    }
13186
13187    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13188        let buffer = self.buffer.read(cx).snapshot(cx);
13189        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13190
13191        #[derive(Clone, Debug, PartialEq)]
13192        enum CommentFormat {
13193            /// single line comment, with prefix for line
13194            Line(String),
13195            /// single line within a block comment, with prefix for line
13196            BlockLine(String),
13197            /// a single line of a block comment that includes the initial delimiter
13198            BlockCommentWithStart(BlockCommentConfig),
13199            /// a single line of a block comment that includes the ending delimiter
13200            BlockCommentWithEnd(BlockCommentConfig),
13201        }
13202
13203        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13204        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13205            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13206                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13207                .peekable();
13208
13209            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13210                row
13211            } else {
13212                return Vec::new();
13213            };
13214
13215            let language_settings = buffer.language_settings_at(selection.head(), cx);
13216            let language_scope = buffer.language_scope_at(selection.head());
13217
13218            let indent_and_prefix_for_row =
13219                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13220                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13221                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13222                        &language_scope
13223                    {
13224                        let indent_end = Point::new(row, indent.len);
13225                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13226                        let line_text_after_indent = buffer
13227                            .text_for_range(indent_end..line_end)
13228                            .collect::<String>();
13229
13230                        let is_within_comment_override = buffer
13231                            .language_scope_at(indent_end)
13232                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13233                        let comment_delimiters = if is_within_comment_override {
13234                            // we are within a comment syntax node, but we don't
13235                            // yet know what kind of comment: block, doc or line
13236                            match (
13237                                language_scope.documentation_comment(),
13238                                language_scope.block_comment(),
13239                            ) {
13240                                (Some(config), _) | (_, Some(config))
13241                                    if buffer.contains_str_at(indent_end, &config.start) =>
13242                                {
13243                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13244                                }
13245                                (Some(config), _) | (_, Some(config))
13246                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13247                                {
13248                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13249                                }
13250                                (Some(config), _) | (_, Some(config))
13251                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13252                                {
13253                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13254                                }
13255                                (_, _) => language_scope
13256                                    .line_comment_prefixes()
13257                                    .iter()
13258                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13259                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13260                            }
13261                        } else {
13262                            // we not in an overridden comment node, but we may
13263                            // be within a non-overridden line comment node
13264                            language_scope
13265                                .line_comment_prefixes()
13266                                .iter()
13267                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13268                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13269                        };
13270
13271                        let rewrap_prefix = language_scope
13272                            .rewrap_prefixes()
13273                            .iter()
13274                            .find_map(|prefix_regex| {
13275                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13276                                    if mat.start() == 0 {
13277                                        Some(mat.as_str().to_string())
13278                                    } else {
13279                                        None
13280                                    }
13281                                })
13282                            })
13283                            .flatten();
13284                        (comment_delimiters, rewrap_prefix)
13285                    } else {
13286                        (None, None)
13287                    };
13288                    (indent, comment_prefix, rewrap_prefix)
13289                };
13290
13291            let mut ranges = Vec::new();
13292            let from_empty_selection = selection.is_empty();
13293
13294            let mut current_range_start = first_row;
13295            let mut prev_row = first_row;
13296            let (
13297                mut current_range_indent,
13298                mut current_range_comment_delimiters,
13299                mut current_range_rewrap_prefix,
13300            ) = indent_and_prefix_for_row(first_row);
13301
13302            for row in non_blank_rows_iter.skip(1) {
13303                let has_paragraph_break = row > prev_row + 1;
13304
13305                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13306                    indent_and_prefix_for_row(row);
13307
13308                let has_indent_change = row_indent != current_range_indent;
13309                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13310
13311                let has_boundary_change = has_comment_change
13312                    || row_rewrap_prefix.is_some()
13313                    || (has_indent_change && current_range_comment_delimiters.is_some());
13314
13315                if has_paragraph_break || has_boundary_change {
13316                    ranges.push((
13317                        language_settings.clone(),
13318                        Point::new(current_range_start, 0)
13319                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13320                        current_range_indent,
13321                        current_range_comment_delimiters.clone(),
13322                        current_range_rewrap_prefix.clone(),
13323                        from_empty_selection,
13324                    ));
13325                    current_range_start = row;
13326                    current_range_indent = row_indent;
13327                    current_range_comment_delimiters = row_comment_delimiters;
13328                    current_range_rewrap_prefix = row_rewrap_prefix;
13329                }
13330                prev_row = row;
13331            }
13332
13333            ranges.push((
13334                language_settings.clone(),
13335                Point::new(current_range_start, 0)
13336                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13337                current_range_indent,
13338                current_range_comment_delimiters,
13339                current_range_rewrap_prefix,
13340                from_empty_selection,
13341            ));
13342
13343            ranges
13344        });
13345
13346        let mut edits = Vec::new();
13347        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13348
13349        for (
13350            language_settings,
13351            wrap_range,
13352            mut indent_size,
13353            comment_prefix,
13354            rewrap_prefix,
13355            from_empty_selection,
13356        ) in wrap_ranges
13357        {
13358            let mut start_row = wrap_range.start.row;
13359            let mut end_row = wrap_range.end.row;
13360
13361            // Skip selections that overlap with a range that has already been rewrapped.
13362            let selection_range = start_row..end_row;
13363            if rewrapped_row_ranges
13364                .iter()
13365                .any(|range| range.overlaps(&selection_range))
13366            {
13367                continue;
13368            }
13369
13370            let tab_size = language_settings.tab_size;
13371
13372            let (line_prefix, inside_comment) = match &comment_prefix {
13373                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13374                    (Some(prefix.as_str()), true)
13375                }
13376                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13377                    (Some(prefix.as_ref()), true)
13378                }
13379                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13380                    start: _,
13381                    end: _,
13382                    prefix,
13383                    tab_size,
13384                })) => {
13385                    indent_size.len += tab_size;
13386                    (Some(prefix.as_ref()), true)
13387                }
13388                None => (None, false),
13389            };
13390            let indent_prefix = indent_size.chars().collect::<String>();
13391            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13392
13393            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13394                RewrapBehavior::InComments => inside_comment,
13395                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13396                RewrapBehavior::Anywhere => true,
13397            };
13398
13399            let should_rewrap = options.override_language_settings
13400                || allow_rewrap_based_on_language
13401                || self.hard_wrap.is_some();
13402            if !should_rewrap {
13403                continue;
13404            }
13405
13406            if from_empty_selection {
13407                'expand_upwards: while start_row > 0 {
13408                    let prev_row = start_row - 1;
13409                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13410                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13411                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13412                    {
13413                        start_row = prev_row;
13414                    } else {
13415                        break 'expand_upwards;
13416                    }
13417                }
13418
13419                'expand_downwards: while end_row < buffer.max_point().row {
13420                    let next_row = end_row + 1;
13421                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13422                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13423                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13424                    {
13425                        end_row = next_row;
13426                    } else {
13427                        break 'expand_downwards;
13428                    }
13429                }
13430            }
13431
13432            let start = Point::new(start_row, 0);
13433            let start_offset = ToOffset::to_offset(&start, &buffer);
13434            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13435            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13436            let mut first_line_delimiter = None;
13437            let mut last_line_delimiter = None;
13438            let Some(lines_without_prefixes) = selection_text
13439                .lines()
13440                .enumerate()
13441                .map(|(ix, line)| {
13442                    let line_trimmed = line.trim_start();
13443                    if rewrap_prefix.is_some() && ix > 0 {
13444                        Ok(line_trimmed)
13445                    } else if let Some(
13446                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13447                            start,
13448                            prefix,
13449                            end,
13450                            tab_size,
13451                        })
13452                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13453                            start,
13454                            prefix,
13455                            end,
13456                            tab_size,
13457                        }),
13458                    ) = &comment_prefix
13459                    {
13460                        let line_trimmed = line_trimmed
13461                            .strip_prefix(start.as_ref())
13462                            .map(|s| {
13463                                let mut indent_size = indent_size;
13464                                indent_size.len -= tab_size;
13465                                let indent_prefix: String = indent_size.chars().collect();
13466                                first_line_delimiter = Some((indent_prefix, start));
13467                                s.trim_start()
13468                            })
13469                            .unwrap_or(line_trimmed);
13470                        let line_trimmed = line_trimmed
13471                            .strip_suffix(end.as_ref())
13472                            .map(|s| {
13473                                last_line_delimiter = Some(end);
13474                                s.trim_end()
13475                            })
13476                            .unwrap_or(line_trimmed);
13477                        let line_trimmed = line_trimmed
13478                            .strip_prefix(prefix.as_ref())
13479                            .unwrap_or(line_trimmed);
13480                        Ok(line_trimmed)
13481                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13482                        line_trimmed.strip_prefix(prefix).with_context(|| {
13483                            format!("line did not start with prefix {prefix:?}: {line:?}")
13484                        })
13485                    } else {
13486                        line_trimmed
13487                            .strip_prefix(&line_prefix.trim_start())
13488                            .with_context(|| {
13489                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13490                            })
13491                    }
13492                })
13493                .collect::<Result<Vec<_>, _>>()
13494                .log_err()
13495            else {
13496                continue;
13497            };
13498
13499            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13500                buffer
13501                    .language_settings_at(Point::new(start_row, 0), cx)
13502                    .preferred_line_length as usize
13503            });
13504
13505            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13506                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13507            } else {
13508                line_prefix.clone()
13509            };
13510
13511            let wrapped_text = {
13512                let mut wrapped_text = wrap_with_prefix(
13513                    line_prefix,
13514                    subsequent_lines_prefix,
13515                    lines_without_prefixes.join("\n"),
13516                    wrap_column,
13517                    tab_size,
13518                    options.preserve_existing_whitespace,
13519                );
13520
13521                if let Some((indent, delimiter)) = first_line_delimiter {
13522                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13523                }
13524                if let Some(last_line) = last_line_delimiter {
13525                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13526                }
13527
13528                wrapped_text
13529            };
13530
13531            // TODO: should always use char-based diff while still supporting cursor behavior that
13532            // matches vim.
13533            let mut diff_options = DiffOptions::default();
13534            if options.override_language_settings {
13535                diff_options.max_word_diff_len = 0;
13536                diff_options.max_word_diff_line_count = 0;
13537            } else {
13538                diff_options.max_word_diff_len = usize::MAX;
13539                diff_options.max_word_diff_line_count = usize::MAX;
13540            }
13541
13542            for (old_range, new_text) in
13543                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13544            {
13545                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13546                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13547                edits.push((edit_start..edit_end, new_text));
13548            }
13549
13550            rewrapped_row_ranges.push(start_row..=end_row);
13551        }
13552
13553        self.buffer
13554            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13555    }
13556
13557    pub fn cut_common(
13558        &mut self,
13559        cut_no_selection_line: bool,
13560        window: &mut Window,
13561        cx: &mut Context<Self>,
13562    ) -> ClipboardItem {
13563        let mut text = String::new();
13564        let buffer = self.buffer.read(cx).snapshot(cx);
13565        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13566        let mut clipboard_selections = Vec::with_capacity(selections.len());
13567        {
13568            let max_point = buffer.max_point();
13569            let mut is_first = true;
13570            let mut prev_selection_was_entire_line = false;
13571            for selection in &mut selections {
13572                let is_entire_line =
13573                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13574                if is_entire_line {
13575                    selection.start = Point::new(selection.start.row, 0);
13576                    if !selection.is_empty() && selection.end.column == 0 {
13577                        selection.end = cmp::min(max_point, selection.end);
13578                    } else {
13579                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13580                    }
13581                    selection.goal = SelectionGoal::None;
13582                }
13583                if is_first {
13584                    is_first = false;
13585                } else if !prev_selection_was_entire_line {
13586                    text += "\n";
13587                }
13588                prev_selection_was_entire_line = is_entire_line;
13589                let mut len = 0;
13590                for chunk in buffer.text_for_range(selection.start..selection.end) {
13591                    text.push_str(chunk);
13592                    len += chunk.len();
13593                }
13594
13595                clipboard_selections.push(ClipboardSelection::for_buffer(
13596                    len,
13597                    is_entire_line,
13598                    selection.range(),
13599                    &buffer,
13600                    self.project.as_ref(),
13601                    cx,
13602                ));
13603            }
13604        }
13605
13606        self.transact(window, cx, |this, window, cx| {
13607            this.change_selections(Default::default(), window, cx, |s| {
13608                s.select(selections);
13609            });
13610            this.insert("", window, cx);
13611        });
13612        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13613    }
13614
13615    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13616        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13617        let item = self.cut_common(true, window, cx);
13618        cx.write_to_clipboard(item);
13619    }
13620
13621    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13622        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13623        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13624            s.move_with(&mut |snapshot, sel| {
13625                if sel.is_empty() {
13626                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13627                }
13628                if sel.is_empty() {
13629                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13630                }
13631            });
13632        });
13633        let item = self.cut_common(false, window, cx);
13634        cx.set_global(KillRing(item))
13635    }
13636
13637    pub fn kill_ring_yank(
13638        &mut self,
13639        _: &KillRingYank,
13640        window: &mut Window,
13641        cx: &mut Context<Self>,
13642    ) {
13643        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13644        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13645            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13646                (kill_ring.text().to_string(), kill_ring.metadata_json())
13647            } else {
13648                return;
13649            }
13650        } else {
13651            return;
13652        };
13653        self.do_paste(&text, metadata, false, window, cx);
13654    }
13655
13656    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13657        self.do_copy(true, cx);
13658    }
13659
13660    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13661        self.do_copy(false, cx);
13662    }
13663
13664    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13665        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13666        let buffer = self.buffer.read(cx).read(cx);
13667        let mut text = String::new();
13668        let mut clipboard_selections = Vec::with_capacity(selections.len());
13669
13670        let max_point = buffer.max_point();
13671        let mut is_first = true;
13672        for selection in &selections {
13673            let mut start = selection.start;
13674            let mut end = selection.end;
13675            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13676            let mut add_trailing_newline = false;
13677            if is_entire_line {
13678                start = Point::new(start.row, 0);
13679                let next_line_start = Point::new(end.row + 1, 0);
13680                if next_line_start <= max_point {
13681                    end = next_line_start;
13682                } else {
13683                    // We're on the last line without a trailing newline.
13684                    // Copy to the end of the line and add a newline afterwards.
13685                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13686                    add_trailing_newline = true;
13687                }
13688            }
13689
13690            let mut trimmed_selections = Vec::new();
13691            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13692                let row = MultiBufferRow(start.row);
13693                let first_indent = buffer.indent_size_for_line(row);
13694                if first_indent.len == 0 || start.column > first_indent.len {
13695                    trimmed_selections.push(start..end);
13696                } else {
13697                    trimmed_selections.push(
13698                        Point::new(row.0, first_indent.len)
13699                            ..Point::new(row.0, buffer.line_len(row)),
13700                    );
13701                    for row in start.row + 1..=end.row {
13702                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13703                        if row == end.row {
13704                            line_len = end.column;
13705                        }
13706                        if line_len == 0 {
13707                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13708                            continue;
13709                        }
13710                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13711                        if row_indent_size.len >= first_indent.len {
13712                            trimmed_selections
13713                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13714                        } else {
13715                            trimmed_selections.clear();
13716                            trimmed_selections.push(start..end);
13717                            break;
13718                        }
13719                    }
13720                }
13721            } else {
13722                trimmed_selections.push(start..end);
13723            }
13724
13725            let is_multiline_trim = trimmed_selections.len() > 1;
13726            let mut selection_len: usize = 0;
13727            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13728
13729            for trimmed_range in trimmed_selections {
13730                if is_first {
13731                    is_first = false;
13732                } else if is_multiline_trim || !prev_selection_was_entire_line {
13733                    text.push('\n');
13734                    if is_multiline_trim {
13735                        selection_len += 1;
13736                    }
13737                }
13738                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13739                    text.push_str(chunk);
13740                    selection_len += chunk.len();
13741                }
13742                if add_trailing_newline {
13743                    text.push('\n');
13744                    selection_len += 1;
13745                }
13746            }
13747
13748            clipboard_selections.push(ClipboardSelection::for_buffer(
13749                selection_len,
13750                is_entire_line,
13751                start..end,
13752                &buffer,
13753                self.project.as_ref(),
13754                cx,
13755            ));
13756        }
13757
13758        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13759            text,
13760            clipboard_selections,
13761        ));
13762    }
13763
13764    pub fn do_paste(
13765        &mut self,
13766        text: &String,
13767        clipboard_selections: Option<Vec<ClipboardSelection>>,
13768        handle_entire_lines: bool,
13769        window: &mut Window,
13770        cx: &mut Context<Self>,
13771    ) {
13772        if self.read_only(cx) {
13773            return;
13774        }
13775
13776        let clipboard_text = Cow::Borrowed(text.as_str());
13777
13778        self.transact(window, cx, |this, window, cx| {
13779            let had_active_edit_prediction = this.has_active_edit_prediction();
13780            let display_map = this.display_snapshot(cx);
13781            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13782            let cursor_offset = this
13783                .selections
13784                .last::<MultiBufferOffset>(&display_map)
13785                .head();
13786
13787            if let Some(mut clipboard_selections) = clipboard_selections {
13788                let all_selections_were_entire_line =
13789                    clipboard_selections.iter().all(|s| s.is_entire_line);
13790                let first_selection_indent_column =
13791                    clipboard_selections.first().map(|s| s.first_line_indent);
13792                if clipboard_selections.len() != old_selections.len() {
13793                    clipboard_selections.drain(..);
13794                }
13795                let mut auto_indent_on_paste = true;
13796
13797                this.buffer.update(cx, |buffer, cx| {
13798                    let snapshot = buffer.read(cx);
13799                    auto_indent_on_paste = snapshot
13800                        .language_settings_at(cursor_offset, cx)
13801                        .auto_indent_on_paste;
13802
13803                    let mut start_offset = 0;
13804                    let mut edits = Vec::new();
13805                    let mut original_indent_columns = Vec::new();
13806                    for (ix, selection) in old_selections.iter().enumerate() {
13807                        let to_insert;
13808                        let entire_line;
13809                        let original_indent_column;
13810                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13811                            let end_offset = start_offset + clipboard_selection.len;
13812                            to_insert = &clipboard_text[start_offset..end_offset];
13813                            entire_line = clipboard_selection.is_entire_line;
13814                            start_offset = if entire_line {
13815                                end_offset
13816                            } else {
13817                                end_offset + 1
13818                            };
13819                            original_indent_column = Some(clipboard_selection.first_line_indent);
13820                        } else {
13821                            to_insert = &*clipboard_text;
13822                            entire_line = all_selections_were_entire_line;
13823                            original_indent_column = first_selection_indent_column
13824                        }
13825
13826                        let (range, to_insert) =
13827                            if selection.is_empty() && handle_entire_lines && entire_line {
13828                                // If the corresponding selection was empty when this slice of the
13829                                // clipboard text was written, then the entire line containing the
13830                                // selection was copied. If this selection is also currently empty,
13831                                // then paste the line before the current line of the buffer.
13832                                let column = selection.start.to_point(&snapshot).column as usize;
13833                                let line_start = selection.start - column;
13834                                (line_start..line_start, Cow::Borrowed(to_insert))
13835                            } else {
13836                                let language = snapshot.language_at(selection.head());
13837                                let range = selection.range();
13838                                if let Some(language) = language
13839                                    && language.name() == "Markdown"
13840                                {
13841                                    edit_for_markdown_paste(
13842                                        &snapshot,
13843                                        range,
13844                                        to_insert,
13845                                        url::Url::parse(to_insert).ok(),
13846                                    )
13847                                } else {
13848                                    (range, Cow::Borrowed(to_insert))
13849                                }
13850                            };
13851
13852                        edits.push((range, to_insert));
13853                        original_indent_columns.push(original_indent_column);
13854                    }
13855                    drop(snapshot);
13856
13857                    buffer.edit(
13858                        edits,
13859                        if auto_indent_on_paste {
13860                            Some(AutoindentMode::Block {
13861                                original_indent_columns,
13862                            })
13863                        } else {
13864                            None
13865                        },
13866                        cx,
13867                    );
13868                });
13869
13870                let selections = this
13871                    .selections
13872                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13873                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13874            } else {
13875                let url = url::Url::parse(&clipboard_text).ok();
13876
13877                let auto_indent_mode = if !clipboard_text.is_empty() {
13878                    Some(AutoindentMode::Block {
13879                        original_indent_columns: Vec::new(),
13880                    })
13881                } else {
13882                    None
13883                };
13884
13885                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13886                    let snapshot = buffer.snapshot(cx);
13887
13888                    let anchors = old_selections
13889                        .iter()
13890                        .map(|s| {
13891                            let anchor = snapshot.anchor_after(s.head());
13892                            s.map(|_| anchor)
13893                        })
13894                        .collect::<Vec<_>>();
13895
13896                    let mut edits = Vec::new();
13897
13898                    // When pasting text without metadata (e.g. copied from an
13899                    // external editor using multiple cursors) and the number of
13900                    // lines matches the number of selections, distribute one
13901                    // line per cursor instead of pasting the whole text at each.
13902                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
13903                    let distribute_lines =
13904                        old_selections.len() > 1 && lines.len() == old_selections.len();
13905
13906                    for (ix, selection) in old_selections.iter().enumerate() {
13907                        let language = snapshot.language_at(selection.head());
13908                        let range = selection.range();
13909
13910                        let text_for_cursor: &str = if distribute_lines {
13911                            lines[ix]
13912                        } else {
13913                            &clipboard_text
13914                        };
13915
13916                        let (edit_range, edit_text) = if let Some(language) = language
13917                            && language.name() == "Markdown"
13918                        {
13919                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
13920                        } else {
13921                            (range, Cow::Borrowed(text_for_cursor))
13922                        };
13923
13924                        edits.push((edit_range, edit_text));
13925                    }
13926
13927                    drop(snapshot);
13928                    buffer.edit(edits, auto_indent_mode, cx);
13929
13930                    anchors
13931                });
13932
13933                this.change_selections(Default::default(), window, cx, |s| {
13934                    s.select_anchors(selection_anchors);
13935                });
13936            }
13937
13938            //   🤔                 |    ..     | show_in_menu |
13939            // | ..                  |   true        true
13940            // | had_edit_prediction |   false       true
13941
13942            let trigger_in_words =
13943                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13944
13945            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13946        });
13947    }
13948
13949    pub fn diff_clipboard_with_selection(
13950        &mut self,
13951        _: &DiffClipboardWithSelection,
13952        window: &mut Window,
13953        cx: &mut Context<Self>,
13954    ) {
13955        let selections = self
13956            .selections
13957            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13958
13959        if selections.is_empty() {
13960            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13961            return;
13962        };
13963
13964        let clipboard_text = match cx.read_from_clipboard() {
13965            Some(item) => match item.entries().first() {
13966                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13967                _ => None,
13968            },
13969            None => None,
13970        };
13971
13972        let Some(clipboard_text) = clipboard_text else {
13973            log::warn!("Clipboard doesn't contain text.");
13974            return;
13975        };
13976
13977        window.dispatch_action(
13978            Box::new(DiffClipboardWithSelectionData {
13979                clipboard_text,
13980                editor: cx.entity(),
13981            }),
13982            cx,
13983        );
13984    }
13985
13986    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13987        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13988        if let Some(item) = cx.read_from_clipboard() {
13989            let entries = item.entries();
13990
13991            match entries.first() {
13992                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13993                // of all the pasted entries.
13994                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13995                    .do_paste(
13996                        clipboard_string.text(),
13997                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13998                        true,
13999                        window,
14000                        cx,
14001                    ),
14002                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14003            }
14004        }
14005    }
14006
14007    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14008        if self.read_only(cx) {
14009            return;
14010        }
14011
14012        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14013
14014        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14015            if let Some((selections, _)) =
14016                self.selection_history.transaction(transaction_id).cloned()
14017            {
14018                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14019                    s.select_anchors(selections.to_vec());
14020                });
14021            } else {
14022                log::error!(
14023                    "No entry in selection_history found for undo. \
14024                     This may correspond to a bug where undo does not update the selection. \
14025                     If this is occurring, please add details to \
14026                     https://github.com/zed-industries/zed/issues/22692"
14027                );
14028            }
14029            self.request_autoscroll(Autoscroll::fit(), cx);
14030            self.unmark_text(window, cx);
14031            self.refresh_edit_prediction(true, false, window, cx);
14032            cx.emit(EditorEvent::Edited { transaction_id });
14033            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14034        }
14035    }
14036
14037    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14038        if self.read_only(cx) {
14039            return;
14040        }
14041
14042        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14043
14044        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14045            if let Some((_, Some(selections))) =
14046                self.selection_history.transaction(transaction_id).cloned()
14047            {
14048                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14049                    s.select_anchors(selections.to_vec());
14050                });
14051            } else {
14052                log::error!(
14053                    "No entry in selection_history found for redo. \
14054                     This may correspond to a bug where undo does not update the selection. \
14055                     If this is occurring, please add details to \
14056                     https://github.com/zed-industries/zed/issues/22692"
14057                );
14058            }
14059            self.request_autoscroll(Autoscroll::fit(), cx);
14060            self.unmark_text(window, cx);
14061            self.refresh_edit_prediction(true, false, window, cx);
14062            cx.emit(EditorEvent::Edited { transaction_id });
14063        }
14064    }
14065
14066    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14067        self.buffer
14068            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14069    }
14070
14071    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14072        self.buffer
14073            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14074    }
14075
14076    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14077        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14078        self.change_selections(Default::default(), window, cx, |s| {
14079            s.move_with(&mut |map, selection| {
14080                let cursor = if selection.is_empty() {
14081                    movement::left(map, selection.start)
14082                } else {
14083                    selection.start
14084                };
14085                selection.collapse_to(cursor, SelectionGoal::None);
14086            });
14087        })
14088    }
14089
14090    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14091        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14092        self.change_selections(Default::default(), window, cx, |s| {
14093            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14094        })
14095    }
14096
14097    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14098        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14099        self.change_selections(Default::default(), window, cx, |s| {
14100            s.move_with(&mut |map, selection| {
14101                let cursor = if selection.is_empty() {
14102                    movement::right(map, selection.end)
14103                } else {
14104                    selection.end
14105                };
14106                selection.collapse_to(cursor, SelectionGoal::None)
14107            });
14108        })
14109    }
14110
14111    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14112        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14113        self.change_selections(Default::default(), window, cx, |s| {
14114            s.move_heads_with(&mut |map, head, _| {
14115                (movement::right(map, head), SelectionGoal::None)
14116            });
14117        });
14118    }
14119
14120    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14121        if self.take_rename(true, window, cx).is_some() {
14122            return;
14123        }
14124
14125        if self.mode.is_single_line() {
14126            cx.propagate();
14127            return;
14128        }
14129
14130        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14131
14132        let text_layout_details = &self.text_layout_details(window, cx);
14133        let selection_count = self.selections.count();
14134        let first_selection = self.selections.first_anchor();
14135
14136        self.change_selections(Default::default(), window, cx, |s| {
14137            s.move_with(&mut |map, selection| {
14138                if !selection.is_empty() {
14139                    selection.goal = SelectionGoal::None;
14140                }
14141                let (cursor, goal) = movement::up(
14142                    map,
14143                    selection.start,
14144                    selection.goal,
14145                    false,
14146                    text_layout_details,
14147                );
14148                selection.collapse_to(cursor, goal);
14149            });
14150        });
14151
14152        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14153        {
14154            cx.propagate();
14155        }
14156    }
14157
14158    pub fn move_up_by_lines(
14159        &mut self,
14160        action: &MoveUpByLines,
14161        window: &mut Window,
14162        cx: &mut Context<Self>,
14163    ) {
14164        if self.take_rename(true, window, cx).is_some() {
14165            return;
14166        }
14167
14168        if self.mode.is_single_line() {
14169            cx.propagate();
14170            return;
14171        }
14172
14173        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14174
14175        let text_layout_details = &self.text_layout_details(window, cx);
14176
14177        self.change_selections(Default::default(), window, cx, |s| {
14178            s.move_with(&mut |map, selection| {
14179                if !selection.is_empty() {
14180                    selection.goal = SelectionGoal::None;
14181                }
14182                let (cursor, goal) = movement::up_by_rows(
14183                    map,
14184                    selection.start,
14185                    action.lines,
14186                    selection.goal,
14187                    false,
14188                    text_layout_details,
14189                );
14190                selection.collapse_to(cursor, goal);
14191            });
14192        })
14193    }
14194
14195    pub fn move_down_by_lines(
14196        &mut self,
14197        action: &MoveDownByLines,
14198        window: &mut Window,
14199        cx: &mut Context<Self>,
14200    ) {
14201        if self.take_rename(true, window, cx).is_some() {
14202            return;
14203        }
14204
14205        if self.mode.is_single_line() {
14206            cx.propagate();
14207            return;
14208        }
14209
14210        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14211
14212        let text_layout_details = &self.text_layout_details(window, cx);
14213
14214        self.change_selections(Default::default(), window, cx, |s| {
14215            s.move_with(&mut |map, selection| {
14216                if !selection.is_empty() {
14217                    selection.goal = SelectionGoal::None;
14218                }
14219                let (cursor, goal) = movement::down_by_rows(
14220                    map,
14221                    selection.start,
14222                    action.lines,
14223                    selection.goal,
14224                    false,
14225                    text_layout_details,
14226                );
14227                selection.collapse_to(cursor, goal);
14228            });
14229        })
14230    }
14231
14232    pub fn select_down_by_lines(
14233        &mut self,
14234        action: &SelectDownByLines,
14235        window: &mut Window,
14236        cx: &mut Context<Self>,
14237    ) {
14238        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14239        let text_layout_details = &self.text_layout_details(window, cx);
14240        self.change_selections(Default::default(), window, cx, |s| {
14241            s.move_heads_with(&mut |map, head, goal| {
14242                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14243            })
14244        })
14245    }
14246
14247    pub fn select_up_by_lines(
14248        &mut self,
14249        action: &SelectUpByLines,
14250        window: &mut Window,
14251        cx: &mut Context<Self>,
14252    ) {
14253        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14254        let text_layout_details = &self.text_layout_details(window, cx);
14255        self.change_selections(Default::default(), window, cx, |s| {
14256            s.move_heads_with(&mut |map, head, goal| {
14257                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14258            })
14259        })
14260    }
14261
14262    pub fn select_page_up(
14263        &mut self,
14264        _: &SelectPageUp,
14265        window: &mut Window,
14266        cx: &mut Context<Self>,
14267    ) {
14268        let Some(row_count) = self.visible_row_count() else {
14269            return;
14270        };
14271
14272        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14273
14274        let text_layout_details = &self.text_layout_details(window, cx);
14275
14276        self.change_selections(Default::default(), window, cx, |s| {
14277            s.move_heads_with(&mut |map, head, goal| {
14278                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14279            })
14280        })
14281    }
14282
14283    pub fn move_page_up(
14284        &mut self,
14285        action: &MovePageUp,
14286        window: &mut Window,
14287        cx: &mut Context<Self>,
14288    ) {
14289        if self.take_rename(true, window, cx).is_some() {
14290            return;
14291        }
14292
14293        if self
14294            .context_menu
14295            .borrow_mut()
14296            .as_mut()
14297            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14298            .unwrap_or(false)
14299        {
14300            return;
14301        }
14302
14303        if matches!(self.mode, EditorMode::SingleLine) {
14304            cx.propagate();
14305            return;
14306        }
14307
14308        let Some(row_count) = self.visible_row_count() else {
14309            return;
14310        };
14311
14312        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14313
14314        let effects = if action.center_cursor {
14315            SelectionEffects::scroll(Autoscroll::center())
14316        } else {
14317            SelectionEffects::default()
14318        };
14319
14320        let text_layout_details = &self.text_layout_details(window, cx);
14321
14322        self.change_selections(effects, window, cx, |s| {
14323            s.move_with(&mut |map, selection| {
14324                if !selection.is_empty() {
14325                    selection.goal = SelectionGoal::None;
14326                }
14327                let (cursor, goal) = movement::up_by_rows(
14328                    map,
14329                    selection.end,
14330                    row_count,
14331                    selection.goal,
14332                    false,
14333                    text_layout_details,
14334                );
14335                selection.collapse_to(cursor, goal);
14336            });
14337        });
14338    }
14339
14340    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14341        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14342        let text_layout_details = &self.text_layout_details(window, cx);
14343        self.change_selections(Default::default(), window, cx, |s| {
14344            s.move_heads_with(&mut |map, head, goal| {
14345                movement::up(map, head, goal, false, text_layout_details)
14346            })
14347        })
14348    }
14349
14350    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14351        self.take_rename(true, window, cx);
14352
14353        if self.mode.is_single_line() {
14354            cx.propagate();
14355            return;
14356        }
14357
14358        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14359
14360        let text_layout_details = &self.text_layout_details(window, cx);
14361        let selection_count = self.selections.count();
14362        let first_selection = self.selections.first_anchor();
14363
14364        self.change_selections(Default::default(), window, cx, |s| {
14365            s.move_with(&mut |map, selection| {
14366                if !selection.is_empty() {
14367                    selection.goal = SelectionGoal::None;
14368                }
14369                let (cursor, goal) = movement::down(
14370                    map,
14371                    selection.end,
14372                    selection.goal,
14373                    false,
14374                    text_layout_details,
14375                );
14376                selection.collapse_to(cursor, goal);
14377            });
14378        });
14379
14380        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14381        {
14382            cx.propagate();
14383        }
14384    }
14385
14386    pub fn select_page_down(
14387        &mut self,
14388        _: &SelectPageDown,
14389        window: &mut Window,
14390        cx: &mut Context<Self>,
14391    ) {
14392        let Some(row_count) = self.visible_row_count() else {
14393            return;
14394        };
14395
14396        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14397
14398        let text_layout_details = &self.text_layout_details(window, cx);
14399
14400        self.change_selections(Default::default(), window, cx, |s| {
14401            s.move_heads_with(&mut |map, head, goal| {
14402                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14403            })
14404        })
14405    }
14406
14407    pub fn move_page_down(
14408        &mut self,
14409        action: &MovePageDown,
14410        window: &mut Window,
14411        cx: &mut Context<Self>,
14412    ) {
14413        if self.take_rename(true, window, cx).is_some() {
14414            return;
14415        }
14416
14417        if self
14418            .context_menu
14419            .borrow_mut()
14420            .as_mut()
14421            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14422            .unwrap_or(false)
14423        {
14424            return;
14425        }
14426
14427        if matches!(self.mode, EditorMode::SingleLine) {
14428            cx.propagate();
14429            return;
14430        }
14431
14432        let Some(row_count) = self.visible_row_count() else {
14433            return;
14434        };
14435
14436        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14437
14438        let effects = if action.center_cursor {
14439            SelectionEffects::scroll(Autoscroll::center())
14440        } else {
14441            SelectionEffects::default()
14442        };
14443
14444        let text_layout_details = &self.text_layout_details(window, cx);
14445        self.change_selections(effects, window, cx, |s| {
14446            s.move_with(&mut |map, selection| {
14447                if !selection.is_empty() {
14448                    selection.goal = SelectionGoal::None;
14449                }
14450                let (cursor, goal) = movement::down_by_rows(
14451                    map,
14452                    selection.end,
14453                    row_count,
14454                    selection.goal,
14455                    false,
14456                    text_layout_details,
14457                );
14458                selection.collapse_to(cursor, goal);
14459            });
14460        });
14461    }
14462
14463    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14464        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14465        let text_layout_details = &self.text_layout_details(window, cx);
14466        self.change_selections(Default::default(), window, cx, |s| {
14467            s.move_heads_with(&mut |map, head, goal| {
14468                movement::down(map, head, goal, false, text_layout_details)
14469            })
14470        });
14471    }
14472
14473    pub fn context_menu_first(
14474        &mut self,
14475        _: &ContextMenuFirst,
14476        window: &mut Window,
14477        cx: &mut Context<Self>,
14478    ) {
14479        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14480            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14481        }
14482    }
14483
14484    pub fn context_menu_prev(
14485        &mut self,
14486        _: &ContextMenuPrevious,
14487        window: &mut Window,
14488        cx: &mut Context<Self>,
14489    ) {
14490        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14491            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14492        }
14493    }
14494
14495    pub fn context_menu_next(
14496        &mut self,
14497        _: &ContextMenuNext,
14498        window: &mut Window,
14499        cx: &mut Context<Self>,
14500    ) {
14501        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14502            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14503        }
14504    }
14505
14506    pub fn context_menu_last(
14507        &mut self,
14508        _: &ContextMenuLast,
14509        window: &mut Window,
14510        cx: &mut Context<Self>,
14511    ) {
14512        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14513            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14514        }
14515    }
14516
14517    pub fn signature_help_prev(
14518        &mut self,
14519        _: &SignatureHelpPrevious,
14520        _: &mut Window,
14521        cx: &mut Context<Self>,
14522    ) {
14523        if let Some(popover) = self.signature_help_state.popover_mut() {
14524            if popover.current_signature == 0 {
14525                popover.current_signature = popover.signatures.len() - 1;
14526            } else {
14527                popover.current_signature -= 1;
14528            }
14529            cx.notify();
14530        }
14531    }
14532
14533    pub fn signature_help_next(
14534        &mut self,
14535        _: &SignatureHelpNext,
14536        _: &mut Window,
14537        cx: &mut Context<Self>,
14538    ) {
14539        if let Some(popover) = self.signature_help_state.popover_mut() {
14540            if popover.current_signature + 1 == popover.signatures.len() {
14541                popover.current_signature = 0;
14542            } else {
14543                popover.current_signature += 1;
14544            }
14545            cx.notify();
14546        }
14547    }
14548
14549    pub fn move_to_previous_word_start(
14550        &mut self,
14551        _: &MoveToPreviousWordStart,
14552        window: &mut Window,
14553        cx: &mut Context<Self>,
14554    ) {
14555        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14556        self.change_selections(Default::default(), window, cx, |s| {
14557            s.move_cursors_with(&mut |map, head, _| {
14558                (
14559                    movement::previous_word_start(map, head),
14560                    SelectionGoal::None,
14561                )
14562            });
14563        })
14564    }
14565
14566    pub fn move_to_previous_subword_start(
14567        &mut self,
14568        _: &MoveToPreviousSubwordStart,
14569        window: &mut Window,
14570        cx: &mut Context<Self>,
14571    ) {
14572        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14573        self.change_selections(Default::default(), window, cx, |s| {
14574            s.move_cursors_with(&mut |map, head, _| {
14575                (
14576                    movement::previous_subword_start(map, head),
14577                    SelectionGoal::None,
14578                )
14579            });
14580        })
14581    }
14582
14583    pub fn select_to_previous_word_start(
14584        &mut self,
14585        _: &SelectToPreviousWordStart,
14586        window: &mut Window,
14587        cx: &mut Context<Self>,
14588    ) {
14589        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14590        self.change_selections(Default::default(), window, cx, |s| {
14591            s.move_heads_with(&mut |map, head, _| {
14592                (
14593                    movement::previous_word_start(map, head),
14594                    SelectionGoal::None,
14595                )
14596            });
14597        })
14598    }
14599
14600    pub fn select_to_previous_subword_start(
14601        &mut self,
14602        _: &SelectToPreviousSubwordStart,
14603        window: &mut Window,
14604        cx: &mut Context<Self>,
14605    ) {
14606        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14607        self.change_selections(Default::default(), window, cx, |s| {
14608            s.move_heads_with(&mut |map, head, _| {
14609                (
14610                    movement::previous_subword_start(map, head),
14611                    SelectionGoal::None,
14612                )
14613            });
14614        })
14615    }
14616
14617    pub fn delete_to_previous_word_start(
14618        &mut self,
14619        action: &DeleteToPreviousWordStart,
14620        window: &mut Window,
14621        cx: &mut Context<Self>,
14622    ) {
14623        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14624        self.transact(window, cx, |this, window, cx| {
14625            this.select_autoclose_pair(window, cx);
14626            this.change_selections(Default::default(), window, cx, |s| {
14627                s.move_with(&mut |map, selection| {
14628                    if selection.is_empty() {
14629                        let mut cursor = if action.ignore_newlines {
14630                            movement::previous_word_start(map, selection.head())
14631                        } else {
14632                            movement::previous_word_start_or_newline(map, selection.head())
14633                        };
14634                        cursor = movement::adjust_greedy_deletion(
14635                            map,
14636                            selection.head(),
14637                            cursor,
14638                            action.ignore_brackets,
14639                        );
14640                        selection.set_head(cursor, SelectionGoal::None);
14641                    }
14642                });
14643            });
14644            this.insert("", window, cx);
14645        });
14646    }
14647
14648    pub fn delete_to_previous_subword_start(
14649        &mut self,
14650        action: &DeleteToPreviousSubwordStart,
14651        window: &mut Window,
14652        cx: &mut Context<Self>,
14653    ) {
14654        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14655        self.transact(window, cx, |this, window, cx| {
14656            this.select_autoclose_pair(window, cx);
14657            this.change_selections(Default::default(), window, cx, |s| {
14658                s.move_with(&mut |map, selection| {
14659                    if selection.is_empty() {
14660                        let mut cursor = if action.ignore_newlines {
14661                            movement::previous_subword_start(map, selection.head())
14662                        } else {
14663                            movement::previous_subword_start_or_newline(map, selection.head())
14664                        };
14665                        cursor = movement::adjust_greedy_deletion(
14666                            map,
14667                            selection.head(),
14668                            cursor,
14669                            action.ignore_brackets,
14670                        );
14671                        selection.set_head(cursor, SelectionGoal::None);
14672                    }
14673                });
14674            });
14675            this.insert("", window, cx);
14676        });
14677    }
14678
14679    pub fn move_to_next_word_end(
14680        &mut self,
14681        _: &MoveToNextWordEnd,
14682        window: &mut Window,
14683        cx: &mut Context<Self>,
14684    ) {
14685        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14686        self.change_selections(Default::default(), window, cx, |s| {
14687            s.move_cursors_with(&mut |map, head, _| {
14688                (movement::next_word_end(map, head), SelectionGoal::None)
14689            });
14690        })
14691    }
14692
14693    pub fn move_to_next_subword_end(
14694        &mut self,
14695        _: &MoveToNextSubwordEnd,
14696        window: &mut Window,
14697        cx: &mut Context<Self>,
14698    ) {
14699        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14700        self.change_selections(Default::default(), window, cx, |s| {
14701            s.move_cursors_with(&mut |map, head, _| {
14702                (movement::next_subword_end(map, head), SelectionGoal::None)
14703            });
14704        })
14705    }
14706
14707    pub fn select_to_next_word_end(
14708        &mut self,
14709        _: &SelectToNextWordEnd,
14710        window: &mut Window,
14711        cx: &mut Context<Self>,
14712    ) {
14713        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14714        self.change_selections(Default::default(), window, cx, |s| {
14715            s.move_heads_with(&mut |map, head, _| {
14716                (movement::next_word_end(map, head), SelectionGoal::None)
14717            });
14718        })
14719    }
14720
14721    pub fn select_to_next_subword_end(
14722        &mut self,
14723        _: &SelectToNextSubwordEnd,
14724        window: &mut Window,
14725        cx: &mut Context<Self>,
14726    ) {
14727        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14728        self.change_selections(Default::default(), window, cx, |s| {
14729            s.move_heads_with(&mut |map, head, _| {
14730                (movement::next_subword_end(map, head), SelectionGoal::None)
14731            });
14732        })
14733    }
14734
14735    pub fn delete_to_next_word_end(
14736        &mut self,
14737        action: &DeleteToNextWordEnd,
14738        window: &mut Window,
14739        cx: &mut Context<Self>,
14740    ) {
14741        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14742        self.transact(window, cx, |this, window, cx| {
14743            this.change_selections(Default::default(), window, cx, |s| {
14744                s.move_with(&mut |map, selection| {
14745                    if selection.is_empty() {
14746                        let mut cursor = if action.ignore_newlines {
14747                            movement::next_word_end(map, selection.head())
14748                        } else {
14749                            movement::next_word_end_or_newline(map, selection.head())
14750                        };
14751                        cursor = movement::adjust_greedy_deletion(
14752                            map,
14753                            selection.head(),
14754                            cursor,
14755                            action.ignore_brackets,
14756                        );
14757                        selection.set_head(cursor, SelectionGoal::None);
14758                    }
14759                });
14760            });
14761            this.insert("", window, cx);
14762        });
14763    }
14764
14765    pub fn delete_to_next_subword_end(
14766        &mut self,
14767        action: &DeleteToNextSubwordEnd,
14768        window: &mut Window,
14769        cx: &mut Context<Self>,
14770    ) {
14771        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14772        self.transact(window, cx, |this, window, cx| {
14773            this.change_selections(Default::default(), window, cx, |s| {
14774                s.move_with(&mut |map, selection| {
14775                    if selection.is_empty() {
14776                        let mut cursor = if action.ignore_newlines {
14777                            movement::next_subword_end(map, selection.head())
14778                        } else {
14779                            movement::next_subword_end_or_newline(map, selection.head())
14780                        };
14781                        cursor = movement::adjust_greedy_deletion(
14782                            map,
14783                            selection.head(),
14784                            cursor,
14785                            action.ignore_brackets,
14786                        );
14787                        selection.set_head(cursor, SelectionGoal::None);
14788                    }
14789                });
14790            });
14791            this.insert("", window, cx);
14792        });
14793    }
14794
14795    pub fn move_to_beginning_of_line(
14796        &mut self,
14797        action: &MoveToBeginningOfLine,
14798        window: &mut Window,
14799        cx: &mut Context<Self>,
14800    ) {
14801        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14802        self.change_selections(Default::default(), window, cx, |s| {
14803            s.move_cursors_with(&mut |map, head, _| {
14804                (
14805                    movement::indented_line_beginning(
14806                        map,
14807                        head,
14808                        action.stop_at_soft_wraps,
14809                        action.stop_at_indent,
14810                    ),
14811                    SelectionGoal::None,
14812                )
14813            });
14814        })
14815    }
14816
14817    pub fn select_to_beginning_of_line(
14818        &mut self,
14819        action: &SelectToBeginningOfLine,
14820        window: &mut Window,
14821        cx: &mut Context<Self>,
14822    ) {
14823        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14824        self.change_selections(Default::default(), window, cx, |s| {
14825            s.move_heads_with(&mut |map, head, _| {
14826                (
14827                    movement::indented_line_beginning(
14828                        map,
14829                        head,
14830                        action.stop_at_soft_wraps,
14831                        action.stop_at_indent,
14832                    ),
14833                    SelectionGoal::None,
14834                )
14835            });
14836        });
14837    }
14838
14839    pub fn delete_to_beginning_of_line(
14840        &mut self,
14841        action: &DeleteToBeginningOfLine,
14842        window: &mut Window,
14843        cx: &mut Context<Self>,
14844    ) {
14845        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14846        self.transact(window, cx, |this, window, cx| {
14847            this.change_selections(Default::default(), window, cx, |s| {
14848                s.move_with(&mut |_, selection| {
14849                    selection.reversed = true;
14850                });
14851            });
14852
14853            this.select_to_beginning_of_line(
14854                &SelectToBeginningOfLine {
14855                    stop_at_soft_wraps: false,
14856                    stop_at_indent: action.stop_at_indent,
14857                },
14858                window,
14859                cx,
14860            );
14861            this.backspace(&Backspace, window, cx);
14862        });
14863    }
14864
14865    pub fn move_to_end_of_line(
14866        &mut self,
14867        action: &MoveToEndOfLine,
14868        window: &mut Window,
14869        cx: &mut Context<Self>,
14870    ) {
14871        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14872        self.change_selections(Default::default(), window, cx, |s| {
14873            s.move_cursors_with(&mut |map, head, _| {
14874                (
14875                    movement::line_end(map, head, action.stop_at_soft_wraps),
14876                    SelectionGoal::None,
14877                )
14878            });
14879        })
14880    }
14881
14882    pub fn select_to_end_of_line(
14883        &mut self,
14884        action: &SelectToEndOfLine,
14885        window: &mut Window,
14886        cx: &mut Context<Self>,
14887    ) {
14888        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14889        self.change_selections(Default::default(), window, cx, |s| {
14890            s.move_heads_with(&mut |map, head, _| {
14891                (
14892                    movement::line_end(map, head, action.stop_at_soft_wraps),
14893                    SelectionGoal::None,
14894                )
14895            });
14896        })
14897    }
14898
14899    pub fn delete_to_end_of_line(
14900        &mut self,
14901        _: &DeleteToEndOfLine,
14902        window: &mut Window,
14903        cx: &mut Context<Self>,
14904    ) {
14905        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14906        self.transact(window, cx, |this, window, cx| {
14907            this.select_to_end_of_line(
14908                &SelectToEndOfLine {
14909                    stop_at_soft_wraps: false,
14910                },
14911                window,
14912                cx,
14913            );
14914            this.delete(&Delete, window, cx);
14915        });
14916    }
14917
14918    pub fn cut_to_end_of_line(
14919        &mut self,
14920        action: &CutToEndOfLine,
14921        window: &mut Window,
14922        cx: &mut Context<Self>,
14923    ) {
14924        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14925        self.transact(window, cx, |this, window, cx| {
14926            this.select_to_end_of_line(
14927                &SelectToEndOfLine {
14928                    stop_at_soft_wraps: false,
14929                },
14930                window,
14931                cx,
14932            );
14933            if !action.stop_at_newlines {
14934                this.change_selections(Default::default(), window, cx, |s| {
14935                    s.move_with(&mut |_, sel| {
14936                        if sel.is_empty() {
14937                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14938                        }
14939                    });
14940                });
14941            }
14942            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14943            let item = this.cut_common(false, window, cx);
14944            cx.write_to_clipboard(item);
14945        });
14946    }
14947
14948    pub fn move_to_start_of_paragraph(
14949        &mut self,
14950        _: &MoveToStartOfParagraph,
14951        window: &mut Window,
14952        cx: &mut Context<Self>,
14953    ) {
14954        if matches!(self.mode, EditorMode::SingleLine) {
14955            cx.propagate();
14956            return;
14957        }
14958        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14959        self.change_selections(Default::default(), window, cx, |s| {
14960            s.move_with(&mut |map, selection| {
14961                selection.collapse_to(
14962                    movement::start_of_paragraph(map, selection.head(), 1),
14963                    SelectionGoal::None,
14964                )
14965            });
14966        })
14967    }
14968
14969    pub fn move_to_end_of_paragraph(
14970        &mut self,
14971        _: &MoveToEndOfParagraph,
14972        window: &mut Window,
14973        cx: &mut Context<Self>,
14974    ) {
14975        if matches!(self.mode, EditorMode::SingleLine) {
14976            cx.propagate();
14977            return;
14978        }
14979        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14980        self.change_selections(Default::default(), window, cx, |s| {
14981            s.move_with(&mut |map, selection| {
14982                selection.collapse_to(
14983                    movement::end_of_paragraph(map, selection.head(), 1),
14984                    SelectionGoal::None,
14985                )
14986            });
14987        })
14988    }
14989
14990    pub fn select_to_start_of_paragraph(
14991        &mut self,
14992        _: &SelectToStartOfParagraph,
14993        window: &mut Window,
14994        cx: &mut Context<Self>,
14995    ) {
14996        if matches!(self.mode, EditorMode::SingleLine) {
14997            cx.propagate();
14998            return;
14999        }
15000        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15001        self.change_selections(Default::default(), window, cx, |s| {
15002            s.move_heads_with(&mut |map, head, _| {
15003                (
15004                    movement::start_of_paragraph(map, head, 1),
15005                    SelectionGoal::None,
15006                )
15007            });
15008        })
15009    }
15010
15011    pub fn select_to_end_of_paragraph(
15012        &mut self,
15013        _: &SelectToEndOfParagraph,
15014        window: &mut Window,
15015        cx: &mut Context<Self>,
15016    ) {
15017        if matches!(self.mode, EditorMode::SingleLine) {
15018            cx.propagate();
15019            return;
15020        }
15021        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15022        self.change_selections(Default::default(), window, cx, |s| {
15023            s.move_heads_with(&mut |map, head, _| {
15024                (
15025                    movement::end_of_paragraph(map, head, 1),
15026                    SelectionGoal::None,
15027                )
15028            });
15029        })
15030    }
15031
15032    pub fn move_to_start_of_excerpt(
15033        &mut self,
15034        _: &MoveToStartOfExcerpt,
15035        window: &mut Window,
15036        cx: &mut Context<Self>,
15037    ) {
15038        if matches!(self.mode, EditorMode::SingleLine) {
15039            cx.propagate();
15040            return;
15041        }
15042        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15043        self.change_selections(Default::default(), window, cx, |s| {
15044            s.move_with(&mut |map, selection| {
15045                selection.collapse_to(
15046                    movement::start_of_excerpt(
15047                        map,
15048                        selection.head(),
15049                        workspace::searchable::Direction::Prev,
15050                    ),
15051                    SelectionGoal::None,
15052                )
15053            });
15054        })
15055    }
15056
15057    pub fn move_to_start_of_next_excerpt(
15058        &mut self,
15059        _: &MoveToStartOfNextExcerpt,
15060        window: &mut Window,
15061        cx: &mut Context<Self>,
15062    ) {
15063        if matches!(self.mode, EditorMode::SingleLine) {
15064            cx.propagate();
15065            return;
15066        }
15067
15068        self.change_selections(Default::default(), window, cx, |s| {
15069            s.move_with(&mut |map, selection| {
15070                selection.collapse_to(
15071                    movement::start_of_excerpt(
15072                        map,
15073                        selection.head(),
15074                        workspace::searchable::Direction::Next,
15075                    ),
15076                    SelectionGoal::None,
15077                )
15078            });
15079        })
15080    }
15081
15082    pub fn move_to_end_of_excerpt(
15083        &mut self,
15084        _: &MoveToEndOfExcerpt,
15085        window: &mut Window,
15086        cx: &mut Context<Self>,
15087    ) {
15088        if matches!(self.mode, EditorMode::SingleLine) {
15089            cx.propagate();
15090            return;
15091        }
15092        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15093        self.change_selections(Default::default(), window, cx, |s| {
15094            s.move_with(&mut |map, selection| {
15095                selection.collapse_to(
15096                    movement::end_of_excerpt(
15097                        map,
15098                        selection.head(),
15099                        workspace::searchable::Direction::Next,
15100                    ),
15101                    SelectionGoal::None,
15102                )
15103            });
15104        })
15105    }
15106
15107    pub fn move_to_end_of_previous_excerpt(
15108        &mut self,
15109        _: &MoveToEndOfPreviousExcerpt,
15110        window: &mut Window,
15111        cx: &mut Context<Self>,
15112    ) {
15113        if matches!(self.mode, EditorMode::SingleLine) {
15114            cx.propagate();
15115            return;
15116        }
15117        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15118        self.change_selections(Default::default(), window, cx, |s| {
15119            s.move_with(&mut |map, selection| {
15120                selection.collapse_to(
15121                    movement::end_of_excerpt(
15122                        map,
15123                        selection.head(),
15124                        workspace::searchable::Direction::Prev,
15125                    ),
15126                    SelectionGoal::None,
15127                )
15128            });
15129        })
15130    }
15131
15132    pub fn select_to_start_of_excerpt(
15133        &mut self,
15134        _: &SelectToStartOfExcerpt,
15135        window: &mut Window,
15136        cx: &mut Context<Self>,
15137    ) {
15138        if matches!(self.mode, EditorMode::SingleLine) {
15139            cx.propagate();
15140            return;
15141        }
15142        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15143        self.change_selections(Default::default(), window, cx, |s| {
15144            s.move_heads_with(&mut |map, head, _| {
15145                (
15146                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15147                    SelectionGoal::None,
15148                )
15149            });
15150        })
15151    }
15152
15153    pub fn select_to_start_of_next_excerpt(
15154        &mut self,
15155        _: &SelectToStartOfNextExcerpt,
15156        window: &mut Window,
15157        cx: &mut Context<Self>,
15158    ) {
15159        if matches!(self.mode, EditorMode::SingleLine) {
15160            cx.propagate();
15161            return;
15162        }
15163        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15164        self.change_selections(Default::default(), window, cx, |s| {
15165            s.move_heads_with(&mut |map, head, _| {
15166                (
15167                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15168                    SelectionGoal::None,
15169                )
15170            });
15171        })
15172    }
15173
15174    pub fn select_to_end_of_excerpt(
15175        &mut self,
15176        _: &SelectToEndOfExcerpt,
15177        window: &mut Window,
15178        cx: &mut Context<Self>,
15179    ) {
15180        if matches!(self.mode, EditorMode::SingleLine) {
15181            cx.propagate();
15182            return;
15183        }
15184        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15185        self.change_selections(Default::default(), window, cx, |s| {
15186            s.move_heads_with(&mut |map, head, _| {
15187                (
15188                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15189                    SelectionGoal::None,
15190                )
15191            });
15192        })
15193    }
15194
15195    pub fn select_to_end_of_previous_excerpt(
15196        &mut self,
15197        _: &SelectToEndOfPreviousExcerpt,
15198        window: &mut Window,
15199        cx: &mut Context<Self>,
15200    ) {
15201        if matches!(self.mode, EditorMode::SingleLine) {
15202            cx.propagate();
15203            return;
15204        }
15205        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15206        self.change_selections(Default::default(), window, cx, |s| {
15207            s.move_heads_with(&mut |map, head, _| {
15208                (
15209                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15210                    SelectionGoal::None,
15211                )
15212            });
15213        })
15214    }
15215
15216    pub fn move_to_beginning(
15217        &mut self,
15218        _: &MoveToBeginning,
15219        window: &mut Window,
15220        cx: &mut Context<Self>,
15221    ) {
15222        if matches!(self.mode, EditorMode::SingleLine) {
15223            cx.propagate();
15224            return;
15225        }
15226        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15227        self.change_selections(Default::default(), window, cx, |s| {
15228            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15229        });
15230    }
15231
15232    pub fn select_to_beginning(
15233        &mut self,
15234        _: &SelectToBeginning,
15235        window: &mut Window,
15236        cx: &mut Context<Self>,
15237    ) {
15238        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15239        selection.set_head(Point::zero(), SelectionGoal::None);
15240        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15241        self.change_selections(Default::default(), window, cx, |s| {
15242            s.select(vec![selection]);
15243        });
15244    }
15245
15246    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15247        if matches!(self.mode, EditorMode::SingleLine) {
15248            cx.propagate();
15249            return;
15250        }
15251        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15252        let cursor = self.buffer.read(cx).read(cx).len();
15253        self.change_selections(Default::default(), window, cx, |s| {
15254            s.select_ranges(vec![cursor..cursor])
15255        });
15256    }
15257
15258    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15259        self.nav_history = nav_history;
15260    }
15261
15262    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15263        self.nav_history.as_ref()
15264    }
15265
15266    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15267        self.push_to_nav_history(
15268            self.selections.newest_anchor().head(),
15269            None,
15270            false,
15271            true,
15272            cx,
15273        );
15274    }
15275
15276    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15277        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15278        let buffer = self.buffer.read(cx).read(cx);
15279        let cursor_position = cursor_anchor.to_point(&buffer);
15280        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15281        let scroll_top_row = scroll_anchor.top_row(&buffer);
15282        drop(buffer);
15283
15284        NavigationData {
15285            cursor_anchor,
15286            cursor_position,
15287            scroll_anchor,
15288            scroll_top_row,
15289        }
15290    }
15291
15292    fn navigation_entry(
15293        &self,
15294        cursor_anchor: Anchor,
15295        cx: &mut Context<Self>,
15296    ) -> Option<NavigationEntry> {
15297        let Some(history) = self.nav_history.clone() else {
15298            return None;
15299        };
15300        let data = self.navigation_data(cursor_anchor, cx);
15301        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15302    }
15303
15304    fn push_to_nav_history(
15305        &mut self,
15306        cursor_anchor: Anchor,
15307        new_position: Option<Point>,
15308        is_deactivate: bool,
15309        always: bool,
15310        cx: &mut Context<Self>,
15311    ) {
15312        let data = self.navigation_data(cursor_anchor, cx);
15313        if let Some(nav_history) = self.nav_history.as_mut() {
15314            if let Some(new_position) = new_position {
15315                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15316                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15317                    return;
15318                }
15319            }
15320
15321            nav_history.push(Some(data), cx);
15322            cx.emit(EditorEvent::PushedToNavHistory {
15323                anchor: cursor_anchor,
15324                is_deactivate,
15325            })
15326        }
15327    }
15328
15329    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15330        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15331        let buffer = self.buffer.read(cx).snapshot(cx);
15332        let mut selection = self
15333            .selections
15334            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15335        selection.set_head(buffer.len(), SelectionGoal::None);
15336        self.change_selections(Default::default(), window, cx, |s| {
15337            s.select(vec![selection]);
15338        });
15339    }
15340
15341    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15342        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15343        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15344            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15345        });
15346    }
15347
15348    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15349        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15350        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15351        let mut selections = self.selections.all::<Point>(&display_map);
15352        let max_point = display_map.buffer_snapshot().max_point();
15353        for selection in &mut selections {
15354            let rows = selection.spanned_rows(true, &display_map);
15355            selection.start = Point::new(rows.start.0, 0);
15356            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15357            selection.reversed = false;
15358        }
15359        self.change_selections(Default::default(), window, cx, |s| {
15360            s.select(selections);
15361        });
15362    }
15363
15364    pub fn split_selection_into_lines(
15365        &mut self,
15366        action: &SplitSelectionIntoLines,
15367        window: &mut Window,
15368        cx: &mut Context<Self>,
15369    ) {
15370        let selections = self
15371            .selections
15372            .all::<Point>(&self.display_snapshot(cx))
15373            .into_iter()
15374            .map(|selection| selection.start..selection.end)
15375            .collect::<Vec<_>>();
15376        self.unfold_ranges(&selections, true, true, cx);
15377
15378        let mut new_selection_ranges = Vec::new();
15379        {
15380            let buffer = self.buffer.read(cx).read(cx);
15381            for selection in selections {
15382                for row in selection.start.row..selection.end.row {
15383                    let line_start = Point::new(row, 0);
15384                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15385
15386                    if action.keep_selections {
15387                        // Keep the selection range for each line
15388                        let selection_start = if row == selection.start.row {
15389                            selection.start
15390                        } else {
15391                            line_start
15392                        };
15393                        new_selection_ranges.push(selection_start..line_end);
15394                    } else {
15395                        // Collapse to cursor at end of line
15396                        new_selection_ranges.push(line_end..line_end);
15397                    }
15398                }
15399
15400                let is_multiline_selection = selection.start.row != selection.end.row;
15401                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15402                // so this action feels more ergonomic when paired with other selection operations
15403                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15404                if !should_skip_last {
15405                    if action.keep_selections {
15406                        if is_multiline_selection {
15407                            let line_start = Point::new(selection.end.row, 0);
15408                            new_selection_ranges.push(line_start..selection.end);
15409                        } else {
15410                            new_selection_ranges.push(selection.start..selection.end);
15411                        }
15412                    } else {
15413                        new_selection_ranges.push(selection.end..selection.end);
15414                    }
15415                }
15416            }
15417        }
15418        self.change_selections(Default::default(), window, cx, |s| {
15419            s.select_ranges(new_selection_ranges);
15420        });
15421    }
15422
15423    pub fn add_selection_above(
15424        &mut self,
15425        action: &AddSelectionAbove,
15426        window: &mut Window,
15427        cx: &mut Context<Self>,
15428    ) {
15429        self.add_selection(true, action.skip_soft_wrap, window, cx);
15430    }
15431
15432    pub fn add_selection_below(
15433        &mut self,
15434        action: &AddSelectionBelow,
15435        window: &mut Window,
15436        cx: &mut Context<Self>,
15437    ) {
15438        self.add_selection(false, action.skip_soft_wrap, window, cx);
15439    }
15440
15441    fn add_selection(
15442        &mut self,
15443        above: bool,
15444        skip_soft_wrap: bool,
15445        window: &mut Window,
15446        cx: &mut Context<Self>,
15447    ) {
15448        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15449
15450        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15451        let all_selections = self.selections.all::<Point>(&display_map);
15452        let text_layout_details = self.text_layout_details(window, cx);
15453
15454        let (mut columnar_selections, new_selections_to_columnarize) = {
15455            if let Some(state) = self.add_selections_state.as_ref() {
15456                let columnar_selection_ids: HashSet<_> = state
15457                    .groups
15458                    .iter()
15459                    .flat_map(|group| group.stack.iter())
15460                    .copied()
15461                    .collect();
15462
15463                all_selections
15464                    .into_iter()
15465                    .partition(|s| columnar_selection_ids.contains(&s.id))
15466            } else {
15467                (Vec::new(), all_selections)
15468            }
15469        };
15470
15471        let mut state = self
15472            .add_selections_state
15473            .take()
15474            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15475
15476        for selection in new_selections_to_columnarize {
15477            let range = selection.display_range(&display_map).sorted();
15478            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15479            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15480            let positions = start_x.min(end_x)..start_x.max(end_x);
15481            let mut stack = Vec::new();
15482            for row in range.start.row().0..=range.end.row().0 {
15483                if let Some(selection) = self.selections.build_columnar_selection(
15484                    &display_map,
15485                    DisplayRow(row),
15486                    &positions,
15487                    selection.reversed,
15488                    &text_layout_details,
15489                ) {
15490                    stack.push(selection.id);
15491                    columnar_selections.push(selection);
15492                }
15493            }
15494            if !stack.is_empty() {
15495                if above {
15496                    stack.reverse();
15497                }
15498                state.groups.push(AddSelectionsGroup { above, stack });
15499            }
15500        }
15501
15502        let mut final_selections = Vec::new();
15503        let end_row = if above {
15504            DisplayRow(0)
15505        } else {
15506            display_map.max_point().row()
15507        };
15508
15509        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15510        // positions to place new selections, so we need to keep track of the
15511        // column range of the oldest selection in each group, because
15512        // intermediate selections may have been clamped to shorter lines.
15513        // selections may have been clamped to shorter lines.
15514        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15515            let mut map = HashMap::default();
15516            for group in state.groups.iter() {
15517                if let Some(oldest_id) = group.stack.first() {
15518                    if let Some(oldest_selection) =
15519                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15520                    {
15521                        let start_col = oldest_selection.start.column;
15522                        let end_col = oldest_selection.end.column;
15523                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15524                        for id in &group.stack {
15525                            map.insert(*id, goal_columns.clone());
15526                        }
15527                    }
15528                }
15529            }
15530            map
15531        } else {
15532            HashMap::default()
15533        };
15534
15535        let mut last_added_item_per_group = HashMap::default();
15536        for group in state.groups.iter_mut() {
15537            if let Some(last_id) = group.stack.last() {
15538                last_added_item_per_group.insert(*last_id, group);
15539            }
15540        }
15541
15542        for selection in columnar_selections {
15543            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15544                if above == group.above {
15545                    let range = selection.display_range(&display_map).sorted();
15546                    debug_assert_eq!(range.start.row(), range.end.row());
15547                    let row = range.start.row();
15548                    let positions =
15549                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15550                            Pixels::from(start)..Pixels::from(end)
15551                        } else {
15552                            let start_x =
15553                                display_map.x_for_display_point(range.start, &text_layout_details);
15554                            let end_x =
15555                                display_map.x_for_display_point(range.end, &text_layout_details);
15556                            start_x.min(end_x)..start_x.max(end_x)
15557                        };
15558
15559                    let maybe_new_selection = if skip_soft_wrap {
15560                        let goal_columns = goal_columns_by_selection_id
15561                            .remove(&selection.id)
15562                            .unwrap_or_else(|| {
15563                                let start_col = selection.start.column;
15564                                let end_col = selection.end.column;
15565                                start_col.min(end_col)..start_col.max(end_col)
15566                            });
15567                        self.selections.find_next_columnar_selection_by_buffer_row(
15568                            &display_map,
15569                            row,
15570                            end_row,
15571                            above,
15572                            &goal_columns,
15573                            selection.reversed,
15574                            &text_layout_details,
15575                        )
15576                    } else {
15577                        self.selections.find_next_columnar_selection_by_display_row(
15578                            &display_map,
15579                            row,
15580                            end_row,
15581                            above,
15582                            &positions,
15583                            selection.reversed,
15584                            &text_layout_details,
15585                        )
15586                    };
15587
15588                    if let Some(new_selection) = maybe_new_selection {
15589                        group.stack.push(new_selection.id);
15590                        if above {
15591                            final_selections.push(new_selection);
15592                            final_selections.push(selection);
15593                        } else {
15594                            final_selections.push(selection);
15595                            final_selections.push(new_selection);
15596                        }
15597                    } else {
15598                        final_selections.push(selection);
15599                    }
15600                } else {
15601                    group.stack.pop();
15602                }
15603            } else {
15604                final_selections.push(selection);
15605            }
15606        }
15607
15608        self.change_selections(Default::default(), window, cx, |s| {
15609            s.select(final_selections);
15610        });
15611
15612        let final_selection_ids: HashSet<_> = self
15613            .selections
15614            .all::<Point>(&display_map)
15615            .iter()
15616            .map(|s| s.id)
15617            .collect();
15618        state.groups.retain_mut(|group| {
15619            // selections might get merged above so we remove invalid items from stacks
15620            group.stack.retain(|id| final_selection_ids.contains(id));
15621
15622            // single selection in stack can be treated as initial state
15623            group.stack.len() > 1
15624        });
15625
15626        if !state.groups.is_empty() {
15627            self.add_selections_state = Some(state);
15628        }
15629    }
15630
15631    pub fn insert_snippet_at_selections(
15632        &mut self,
15633        action: &InsertSnippet,
15634        window: &mut Window,
15635        cx: &mut Context<Self>,
15636    ) {
15637        self.try_insert_snippet_at_selections(action, window, cx)
15638            .log_err();
15639    }
15640
15641    fn try_insert_snippet_at_selections(
15642        &mut self,
15643        action: &InsertSnippet,
15644        window: &mut Window,
15645        cx: &mut Context<Self>,
15646    ) -> Result<()> {
15647        let insertion_ranges = self
15648            .selections
15649            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15650            .into_iter()
15651            .map(|selection| selection.range())
15652            .collect_vec();
15653
15654        let snippet = if let Some(snippet_body) = &action.snippet {
15655            if action.language.is_none() && action.name.is_none() {
15656                Snippet::parse(snippet_body)?
15657            } else {
15658                bail!("`snippet` is mutually exclusive with `language` and `name`")
15659            }
15660        } else if let Some(name) = &action.name {
15661            let project = self.project().context("no project")?;
15662            let snippet_store = project.read(cx).snippets().read(cx);
15663            let snippet = snippet_store
15664                .snippets_for(action.language.clone(), cx)
15665                .into_iter()
15666                .find(|snippet| snippet.name == *name)
15667                .context("snippet not found")?;
15668            Snippet::parse(&snippet.body)?
15669        } else {
15670            // todo(andrew): open modal to select snippet
15671            bail!("`name` or `snippet` is required")
15672        };
15673
15674        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15675    }
15676
15677    fn select_match_ranges(
15678        &mut self,
15679        range: Range<MultiBufferOffset>,
15680        reversed: bool,
15681        replace_newest: bool,
15682        auto_scroll: Option<Autoscroll>,
15683        window: &mut Window,
15684        cx: &mut Context<Editor>,
15685    ) {
15686        self.unfold_ranges(
15687            std::slice::from_ref(&range),
15688            false,
15689            auto_scroll.is_some(),
15690            cx,
15691        );
15692        let effects = if let Some(scroll) = auto_scroll {
15693            SelectionEffects::scroll(scroll)
15694        } else {
15695            SelectionEffects::no_scroll()
15696        };
15697        self.change_selections(effects, window, cx, |s| {
15698            if replace_newest {
15699                s.delete(s.newest_anchor().id);
15700            }
15701            if reversed {
15702                s.insert_range(range.end..range.start);
15703            } else {
15704                s.insert_range(range);
15705            }
15706        });
15707    }
15708
15709    pub fn select_next_match_internal(
15710        &mut self,
15711        display_map: &DisplaySnapshot,
15712        replace_newest: bool,
15713        autoscroll: Option<Autoscroll>,
15714        window: &mut Window,
15715        cx: &mut Context<Self>,
15716    ) -> Result<()> {
15717        let buffer = display_map.buffer_snapshot();
15718        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15719        if let Some(mut select_next_state) = self.select_next_state.take() {
15720            let query = &select_next_state.query;
15721            if !select_next_state.done {
15722                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15723                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15724                let mut next_selected_range = None;
15725
15726                let bytes_after_last_selection =
15727                    buffer.bytes_in_range(last_selection.end..buffer.len());
15728                let bytes_before_first_selection =
15729                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15730                let query_matches = query
15731                    .stream_find_iter(bytes_after_last_selection)
15732                    .map(|result| (last_selection.end, result))
15733                    .chain(
15734                        query
15735                            .stream_find_iter(bytes_before_first_selection)
15736                            .map(|result| (MultiBufferOffset(0), result)),
15737                    );
15738
15739                for (start_offset, query_match) in query_matches {
15740                    let query_match = query_match.unwrap(); // can only fail due to I/O
15741                    let offset_range =
15742                        start_offset + query_match.start()..start_offset + query_match.end();
15743
15744                    if !select_next_state.wordwise
15745                        || (!buffer.is_inside_word(offset_range.start, None)
15746                            && !buffer.is_inside_word(offset_range.end, None))
15747                    {
15748                        let idx = selections
15749                            .partition_point(|selection| selection.end <= offset_range.start);
15750                        let overlaps = selections
15751                            .get(idx)
15752                            .map_or(false, |selection| selection.start < offset_range.end);
15753
15754                        if !overlaps {
15755                            next_selected_range = Some(offset_range);
15756                            break;
15757                        }
15758                    }
15759                }
15760
15761                if let Some(next_selected_range) = next_selected_range {
15762                    self.select_match_ranges(
15763                        next_selected_range,
15764                        last_selection.reversed,
15765                        replace_newest,
15766                        autoscroll,
15767                        window,
15768                        cx,
15769                    );
15770                } else {
15771                    select_next_state.done = true;
15772                }
15773            }
15774
15775            self.select_next_state = Some(select_next_state);
15776        } else {
15777            let mut only_carets = true;
15778            let mut same_text_selected = true;
15779            let mut selected_text = None;
15780
15781            let mut selections_iter = selections.iter().peekable();
15782            while let Some(selection) = selections_iter.next() {
15783                if selection.start != selection.end {
15784                    only_carets = false;
15785                }
15786
15787                if same_text_selected {
15788                    if selected_text.is_none() {
15789                        selected_text =
15790                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15791                    }
15792
15793                    if let Some(next_selection) = selections_iter.peek() {
15794                        if next_selection.len() == selection.len() {
15795                            let next_selected_text = buffer
15796                                .text_for_range(next_selection.range())
15797                                .collect::<String>();
15798                            if Some(next_selected_text) != selected_text {
15799                                same_text_selected = false;
15800                                selected_text = None;
15801                            }
15802                        } else {
15803                            same_text_selected = false;
15804                            selected_text = None;
15805                        }
15806                    }
15807                }
15808            }
15809
15810            if only_carets {
15811                for selection in &mut selections {
15812                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15813                    selection.start = word_range.start;
15814                    selection.end = word_range.end;
15815                    selection.goal = SelectionGoal::None;
15816                    selection.reversed = false;
15817                    self.select_match_ranges(
15818                        selection.start..selection.end,
15819                        selection.reversed,
15820                        replace_newest,
15821                        autoscroll,
15822                        window,
15823                        cx,
15824                    );
15825                }
15826
15827                if selections.len() == 1 {
15828                    let selection = selections
15829                        .last()
15830                        .expect("ensured that there's only one selection");
15831                    let query = buffer
15832                        .text_for_range(selection.start..selection.end)
15833                        .collect::<String>();
15834                    let is_empty = query.is_empty();
15835                    let select_state = SelectNextState {
15836                        query: self.build_query(&[query], cx)?,
15837                        wordwise: true,
15838                        done: is_empty,
15839                    };
15840                    self.select_next_state = Some(select_state);
15841                } else {
15842                    self.select_next_state = None;
15843                }
15844            } else if let Some(selected_text) = selected_text {
15845                self.select_next_state = Some(SelectNextState {
15846                    query: self.build_query(&[selected_text], cx)?,
15847                    wordwise: false,
15848                    done: false,
15849                });
15850                self.select_next_match_internal(
15851                    display_map,
15852                    replace_newest,
15853                    autoscroll,
15854                    window,
15855                    cx,
15856                )?;
15857            }
15858        }
15859        Ok(())
15860    }
15861
15862    pub fn select_all_matches(
15863        &mut self,
15864        _action: &SelectAllMatches,
15865        window: &mut Window,
15866        cx: &mut Context<Self>,
15867    ) -> Result<()> {
15868        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15869
15870        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15871
15872        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15873        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15874        else {
15875            return Ok(());
15876        };
15877
15878        let mut new_selections = Vec::new();
15879
15880        let reversed = self
15881            .selections
15882            .oldest::<MultiBufferOffset>(&display_map)
15883            .reversed;
15884        let buffer = display_map.buffer_snapshot();
15885        let query_matches = select_next_state
15886            .query
15887            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15888
15889        for query_match in query_matches.into_iter() {
15890            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15891            let offset_range = if reversed {
15892                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15893            } else {
15894                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15895            };
15896
15897            if !select_next_state.wordwise
15898                || (!buffer.is_inside_word(offset_range.start, None)
15899                    && !buffer.is_inside_word(offset_range.end, None))
15900            {
15901                new_selections.push(offset_range.start..offset_range.end);
15902            }
15903        }
15904
15905        select_next_state.done = true;
15906
15907        if new_selections.is_empty() {
15908            log::error!("bug: new_selections is empty in select_all_matches");
15909            return Ok(());
15910        }
15911
15912        self.unfold_ranges(&new_selections, false, false, cx);
15913        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15914            selections.select_ranges(new_selections)
15915        });
15916
15917        Ok(())
15918    }
15919
15920    pub fn select_next(
15921        &mut self,
15922        action: &SelectNext,
15923        window: &mut Window,
15924        cx: &mut Context<Self>,
15925    ) -> Result<()> {
15926        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15927        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15928        self.select_next_match_internal(
15929            &display_map,
15930            action.replace_newest,
15931            Some(Autoscroll::newest()),
15932            window,
15933            cx,
15934        )
15935    }
15936
15937    pub fn select_previous(
15938        &mut self,
15939        action: &SelectPrevious,
15940        window: &mut Window,
15941        cx: &mut Context<Self>,
15942    ) -> Result<()> {
15943        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15944        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15945        let buffer = display_map.buffer_snapshot();
15946        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15947        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15948            let query = &select_prev_state.query;
15949            if !select_prev_state.done {
15950                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15951                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15952                let mut next_selected_range = None;
15953                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15954                let bytes_before_last_selection =
15955                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15956                let bytes_after_first_selection =
15957                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15958                let query_matches = query
15959                    .stream_find_iter(bytes_before_last_selection)
15960                    .map(|result| (last_selection.start, result))
15961                    .chain(
15962                        query
15963                            .stream_find_iter(bytes_after_first_selection)
15964                            .map(|result| (buffer.len(), result)),
15965                    );
15966                for (end_offset, query_match) in query_matches {
15967                    let query_match = query_match.unwrap(); // can only fail due to I/O
15968                    let offset_range =
15969                        end_offset - query_match.end()..end_offset - query_match.start();
15970
15971                    if !select_prev_state.wordwise
15972                        || (!buffer.is_inside_word(offset_range.start, None)
15973                            && !buffer.is_inside_word(offset_range.end, None))
15974                    {
15975                        next_selected_range = Some(offset_range);
15976                        break;
15977                    }
15978                }
15979
15980                if let Some(next_selected_range) = next_selected_range {
15981                    self.select_match_ranges(
15982                        next_selected_range,
15983                        last_selection.reversed,
15984                        action.replace_newest,
15985                        Some(Autoscroll::newest()),
15986                        window,
15987                        cx,
15988                    );
15989                } else {
15990                    select_prev_state.done = true;
15991                }
15992            }
15993
15994            self.select_prev_state = Some(select_prev_state);
15995        } else {
15996            let mut only_carets = true;
15997            let mut same_text_selected = true;
15998            let mut selected_text = None;
15999
16000            let mut selections_iter = selections.iter().peekable();
16001            while let Some(selection) = selections_iter.next() {
16002                if selection.start != selection.end {
16003                    only_carets = false;
16004                }
16005
16006                if same_text_selected {
16007                    if selected_text.is_none() {
16008                        selected_text =
16009                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16010                    }
16011
16012                    if let Some(next_selection) = selections_iter.peek() {
16013                        if next_selection.len() == selection.len() {
16014                            let next_selected_text = buffer
16015                                .text_for_range(next_selection.range())
16016                                .collect::<String>();
16017                            if Some(next_selected_text) != selected_text {
16018                                same_text_selected = false;
16019                                selected_text = None;
16020                            }
16021                        } else {
16022                            same_text_selected = false;
16023                            selected_text = None;
16024                        }
16025                    }
16026                }
16027            }
16028
16029            if only_carets {
16030                for selection in &mut selections {
16031                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16032                    selection.start = word_range.start;
16033                    selection.end = word_range.end;
16034                    selection.goal = SelectionGoal::None;
16035                    selection.reversed = false;
16036                    self.select_match_ranges(
16037                        selection.start..selection.end,
16038                        selection.reversed,
16039                        action.replace_newest,
16040                        Some(Autoscroll::newest()),
16041                        window,
16042                        cx,
16043                    );
16044                }
16045                if selections.len() == 1 {
16046                    let selection = selections
16047                        .last()
16048                        .expect("ensured that there's only one selection");
16049                    let query = buffer
16050                        .text_for_range(selection.start..selection.end)
16051                        .collect::<String>();
16052                    let is_empty = query.is_empty();
16053                    let select_state = SelectNextState {
16054                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16055                        wordwise: true,
16056                        done: is_empty,
16057                    };
16058                    self.select_prev_state = Some(select_state);
16059                } else {
16060                    self.select_prev_state = None;
16061                }
16062            } else if let Some(selected_text) = selected_text {
16063                self.select_prev_state = Some(SelectNextState {
16064                    query: self
16065                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16066                    wordwise: false,
16067                    done: false,
16068                });
16069                self.select_previous(action, window, cx)?;
16070            }
16071        }
16072        Ok(())
16073    }
16074
16075    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16076    /// setting the case sensitivity based on the global
16077    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16078    /// editor's settings.
16079    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16080    where
16081        I: IntoIterator<Item = P>,
16082        P: AsRef<[u8]>,
16083    {
16084        let case_sensitive = self
16085            .select_next_is_case_sensitive
16086            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16087
16088        let mut builder = AhoCorasickBuilder::new();
16089        builder.ascii_case_insensitive(!case_sensitive);
16090        builder.build(patterns)
16091    }
16092
16093    pub fn find_next_match(
16094        &mut self,
16095        _: &FindNextMatch,
16096        window: &mut Window,
16097        cx: &mut Context<Self>,
16098    ) -> Result<()> {
16099        let selections = self.selections.disjoint_anchors_arc();
16100        match selections.first() {
16101            Some(first) if selections.len() >= 2 => {
16102                self.change_selections(Default::default(), window, cx, |s| {
16103                    s.select_ranges([first.range()]);
16104                });
16105            }
16106            _ => self.select_next(
16107                &SelectNext {
16108                    replace_newest: true,
16109                },
16110                window,
16111                cx,
16112            )?,
16113        }
16114        Ok(())
16115    }
16116
16117    pub fn find_previous_match(
16118        &mut self,
16119        _: &FindPreviousMatch,
16120        window: &mut Window,
16121        cx: &mut Context<Self>,
16122    ) -> Result<()> {
16123        let selections = self.selections.disjoint_anchors_arc();
16124        match selections.last() {
16125            Some(last) if selections.len() >= 2 => {
16126                self.change_selections(Default::default(), window, cx, |s| {
16127                    s.select_ranges([last.range()]);
16128                });
16129            }
16130            _ => self.select_previous(
16131                &SelectPrevious {
16132                    replace_newest: true,
16133                },
16134                window,
16135                cx,
16136            )?,
16137        }
16138        Ok(())
16139    }
16140
16141    pub fn toggle_comments(
16142        &mut self,
16143        action: &ToggleComments,
16144        window: &mut Window,
16145        cx: &mut Context<Self>,
16146    ) {
16147        if self.read_only(cx) {
16148            return;
16149        }
16150        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16151        let text_layout_details = &self.text_layout_details(window, cx);
16152        self.transact(window, cx, |this, window, cx| {
16153            let mut selections = this
16154                .selections
16155                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16156            let mut edits = Vec::new();
16157            let mut selection_edit_ranges = Vec::new();
16158            let mut last_toggled_row = None;
16159            let snapshot = this.buffer.read(cx).read(cx);
16160            let empty_str: Arc<str> = Arc::default();
16161            let mut suffixes_inserted = Vec::new();
16162            let ignore_indent = action.ignore_indent;
16163
16164            fn comment_prefix_range(
16165                snapshot: &MultiBufferSnapshot,
16166                row: MultiBufferRow,
16167                comment_prefix: &str,
16168                comment_prefix_whitespace: &str,
16169                ignore_indent: bool,
16170            ) -> Range<Point> {
16171                let indent_size = if ignore_indent {
16172                    0
16173                } else {
16174                    snapshot.indent_size_for_line(row).len
16175                };
16176
16177                let start = Point::new(row.0, indent_size);
16178
16179                let mut line_bytes = snapshot
16180                    .bytes_in_range(start..snapshot.max_point())
16181                    .flatten()
16182                    .copied();
16183
16184                // If this line currently begins with the line comment prefix, then record
16185                // the range containing the prefix.
16186                if line_bytes
16187                    .by_ref()
16188                    .take(comment_prefix.len())
16189                    .eq(comment_prefix.bytes())
16190                {
16191                    // Include any whitespace that matches the comment prefix.
16192                    let matching_whitespace_len = line_bytes
16193                        .zip(comment_prefix_whitespace.bytes())
16194                        .take_while(|(a, b)| a == b)
16195                        .count() as u32;
16196                    let end = Point::new(
16197                        start.row,
16198                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16199                    );
16200                    start..end
16201                } else {
16202                    start..start
16203                }
16204            }
16205
16206            fn comment_suffix_range(
16207                snapshot: &MultiBufferSnapshot,
16208                row: MultiBufferRow,
16209                comment_suffix: &str,
16210                comment_suffix_has_leading_space: bool,
16211            ) -> Range<Point> {
16212                let end = Point::new(row.0, snapshot.line_len(row));
16213                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16214
16215                let mut line_end_bytes = snapshot
16216                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16217                    .flatten()
16218                    .copied();
16219
16220                let leading_space_len = if suffix_start_column > 0
16221                    && line_end_bytes.next() == Some(b' ')
16222                    && comment_suffix_has_leading_space
16223                {
16224                    1
16225                } else {
16226                    0
16227                };
16228
16229                // If this line currently begins with the line comment prefix, then record
16230                // the range containing the prefix.
16231                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16232                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16233                    start..end
16234                } else {
16235                    end..end
16236                }
16237            }
16238
16239            // TODO: Handle selections that cross excerpts
16240            for selection in &mut selections {
16241                let start_column = snapshot
16242                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16243                    .len;
16244                let language = if let Some(language) =
16245                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16246                {
16247                    language
16248                } else {
16249                    continue;
16250                };
16251
16252                selection_edit_ranges.clear();
16253
16254                // If multiple selections contain a given row, avoid processing that
16255                // row more than once.
16256                let mut start_row = MultiBufferRow(selection.start.row);
16257                if last_toggled_row == Some(start_row) {
16258                    start_row = start_row.next_row();
16259                }
16260                let end_row =
16261                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16262                        MultiBufferRow(selection.end.row - 1)
16263                    } else {
16264                        MultiBufferRow(selection.end.row)
16265                    };
16266                last_toggled_row = Some(end_row);
16267
16268                if start_row > end_row {
16269                    continue;
16270                }
16271
16272                // If the language has line comments, toggle those.
16273                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16274
16275                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16276                if ignore_indent {
16277                    full_comment_prefixes = full_comment_prefixes
16278                        .into_iter()
16279                        .map(|s| Arc::from(s.trim_end()))
16280                        .collect();
16281                }
16282
16283                if !full_comment_prefixes.is_empty() {
16284                    let first_prefix = full_comment_prefixes
16285                        .first()
16286                        .expect("prefixes is non-empty");
16287                    let prefix_trimmed_lengths = full_comment_prefixes
16288                        .iter()
16289                        .map(|p| p.trim_end_matches(' ').len())
16290                        .collect::<SmallVec<[usize; 4]>>();
16291
16292                    let mut all_selection_lines_are_comments = true;
16293
16294                    for row in start_row.0..=end_row.0 {
16295                        let row = MultiBufferRow(row);
16296                        if start_row < end_row && snapshot.is_line_blank(row) {
16297                            continue;
16298                        }
16299
16300                        let prefix_range = full_comment_prefixes
16301                            .iter()
16302                            .zip(prefix_trimmed_lengths.iter().copied())
16303                            .map(|(prefix, trimmed_prefix_len)| {
16304                                comment_prefix_range(
16305                                    snapshot.deref(),
16306                                    row,
16307                                    &prefix[..trimmed_prefix_len],
16308                                    &prefix[trimmed_prefix_len..],
16309                                    ignore_indent,
16310                                )
16311                            })
16312                            .max_by_key(|range| range.end.column - range.start.column)
16313                            .expect("prefixes is non-empty");
16314
16315                        if prefix_range.is_empty() {
16316                            all_selection_lines_are_comments = false;
16317                        }
16318
16319                        selection_edit_ranges.push(prefix_range);
16320                    }
16321
16322                    if all_selection_lines_are_comments {
16323                        edits.extend(
16324                            selection_edit_ranges
16325                                .iter()
16326                                .cloned()
16327                                .map(|range| (range, empty_str.clone())),
16328                        );
16329                    } else {
16330                        let min_column = selection_edit_ranges
16331                            .iter()
16332                            .map(|range| range.start.column)
16333                            .min()
16334                            .unwrap_or(0);
16335                        edits.extend(selection_edit_ranges.iter().map(|range| {
16336                            let position = Point::new(range.start.row, min_column);
16337                            (position..position, first_prefix.clone())
16338                        }));
16339                    }
16340                } else if let Some(BlockCommentConfig {
16341                    start: full_comment_prefix,
16342                    end: comment_suffix,
16343                    ..
16344                }) = language.block_comment()
16345                {
16346                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16347                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16348                    let prefix_range = comment_prefix_range(
16349                        snapshot.deref(),
16350                        start_row,
16351                        comment_prefix,
16352                        comment_prefix_whitespace,
16353                        ignore_indent,
16354                    );
16355                    let suffix_range = comment_suffix_range(
16356                        snapshot.deref(),
16357                        end_row,
16358                        comment_suffix.trim_start_matches(' '),
16359                        comment_suffix.starts_with(' '),
16360                    );
16361
16362                    if prefix_range.is_empty() || suffix_range.is_empty() {
16363                        edits.push((
16364                            prefix_range.start..prefix_range.start,
16365                            full_comment_prefix.clone(),
16366                        ));
16367                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16368                        suffixes_inserted.push((end_row, comment_suffix.len()));
16369                    } else {
16370                        edits.push((prefix_range, empty_str.clone()));
16371                        edits.push((suffix_range, empty_str.clone()));
16372                    }
16373                } else {
16374                    continue;
16375                }
16376            }
16377
16378            drop(snapshot);
16379            this.buffer.update(cx, |buffer, cx| {
16380                buffer.edit(edits, None, cx);
16381            });
16382
16383            // Adjust selections so that they end before any comment suffixes that
16384            // were inserted.
16385            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16386            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16387            let snapshot = this.buffer.read(cx).read(cx);
16388            for selection in &mut selections {
16389                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16390                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16391                        Ordering::Less => {
16392                            suffixes_inserted.next();
16393                            continue;
16394                        }
16395                        Ordering::Greater => break,
16396                        Ordering::Equal => {
16397                            if selection.end.column == snapshot.line_len(row) {
16398                                if selection.is_empty() {
16399                                    selection.start.column -= suffix_len as u32;
16400                                }
16401                                selection.end.column -= suffix_len as u32;
16402                            }
16403                            break;
16404                        }
16405                    }
16406                }
16407            }
16408
16409            drop(snapshot);
16410            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16411
16412            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16413            let selections_on_single_row = selections.windows(2).all(|selections| {
16414                selections[0].start.row == selections[1].start.row
16415                    && selections[0].end.row == selections[1].end.row
16416                    && selections[0].start.row == selections[0].end.row
16417            });
16418            let selections_selecting = selections
16419                .iter()
16420                .any(|selection| selection.start != selection.end);
16421            let advance_downwards = action.advance_downwards
16422                && selections_on_single_row
16423                && !selections_selecting
16424                && !matches!(this.mode, EditorMode::SingleLine);
16425
16426            if advance_downwards {
16427                let snapshot = this.buffer.read(cx).snapshot(cx);
16428
16429                this.change_selections(Default::default(), window, cx, |s| {
16430                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16431                        let mut point = display_point.to_point(display_snapshot);
16432                        point.row += 1;
16433                        point = snapshot.clip_point(point, Bias::Left);
16434                        let display_point = point.to_display_point(display_snapshot);
16435                        let goal = SelectionGoal::HorizontalPosition(
16436                            display_snapshot
16437                                .x_for_display_point(display_point, text_layout_details)
16438                                .into(),
16439                        );
16440                        (display_point, goal)
16441                    })
16442                });
16443            }
16444        });
16445    }
16446
16447    pub fn select_enclosing_symbol(
16448        &mut self,
16449        _: &SelectEnclosingSymbol,
16450        window: &mut Window,
16451        cx: &mut Context<Self>,
16452    ) {
16453        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16454
16455        let buffer = self.buffer.read(cx).snapshot(cx);
16456        let old_selections = self
16457            .selections
16458            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16459            .into_boxed_slice();
16460
16461        fn update_selection(
16462            selection: &Selection<MultiBufferOffset>,
16463            buffer_snap: &MultiBufferSnapshot,
16464        ) -> Option<Selection<MultiBufferOffset>> {
16465            let cursor = selection.head();
16466            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16467            for symbol in symbols.iter().rev() {
16468                let start = symbol.range.start.to_offset(buffer_snap);
16469                let end = symbol.range.end.to_offset(buffer_snap);
16470                let new_range = start..end;
16471                if start < selection.start || end > selection.end {
16472                    return Some(Selection {
16473                        id: selection.id,
16474                        start: new_range.start,
16475                        end: new_range.end,
16476                        goal: SelectionGoal::None,
16477                        reversed: selection.reversed,
16478                    });
16479                }
16480            }
16481            None
16482        }
16483
16484        let mut selected_larger_symbol = false;
16485        let new_selections = old_selections
16486            .iter()
16487            .map(|selection| match update_selection(selection, &buffer) {
16488                Some(new_selection) => {
16489                    if new_selection.range() != selection.range() {
16490                        selected_larger_symbol = true;
16491                    }
16492                    new_selection
16493                }
16494                None => selection.clone(),
16495            })
16496            .collect::<Vec<_>>();
16497
16498        if selected_larger_symbol {
16499            self.change_selections(Default::default(), window, cx, |s| {
16500                s.select(new_selections);
16501            });
16502        }
16503    }
16504
16505    pub fn select_larger_syntax_node(
16506        &mut self,
16507        _: &SelectLargerSyntaxNode,
16508        window: &mut Window,
16509        cx: &mut Context<Self>,
16510    ) {
16511        let Some(visible_row_count) = self.visible_row_count() else {
16512            return;
16513        };
16514        let old_selections: Box<[_]> = self
16515            .selections
16516            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16517            .into();
16518        if old_selections.is_empty() {
16519            return;
16520        }
16521
16522        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16523
16524        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16525        let buffer = self.buffer.read(cx).snapshot(cx);
16526
16527        let mut selected_larger_node = false;
16528        let mut new_selections = old_selections
16529            .iter()
16530            .map(|selection| {
16531                let old_range = selection.start..selection.end;
16532
16533                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16534                    // manually select word at selection
16535                    if ["string_content", "inline"].contains(&node.kind()) {
16536                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16537                        // ignore if word is already selected
16538                        if !word_range.is_empty() && old_range != word_range {
16539                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16540                            // only select word if start and end point belongs to same word
16541                            if word_range == last_word_range {
16542                                selected_larger_node = true;
16543                                return Selection {
16544                                    id: selection.id,
16545                                    start: word_range.start,
16546                                    end: word_range.end,
16547                                    goal: SelectionGoal::None,
16548                                    reversed: selection.reversed,
16549                                };
16550                            }
16551                        }
16552                    }
16553                }
16554
16555                let mut new_range = old_range.clone();
16556                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16557                    new_range = range;
16558                    if !node.is_named() {
16559                        continue;
16560                    }
16561                    if !display_map.intersects_fold(new_range.start)
16562                        && !display_map.intersects_fold(new_range.end)
16563                    {
16564                        break;
16565                    }
16566                }
16567
16568                selected_larger_node |= new_range != old_range;
16569                Selection {
16570                    id: selection.id,
16571                    start: new_range.start,
16572                    end: new_range.end,
16573                    goal: SelectionGoal::None,
16574                    reversed: selection.reversed,
16575                }
16576            })
16577            .collect::<Vec<_>>();
16578
16579        if !selected_larger_node {
16580            return; // don't put this call in the history
16581        }
16582
16583        // scroll based on transformation done to the last selection created by the user
16584        let (last_old, last_new) = old_selections
16585            .last()
16586            .zip(new_selections.last().cloned())
16587            .expect("old_selections isn't empty");
16588
16589        // revert selection
16590        let is_selection_reversed = {
16591            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16592            new_selections.last_mut().expect("checked above").reversed =
16593                should_newest_selection_be_reversed;
16594            should_newest_selection_be_reversed
16595        };
16596
16597        if selected_larger_node {
16598            self.select_syntax_node_history.disable_clearing = true;
16599            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16600                s.select(new_selections.clone());
16601            });
16602            self.select_syntax_node_history.disable_clearing = false;
16603        }
16604
16605        let start_row = last_new.start.to_display_point(&display_map).row().0;
16606        let end_row = last_new.end.to_display_point(&display_map).row().0;
16607        let selection_height = end_row - start_row + 1;
16608        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16609
16610        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16611        let scroll_behavior = if fits_on_the_screen {
16612            self.request_autoscroll(Autoscroll::fit(), cx);
16613            SelectSyntaxNodeScrollBehavior::FitSelection
16614        } else if is_selection_reversed {
16615            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16616            SelectSyntaxNodeScrollBehavior::CursorTop
16617        } else {
16618            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16619            SelectSyntaxNodeScrollBehavior::CursorBottom
16620        };
16621
16622        let old_selections: Box<[Selection<Anchor>]> = old_selections
16623            .iter()
16624            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16625            .collect();
16626        self.select_syntax_node_history.push((
16627            old_selections,
16628            scroll_behavior,
16629            is_selection_reversed,
16630        ));
16631    }
16632
16633    pub fn select_smaller_syntax_node(
16634        &mut self,
16635        _: &SelectSmallerSyntaxNode,
16636        window: &mut Window,
16637        cx: &mut Context<Self>,
16638    ) {
16639        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16640
16641        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16642            self.select_syntax_node_history.pop()
16643        {
16644            if let Some(selection) = selections.last_mut() {
16645                selection.reversed = is_selection_reversed;
16646            }
16647
16648            let snapshot = self.buffer.read(cx).snapshot(cx);
16649            let selections: Vec<Selection<MultiBufferOffset>> = selections
16650                .iter()
16651                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16652                .collect();
16653
16654            self.select_syntax_node_history.disable_clearing = true;
16655            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16656                s.select(selections);
16657            });
16658            self.select_syntax_node_history.disable_clearing = false;
16659
16660            match scroll_behavior {
16661                SelectSyntaxNodeScrollBehavior::CursorTop => {
16662                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16663                }
16664                SelectSyntaxNodeScrollBehavior::FitSelection => {
16665                    self.request_autoscroll(Autoscroll::fit(), cx);
16666                }
16667                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16668                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16669                }
16670            }
16671        }
16672    }
16673
16674    pub fn unwrap_syntax_node(
16675        &mut self,
16676        _: &UnwrapSyntaxNode,
16677        window: &mut Window,
16678        cx: &mut Context<Self>,
16679    ) {
16680        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16681
16682        let buffer = self.buffer.read(cx).snapshot(cx);
16683        let selections = self
16684            .selections
16685            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16686            .into_iter()
16687            // subtracting the offset requires sorting
16688            .sorted_by_key(|i| i.start);
16689
16690        let full_edits = selections
16691            .into_iter()
16692            .filter_map(|selection| {
16693                let child = if selection.is_empty()
16694                    && let Some((_, ancestor_range)) =
16695                        buffer.syntax_ancestor(selection.start..selection.end)
16696                {
16697                    ancestor_range
16698                } else {
16699                    selection.range()
16700                };
16701
16702                let mut parent = child.clone();
16703                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16704                    parent = ancestor_range;
16705                    if parent.start < child.start || parent.end > child.end {
16706                        break;
16707                    }
16708                }
16709
16710                if parent == child {
16711                    return None;
16712                }
16713                let text = buffer.text_for_range(child).collect::<String>();
16714                Some((selection.id, parent, text))
16715            })
16716            .collect::<Vec<_>>();
16717        if full_edits.is_empty() {
16718            return;
16719        }
16720
16721        self.transact(window, cx, |this, window, cx| {
16722            this.buffer.update(cx, |buffer, cx| {
16723                buffer.edit(
16724                    full_edits
16725                        .iter()
16726                        .map(|(_, p, t)| (p.clone(), t.clone()))
16727                        .collect::<Vec<_>>(),
16728                    None,
16729                    cx,
16730                );
16731            });
16732            this.change_selections(Default::default(), window, cx, |s| {
16733                let mut offset = 0;
16734                let mut selections = vec![];
16735                for (id, parent, text) in full_edits {
16736                    let start = parent.start - offset;
16737                    offset += (parent.end - parent.start) - text.len();
16738                    selections.push(Selection {
16739                        id,
16740                        start,
16741                        end: start + text.len(),
16742                        reversed: false,
16743                        goal: Default::default(),
16744                    });
16745                }
16746                s.select(selections);
16747            });
16748        });
16749    }
16750
16751    pub fn select_next_syntax_node(
16752        &mut self,
16753        _: &SelectNextSyntaxNode,
16754        window: &mut Window,
16755        cx: &mut Context<Self>,
16756    ) {
16757        let old_selections: Box<[_]> = self
16758            .selections
16759            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16760            .into();
16761        if old_selections.is_empty() {
16762            return;
16763        }
16764
16765        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16766
16767        let buffer = self.buffer.read(cx).snapshot(cx);
16768        let mut selected_sibling = false;
16769
16770        let new_selections = old_selections
16771            .iter()
16772            .map(|selection| {
16773                let old_range = selection.start..selection.end;
16774
16775                let old_range =
16776                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16777                let excerpt = buffer.excerpt_containing(old_range.clone());
16778
16779                if let Some(mut excerpt) = excerpt
16780                    && let Some(node) = excerpt
16781                        .buffer()
16782                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16783                {
16784                    let new_range = excerpt.map_range_from_buffer(
16785                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16786                    );
16787                    selected_sibling = true;
16788                    Selection {
16789                        id: selection.id,
16790                        start: new_range.start,
16791                        end: new_range.end,
16792                        goal: SelectionGoal::None,
16793                        reversed: selection.reversed,
16794                    }
16795                } else {
16796                    selection.clone()
16797                }
16798            })
16799            .collect::<Vec<_>>();
16800
16801        if selected_sibling {
16802            self.change_selections(
16803                SelectionEffects::scroll(Autoscroll::fit()),
16804                window,
16805                cx,
16806                |s| {
16807                    s.select(new_selections);
16808                },
16809            );
16810        }
16811    }
16812
16813    pub fn select_prev_syntax_node(
16814        &mut self,
16815        _: &SelectPreviousSyntaxNode,
16816        window: &mut Window,
16817        cx: &mut Context<Self>,
16818    ) {
16819        let old_selections: Box<[_]> = self
16820            .selections
16821            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16822            .into();
16823        if old_selections.is_empty() {
16824            return;
16825        }
16826
16827        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16828
16829        let buffer = self.buffer.read(cx).snapshot(cx);
16830        let mut selected_sibling = false;
16831
16832        let new_selections = old_selections
16833            .iter()
16834            .map(|selection| {
16835                let old_range = selection.start..selection.end;
16836                let old_range =
16837                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16838                let excerpt = buffer.excerpt_containing(old_range.clone());
16839
16840                if let Some(mut excerpt) = excerpt
16841                    && let Some(node) = excerpt
16842                        .buffer()
16843                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16844                {
16845                    let new_range = excerpt.map_range_from_buffer(
16846                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16847                    );
16848                    selected_sibling = true;
16849                    Selection {
16850                        id: selection.id,
16851                        start: new_range.start,
16852                        end: new_range.end,
16853                        goal: SelectionGoal::None,
16854                        reversed: selection.reversed,
16855                    }
16856                } else {
16857                    selection.clone()
16858                }
16859            })
16860            .collect::<Vec<_>>();
16861
16862        if selected_sibling {
16863            self.change_selections(
16864                SelectionEffects::scroll(Autoscroll::fit()),
16865                window,
16866                cx,
16867                |s| {
16868                    s.select(new_selections);
16869                },
16870            );
16871        }
16872    }
16873
16874    pub fn move_to_start_of_larger_syntax_node(
16875        &mut self,
16876        _: &MoveToStartOfLargerSyntaxNode,
16877        window: &mut Window,
16878        cx: &mut Context<Self>,
16879    ) {
16880        self.move_cursors_to_syntax_nodes(window, cx, false);
16881    }
16882
16883    pub fn move_to_end_of_larger_syntax_node(
16884        &mut self,
16885        _: &MoveToEndOfLargerSyntaxNode,
16886        window: &mut Window,
16887        cx: &mut Context<Self>,
16888    ) {
16889        self.move_cursors_to_syntax_nodes(window, cx, true);
16890    }
16891
16892    fn find_syntax_node_boundary(
16893        &self,
16894        selection_pos: MultiBufferOffset,
16895        move_to_end: bool,
16896        display_map: &DisplaySnapshot,
16897        buffer: &MultiBufferSnapshot,
16898    ) -> MultiBufferOffset {
16899        let old_range = selection_pos..selection_pos;
16900        let mut new_pos = selection_pos;
16901        let mut search_range = old_range;
16902        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16903            search_range = range.clone();
16904            if !node.is_named()
16905                || display_map.intersects_fold(range.start)
16906                || display_map.intersects_fold(range.end)
16907                // If cursor is already at the end of the syntax node, continue searching
16908                || (move_to_end && range.end == selection_pos)
16909                // If cursor is already at the start of the syntax node, continue searching
16910                || (!move_to_end && range.start == selection_pos)
16911            {
16912                continue;
16913            }
16914
16915            // If we found a string_content node, find the largest parent that is still string_content
16916            // Enables us to skip to the end of strings without taking multiple steps inside the string
16917            let (_, final_range) = if node.kind() == "string_content" {
16918                let mut current_node = node;
16919                let mut current_range = range;
16920                while let Some((parent, parent_range)) =
16921                    buffer.syntax_ancestor(current_range.clone())
16922                {
16923                    if parent.kind() == "string_content" {
16924                        current_node = parent;
16925                        current_range = parent_range;
16926                    } else {
16927                        break;
16928                    }
16929                }
16930
16931                (current_node, current_range)
16932            } else {
16933                (node, range)
16934            };
16935
16936            new_pos = if move_to_end {
16937                final_range.end
16938            } else {
16939                final_range.start
16940            };
16941
16942            break;
16943        }
16944
16945        new_pos
16946    }
16947
16948    fn move_cursors_to_syntax_nodes(
16949        &mut self,
16950        window: &mut Window,
16951        cx: &mut Context<Self>,
16952        move_to_end: bool,
16953    ) -> bool {
16954        let old_selections: Box<[_]> = self
16955            .selections
16956            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16957            .into();
16958        if old_selections.is_empty() {
16959            return false;
16960        }
16961
16962        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16963
16964        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16965        let buffer = self.buffer.read(cx).snapshot(cx);
16966
16967        let mut any_cursor_moved = false;
16968        let new_selections = old_selections
16969            .iter()
16970            .map(|selection| {
16971                if !selection.is_empty() {
16972                    return selection.clone();
16973                }
16974
16975                let selection_pos = selection.head();
16976                let new_pos = self.find_syntax_node_boundary(
16977                    selection_pos,
16978                    move_to_end,
16979                    &display_map,
16980                    &buffer,
16981                );
16982
16983                any_cursor_moved |= new_pos != selection_pos;
16984
16985                Selection {
16986                    id: selection.id,
16987                    start: new_pos,
16988                    end: new_pos,
16989                    goal: SelectionGoal::None,
16990                    reversed: false,
16991                }
16992            })
16993            .collect::<Vec<_>>();
16994
16995        self.change_selections(Default::default(), window, cx, |s| {
16996            s.select(new_selections);
16997        });
16998        self.request_autoscroll(Autoscroll::newest(), cx);
16999
17000        any_cursor_moved
17001    }
17002
17003    pub fn select_to_start_of_larger_syntax_node(
17004        &mut self,
17005        _: &SelectToStartOfLargerSyntaxNode,
17006        window: &mut Window,
17007        cx: &mut Context<Self>,
17008    ) {
17009        self.select_to_syntax_nodes(window, cx, false);
17010    }
17011
17012    pub fn select_to_end_of_larger_syntax_node(
17013        &mut self,
17014        _: &SelectToEndOfLargerSyntaxNode,
17015        window: &mut Window,
17016        cx: &mut Context<Self>,
17017    ) {
17018        self.select_to_syntax_nodes(window, cx, true);
17019    }
17020
17021    fn select_to_syntax_nodes(
17022        &mut self,
17023        window: &mut Window,
17024        cx: &mut Context<Self>,
17025        move_to_end: bool,
17026    ) {
17027        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17028
17029        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17030        let buffer = self.buffer.read(cx).snapshot(cx);
17031        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17032
17033        let new_selections = old_selections
17034            .iter()
17035            .map(|selection| {
17036                let new_pos = self.find_syntax_node_boundary(
17037                    selection.head(),
17038                    move_to_end,
17039                    &display_map,
17040                    &buffer,
17041                );
17042
17043                let mut new_selection = selection.clone();
17044                new_selection.set_head(new_pos, SelectionGoal::None);
17045                new_selection
17046            })
17047            .collect::<Vec<_>>();
17048
17049        self.change_selections(Default::default(), window, cx, |s| {
17050            s.select(new_selections);
17051        });
17052    }
17053
17054    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17055        if !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables {
17056            self.clear_tasks();
17057            return Task::ready(());
17058        }
17059        let project = self.project().map(Entity::downgrade);
17060        let task_sources = self.lsp_task_sources(cx);
17061        let multi_buffer = self.buffer.downgrade();
17062        cx.spawn_in(window, async move |editor, cx| {
17063            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17064            let Some(project) = project.and_then(|p| p.upgrade()) else {
17065                return;
17066            };
17067            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17068                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17069            }) else {
17070                return;
17071            };
17072
17073            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17074            if hide_runnables {
17075                return;
17076            }
17077            let new_rows =
17078                cx.background_spawn({
17079                    let snapshot = display_snapshot.clone();
17080                    async move {
17081                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
17082                    }
17083                })
17084                    .await;
17085            let Ok(lsp_tasks) =
17086                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17087            else {
17088                return;
17089            };
17090            let lsp_tasks = lsp_tasks.await;
17091
17092            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17093                lsp_tasks
17094                    .into_iter()
17095                    .flat_map(|(kind, tasks)| {
17096                        tasks.into_iter().filter_map(move |(location, task)| {
17097                            Some((kind.clone(), location?, task))
17098                        })
17099                    })
17100                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17101                        let buffer = location.target.buffer;
17102                        let buffer_snapshot = buffer.read(cx).snapshot();
17103                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17104                            |(excerpt_id, snapshot, _)| {
17105                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17106                                    display_snapshot
17107                                        .buffer_snapshot()
17108                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17109                                } else {
17110                                    None
17111                                }
17112                            },
17113                        );
17114                        if let Some(offset) = offset {
17115                            let task_buffer_range =
17116                                location.target.range.to_point(&buffer_snapshot);
17117                            let context_buffer_range =
17118                                task_buffer_range.to_offset(&buffer_snapshot);
17119                            let context_range = BufferOffset(context_buffer_range.start)
17120                                ..BufferOffset(context_buffer_range.end);
17121
17122                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17123                                .or_insert_with(|| RunnableTasks {
17124                                    templates: Vec::new(),
17125                                    offset,
17126                                    column: task_buffer_range.start.column,
17127                                    extra_variables: HashMap::default(),
17128                                    context_range,
17129                                })
17130                                .templates
17131                                .push((kind, task.original_task().clone()));
17132                        }
17133
17134                        acc
17135                    })
17136            }) else {
17137                return;
17138            };
17139
17140            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17141                buffer.language_settings(cx).tasks.prefer_lsp
17142            }) else {
17143                return;
17144            };
17145
17146            let rows = Self::runnable_rows(
17147                project,
17148                display_snapshot,
17149                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17150                new_rows,
17151                cx.clone(),
17152            )
17153            .await;
17154            editor
17155                .update(cx, |editor, _| {
17156                    editor.clear_tasks();
17157                    for (key, mut value) in rows {
17158                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17159                            value.templates.extend(lsp_tasks.templates);
17160                        }
17161
17162                        editor.insert_tasks(key, value);
17163                    }
17164                    for (key, value) in lsp_tasks_by_rows {
17165                        editor.insert_tasks(key, value);
17166                    }
17167                })
17168                .ok();
17169        })
17170    }
17171    fn fetch_runnable_ranges(
17172        snapshot: &DisplaySnapshot,
17173        range: Range<Anchor>,
17174    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
17175        snapshot.buffer_snapshot().runnable_ranges(range).collect()
17176    }
17177
17178    fn runnable_rows(
17179        project: Entity<Project>,
17180        snapshot: DisplaySnapshot,
17181        prefer_lsp: bool,
17182        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17183        cx: AsyncWindowContext,
17184    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17185        cx.spawn(async move |cx| {
17186            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17187            for (run_range, mut runnable) in runnable_ranges {
17188                let Some(tasks) = cx
17189                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17190                    .ok()
17191                else {
17192                    continue;
17193                };
17194                let mut tasks = tasks.await;
17195
17196                if prefer_lsp {
17197                    tasks.retain(|(task_kind, _)| {
17198                        !matches!(task_kind, TaskSourceKind::Language { .. })
17199                    });
17200                }
17201                if tasks.is_empty() {
17202                    continue;
17203                }
17204
17205                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17206                let Some(row) = snapshot
17207                    .buffer_snapshot()
17208                    .buffer_line_for_row(MultiBufferRow(point.row))
17209                    .map(|(_, range)| range.start.row)
17210                else {
17211                    continue;
17212                };
17213
17214                let context_range =
17215                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17216                runnable_rows.push((
17217                    (runnable.buffer_id, row),
17218                    RunnableTasks {
17219                        templates: tasks,
17220                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17221                        context_range,
17222                        column: point.column,
17223                        extra_variables: runnable.extra_captures,
17224                    },
17225                ));
17226            }
17227            runnable_rows
17228        })
17229    }
17230
17231    fn templates_with_tags(
17232        project: &Entity<Project>,
17233        runnable: &mut Runnable,
17234        cx: &mut App,
17235    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17236        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17237            let (worktree_id, file) = project
17238                .buffer_for_id(runnable.buffer, cx)
17239                .and_then(|buffer| buffer.read(cx).file())
17240                .map(|file| (file.worktree_id(cx), file.clone()))
17241                .unzip();
17242
17243            (
17244                project.task_store().read(cx).task_inventory().cloned(),
17245                worktree_id,
17246                file,
17247            )
17248        });
17249
17250        let tags = mem::take(&mut runnable.tags);
17251        let language = runnable.language.clone();
17252        cx.spawn(async move |cx| {
17253            let mut templates_with_tags = Vec::new();
17254            if let Some(inventory) = inventory {
17255                for RunnableTag(tag) in tags {
17256                    let new_tasks = inventory.update(cx, |inventory, cx| {
17257                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17258                    });
17259                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17260                        move |(_, template)| {
17261                            template.tags.iter().any(|source_tag| source_tag == &tag)
17262                        },
17263                    ));
17264                }
17265            }
17266            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17267
17268            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17269                // Strongest source wins; if we have worktree tag binding, prefer that to
17270                // global and language bindings;
17271                // if we have a global binding, prefer that to language binding.
17272                let first_mismatch = templates_with_tags
17273                    .iter()
17274                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17275                if let Some(index) = first_mismatch {
17276                    templates_with_tags.truncate(index);
17277                }
17278            }
17279
17280            templates_with_tags
17281        })
17282    }
17283
17284    pub fn move_to_enclosing_bracket(
17285        &mut self,
17286        _: &MoveToEnclosingBracket,
17287        window: &mut Window,
17288        cx: &mut Context<Self>,
17289    ) {
17290        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17291        self.change_selections(Default::default(), window, cx, |s| {
17292            s.move_offsets_with(&mut |snapshot, selection| {
17293                let Some(enclosing_bracket_ranges) =
17294                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17295                else {
17296                    return;
17297                };
17298
17299                let mut best_length = usize::MAX;
17300                let mut best_inside = false;
17301                let mut best_in_bracket_range = false;
17302                let mut best_destination = None;
17303                for (open, close) in enclosing_bracket_ranges {
17304                    let close = close.to_inclusive();
17305                    let length = *close.end() - open.start;
17306                    let inside = selection.start >= open.end && selection.end <= *close.start();
17307                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17308                        || close.contains(&selection.head());
17309
17310                    // If best is next to a bracket and current isn't, skip
17311                    if !in_bracket_range && best_in_bracket_range {
17312                        continue;
17313                    }
17314
17315                    // Prefer smaller lengths unless best is inside and current isn't
17316                    if length > best_length && (best_inside || !inside) {
17317                        continue;
17318                    }
17319
17320                    best_length = length;
17321                    best_inside = inside;
17322                    best_in_bracket_range = in_bracket_range;
17323                    best_destination = Some(
17324                        if close.contains(&selection.start) && close.contains(&selection.end) {
17325                            if inside { open.end } else { open.start }
17326                        } else if inside {
17327                            *close.start()
17328                        } else {
17329                            *close.end()
17330                        },
17331                    );
17332                }
17333
17334                if let Some(destination) = best_destination {
17335                    selection.collapse_to(destination, SelectionGoal::None);
17336                }
17337            })
17338        });
17339    }
17340
17341    pub fn undo_selection(
17342        &mut self,
17343        _: &UndoSelection,
17344        window: &mut Window,
17345        cx: &mut Context<Self>,
17346    ) {
17347        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17348        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17349            self.selection_history.mode = SelectionHistoryMode::Undoing;
17350            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17351                this.end_selection(window, cx);
17352                this.change_selections(
17353                    SelectionEffects::scroll(Autoscroll::newest()),
17354                    window,
17355                    cx,
17356                    |s| s.select_anchors(entry.selections.to_vec()),
17357                );
17358            });
17359            self.selection_history.mode = SelectionHistoryMode::Normal;
17360
17361            self.select_next_state = entry.select_next_state;
17362            self.select_prev_state = entry.select_prev_state;
17363            self.add_selections_state = entry.add_selections_state;
17364        }
17365    }
17366
17367    pub fn redo_selection(
17368        &mut self,
17369        _: &RedoSelection,
17370        window: &mut Window,
17371        cx: &mut Context<Self>,
17372    ) {
17373        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17374        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17375            self.selection_history.mode = SelectionHistoryMode::Redoing;
17376            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17377                this.end_selection(window, cx);
17378                this.change_selections(
17379                    SelectionEffects::scroll(Autoscroll::newest()),
17380                    window,
17381                    cx,
17382                    |s| s.select_anchors(entry.selections.to_vec()),
17383                );
17384            });
17385            self.selection_history.mode = SelectionHistoryMode::Normal;
17386
17387            self.select_next_state = entry.select_next_state;
17388            self.select_prev_state = entry.select_prev_state;
17389            self.add_selections_state = entry.add_selections_state;
17390        }
17391    }
17392
17393    pub fn expand_excerpts(
17394        &mut self,
17395        action: &ExpandExcerpts,
17396        _: &mut Window,
17397        cx: &mut Context<Self>,
17398    ) {
17399        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17400    }
17401
17402    pub fn expand_excerpts_down(
17403        &mut self,
17404        action: &ExpandExcerptsDown,
17405        _: &mut Window,
17406        cx: &mut Context<Self>,
17407    ) {
17408        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17409    }
17410
17411    pub fn expand_excerpts_up(
17412        &mut self,
17413        action: &ExpandExcerptsUp,
17414        _: &mut Window,
17415        cx: &mut Context<Self>,
17416    ) {
17417        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17418    }
17419
17420    pub fn expand_excerpts_for_direction(
17421        &mut self,
17422        lines: u32,
17423        direction: ExpandExcerptDirection,
17424        cx: &mut Context<Self>,
17425    ) {
17426        let selections = self.selections.disjoint_anchors_arc();
17427
17428        let lines = if lines == 0 {
17429            EditorSettings::get_global(cx).expand_excerpt_lines
17430        } else {
17431            lines
17432        };
17433
17434        let snapshot = self.buffer.read(cx).snapshot(cx);
17435        let excerpt_ids = selections
17436            .iter()
17437            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17438            .unique()
17439            .sorted()
17440            .collect::<Vec<_>>();
17441
17442        if self.delegate_expand_excerpts {
17443            cx.emit(EditorEvent::ExpandExcerptsRequested {
17444                excerpt_ids,
17445                lines,
17446                direction,
17447            });
17448            return;
17449        }
17450
17451        self.buffer.update(cx, |buffer, cx| {
17452            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17453        })
17454    }
17455
17456    pub fn expand_excerpt(
17457        &mut self,
17458        excerpt: ExcerptId,
17459        direction: ExpandExcerptDirection,
17460        window: &mut Window,
17461        cx: &mut Context<Self>,
17462    ) {
17463        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17464
17465        if self.delegate_expand_excerpts {
17466            cx.emit(EditorEvent::ExpandExcerptsRequested {
17467                excerpt_ids: vec![excerpt],
17468                lines: lines_to_expand,
17469                direction,
17470            });
17471            return;
17472        }
17473
17474        let current_scroll_position = self.scroll_position(cx);
17475        let mut scroll = None;
17476
17477        if direction == ExpandExcerptDirection::Down {
17478            let multi_buffer = self.buffer.read(cx);
17479            let snapshot = multi_buffer.snapshot(cx);
17480            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17481                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17482                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17483            {
17484                let buffer_snapshot = buffer.read(cx).snapshot();
17485                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17486                let last_row = buffer_snapshot.max_point().row;
17487                let lines_below = last_row.saturating_sub(excerpt_end_row);
17488                if lines_below >= lines_to_expand {
17489                    scroll = Some(
17490                        current_scroll_position
17491                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17492                    );
17493                }
17494            }
17495        }
17496        if direction == ExpandExcerptDirection::Up
17497            && self
17498                .buffer
17499                .read(cx)
17500                .snapshot(cx)
17501                .excerpt_before(excerpt)
17502                .is_none()
17503        {
17504            scroll = Some(current_scroll_position);
17505        }
17506
17507        self.buffer.update(cx, |buffer, cx| {
17508            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17509        });
17510
17511        if let Some(new_scroll_position) = scroll {
17512            self.set_scroll_position(new_scroll_position, window, cx);
17513        }
17514    }
17515
17516    pub fn go_to_singleton_buffer_point(
17517        &mut self,
17518        point: Point,
17519        window: &mut Window,
17520        cx: &mut Context<Self>,
17521    ) {
17522        self.go_to_singleton_buffer_range(point..point, window, cx);
17523    }
17524
17525    pub fn go_to_singleton_buffer_range(
17526        &mut self,
17527        range: Range<Point>,
17528        window: &mut Window,
17529        cx: &mut Context<Self>,
17530    ) {
17531        let multibuffer = self.buffer().read(cx);
17532        let Some(buffer) = multibuffer.as_singleton() else {
17533            return;
17534        };
17535        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17536            return;
17537        };
17538        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17539            return;
17540        };
17541        self.change_selections(
17542            SelectionEffects::default().nav_history(true),
17543            window,
17544            cx,
17545            |s| s.select_anchor_ranges([start..end]),
17546        );
17547    }
17548
17549    pub fn go_to_diagnostic(
17550        &mut self,
17551        action: &GoToDiagnostic,
17552        window: &mut Window,
17553        cx: &mut Context<Self>,
17554    ) {
17555        if !self.diagnostics_enabled() {
17556            return;
17557        }
17558        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17559        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17560    }
17561
17562    pub fn go_to_prev_diagnostic(
17563        &mut self,
17564        action: &GoToPreviousDiagnostic,
17565        window: &mut Window,
17566        cx: &mut Context<Self>,
17567    ) {
17568        if !self.diagnostics_enabled() {
17569            return;
17570        }
17571        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17572        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17573    }
17574
17575    pub fn go_to_diagnostic_impl(
17576        &mut self,
17577        direction: Direction,
17578        severity: GoToDiagnosticSeverityFilter,
17579        window: &mut Window,
17580        cx: &mut Context<Self>,
17581    ) {
17582        let buffer = self.buffer.read(cx).snapshot(cx);
17583        let selection = self
17584            .selections
17585            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17586
17587        let mut active_group_id = None;
17588        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17589            && active_group.active_range.start.to_offset(&buffer) == selection.start
17590        {
17591            active_group_id = Some(active_group.group_id);
17592        }
17593
17594        fn filtered<'a>(
17595            severity: GoToDiagnosticSeverityFilter,
17596            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17597        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17598            diagnostics
17599                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17600                .filter(|entry| entry.range.start != entry.range.end)
17601                .filter(|entry| !entry.diagnostic.is_unnecessary)
17602        }
17603
17604        let before = filtered(
17605            severity,
17606            buffer
17607                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17608                .filter(|entry| entry.range.start <= selection.start),
17609        );
17610        let after = filtered(
17611            severity,
17612            buffer
17613                .diagnostics_in_range(selection.start..buffer.len())
17614                .filter(|entry| entry.range.start >= selection.start),
17615        );
17616
17617        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17618        if direction == Direction::Prev {
17619            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17620            {
17621                for diagnostic in prev_diagnostics.into_iter().rev() {
17622                    if diagnostic.range.start != selection.start
17623                        || active_group_id
17624                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17625                    {
17626                        found = Some(diagnostic);
17627                        break 'outer;
17628                    }
17629                }
17630            }
17631        } else {
17632            for diagnostic in after.chain(before) {
17633                if diagnostic.range.start != selection.start
17634                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17635                {
17636                    found = Some(diagnostic);
17637                    break;
17638                }
17639            }
17640        }
17641        let Some(next_diagnostic) = found else {
17642            return;
17643        };
17644
17645        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17646        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17647            return;
17648        };
17649        let snapshot = self.snapshot(window, cx);
17650        if snapshot.intersects_fold(next_diagnostic.range.start) {
17651            self.unfold_ranges(
17652                std::slice::from_ref(&next_diagnostic.range),
17653                true,
17654                false,
17655                cx,
17656            );
17657        }
17658        self.change_selections(Default::default(), window, cx, |s| {
17659            s.select_ranges(vec![
17660                next_diagnostic.range.start..next_diagnostic.range.start,
17661            ])
17662        });
17663        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17664        self.refresh_edit_prediction(false, true, window, cx);
17665    }
17666
17667    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17668        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17669        let snapshot = self.snapshot(window, cx);
17670        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17671        self.go_to_hunk_before_or_after_position(
17672            &snapshot,
17673            selection.head(),
17674            Direction::Next,
17675            window,
17676            cx,
17677        );
17678    }
17679
17680    pub fn go_to_hunk_before_or_after_position(
17681        &mut self,
17682        snapshot: &EditorSnapshot,
17683        position: Point,
17684        direction: Direction,
17685        window: &mut Window,
17686        cx: &mut Context<Editor>,
17687    ) {
17688        let row = if direction == Direction::Next {
17689            self.hunk_after_position(snapshot, position)
17690                .map(|hunk| hunk.row_range.start)
17691        } else {
17692            self.hunk_before_position(snapshot, position)
17693        };
17694
17695        if let Some(row) = row {
17696            let destination = Point::new(row.0, 0);
17697            let autoscroll = Autoscroll::center();
17698
17699            self.unfold_ranges(&[destination..destination], false, false, cx);
17700            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17701                s.select_ranges([destination..destination]);
17702            });
17703        }
17704    }
17705
17706    fn hunk_after_position(
17707        &mut self,
17708        snapshot: &EditorSnapshot,
17709        position: Point,
17710    ) -> Option<MultiBufferDiffHunk> {
17711        snapshot
17712            .buffer_snapshot()
17713            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17714            .find(|hunk| hunk.row_range.start.0 > position.row)
17715            .or_else(|| {
17716                snapshot
17717                    .buffer_snapshot()
17718                    .diff_hunks_in_range(Point::zero()..position)
17719                    .find(|hunk| hunk.row_range.end.0 < position.row)
17720            })
17721    }
17722
17723    fn go_to_prev_hunk(
17724        &mut self,
17725        _: &GoToPreviousHunk,
17726        window: &mut Window,
17727        cx: &mut Context<Self>,
17728    ) {
17729        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17730        let snapshot = self.snapshot(window, cx);
17731        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17732        self.go_to_hunk_before_or_after_position(
17733            &snapshot,
17734            selection.head(),
17735            Direction::Prev,
17736            window,
17737            cx,
17738        );
17739    }
17740
17741    fn hunk_before_position(
17742        &mut self,
17743        snapshot: &EditorSnapshot,
17744        position: Point,
17745    ) -> Option<MultiBufferRow> {
17746        snapshot
17747            .buffer_snapshot()
17748            .diff_hunk_before(position)
17749            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17750    }
17751
17752    fn go_to_next_change(
17753        &mut self,
17754        _: &GoToNextChange,
17755        window: &mut Window,
17756        cx: &mut Context<Self>,
17757    ) {
17758        if let Some(selections) = self
17759            .change_list
17760            .next_change(1, Direction::Next)
17761            .map(|s| s.to_vec())
17762        {
17763            self.change_selections(Default::default(), window, cx, |s| {
17764                let map = s.display_snapshot();
17765                s.select_display_ranges(selections.iter().map(|a| {
17766                    let point = a.to_display_point(&map);
17767                    point..point
17768                }))
17769            })
17770        }
17771    }
17772
17773    fn go_to_previous_change(
17774        &mut self,
17775        _: &GoToPreviousChange,
17776        window: &mut Window,
17777        cx: &mut Context<Self>,
17778    ) {
17779        if let Some(selections) = self
17780            .change_list
17781            .next_change(1, Direction::Prev)
17782            .map(|s| s.to_vec())
17783        {
17784            self.change_selections(Default::default(), window, cx, |s| {
17785                let map = s.display_snapshot();
17786                s.select_display_ranges(selections.iter().map(|a| {
17787                    let point = a.to_display_point(&map);
17788                    point..point
17789                }))
17790            })
17791        }
17792    }
17793
17794    pub fn go_to_next_document_highlight(
17795        &mut self,
17796        _: &GoToNextDocumentHighlight,
17797        window: &mut Window,
17798        cx: &mut Context<Self>,
17799    ) {
17800        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17801    }
17802
17803    pub fn go_to_prev_document_highlight(
17804        &mut self,
17805        _: &GoToPreviousDocumentHighlight,
17806        window: &mut Window,
17807        cx: &mut Context<Self>,
17808    ) {
17809        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17810    }
17811
17812    pub fn go_to_document_highlight_before_or_after_position(
17813        &mut self,
17814        direction: Direction,
17815        window: &mut Window,
17816        cx: &mut Context<Editor>,
17817    ) {
17818        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17819        let snapshot = self.snapshot(window, cx);
17820        let buffer = &snapshot.buffer_snapshot();
17821        let position = self
17822            .selections
17823            .newest::<Point>(&snapshot.display_snapshot)
17824            .head();
17825        let anchor_position = buffer.anchor_after(position);
17826
17827        // Get all document highlights (both read and write)
17828        let mut all_highlights = Vec::new();
17829
17830        if let Some((_, read_highlights)) = self
17831            .background_highlights
17832            .get(&HighlightKey::DocumentHighlightRead)
17833        {
17834            all_highlights.extend(read_highlights.iter());
17835        }
17836
17837        if let Some((_, write_highlights)) = self
17838            .background_highlights
17839            .get(&HighlightKey::DocumentHighlightWrite)
17840        {
17841            all_highlights.extend(write_highlights.iter());
17842        }
17843
17844        if all_highlights.is_empty() {
17845            return;
17846        }
17847
17848        // Sort highlights by position
17849        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17850
17851        let target_highlight = match direction {
17852            Direction::Next => {
17853                // Find the first highlight after the current position
17854                all_highlights
17855                    .iter()
17856                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17857            }
17858            Direction::Prev => {
17859                // Find the last highlight before the current position
17860                all_highlights
17861                    .iter()
17862                    .rev()
17863                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17864            }
17865        };
17866
17867        if let Some(highlight) = target_highlight {
17868            let destination = highlight.start.to_point(buffer);
17869            let autoscroll = Autoscroll::center();
17870
17871            self.unfold_ranges(&[destination..destination], false, false, cx);
17872            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17873                s.select_ranges([destination..destination]);
17874            });
17875        }
17876    }
17877
17878    fn go_to_line<T: 'static>(
17879        &mut self,
17880        position: Anchor,
17881        highlight_color: Option<Hsla>,
17882        window: &mut Window,
17883        cx: &mut Context<Self>,
17884    ) {
17885        let snapshot = self.snapshot(window, cx).display_snapshot;
17886        let position = position.to_point(&snapshot.buffer_snapshot());
17887        let start = snapshot
17888            .buffer_snapshot()
17889            .clip_point(Point::new(position.row, 0), Bias::Left);
17890        let end = start + Point::new(1, 0);
17891        let start = snapshot.buffer_snapshot().anchor_before(start);
17892        let end = snapshot.buffer_snapshot().anchor_before(end);
17893
17894        self.highlight_rows::<T>(
17895            start..end,
17896            highlight_color
17897                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17898            Default::default(),
17899            cx,
17900        );
17901
17902        if self.buffer.read(cx).is_singleton() {
17903            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17904        }
17905    }
17906
17907    pub fn go_to_definition(
17908        &mut self,
17909        _: &GoToDefinition,
17910        window: &mut Window,
17911        cx: &mut Context<Self>,
17912    ) -> Task<Result<Navigated>> {
17913        let definition =
17914            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17915        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17916        cx.spawn_in(window, async move |editor, cx| {
17917            if definition.await? == Navigated::Yes {
17918                return Ok(Navigated::Yes);
17919            }
17920            match fallback_strategy {
17921                GoToDefinitionFallback::None => Ok(Navigated::No),
17922                GoToDefinitionFallback::FindAllReferences => {
17923                    match editor.update_in(cx, |editor, window, cx| {
17924                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17925                    })? {
17926                        Some(references) => references.await,
17927                        None => Ok(Navigated::No),
17928                    }
17929                }
17930            }
17931        })
17932    }
17933
17934    pub fn go_to_declaration(
17935        &mut self,
17936        _: &GoToDeclaration,
17937        window: &mut Window,
17938        cx: &mut Context<Self>,
17939    ) -> Task<Result<Navigated>> {
17940        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17941    }
17942
17943    pub fn go_to_declaration_split(
17944        &mut self,
17945        _: &GoToDeclaration,
17946        window: &mut Window,
17947        cx: &mut Context<Self>,
17948    ) -> Task<Result<Navigated>> {
17949        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17950    }
17951
17952    pub fn go_to_implementation(
17953        &mut self,
17954        _: &GoToImplementation,
17955        window: &mut Window,
17956        cx: &mut Context<Self>,
17957    ) -> Task<Result<Navigated>> {
17958        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17959    }
17960
17961    pub fn go_to_implementation_split(
17962        &mut self,
17963        _: &GoToImplementationSplit,
17964        window: &mut Window,
17965        cx: &mut Context<Self>,
17966    ) -> Task<Result<Navigated>> {
17967        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17968    }
17969
17970    pub fn go_to_type_definition(
17971        &mut self,
17972        _: &GoToTypeDefinition,
17973        window: &mut Window,
17974        cx: &mut Context<Self>,
17975    ) -> Task<Result<Navigated>> {
17976        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17977    }
17978
17979    pub fn go_to_definition_split(
17980        &mut self,
17981        _: &GoToDefinitionSplit,
17982        window: &mut Window,
17983        cx: &mut Context<Self>,
17984    ) -> Task<Result<Navigated>> {
17985        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17986    }
17987
17988    pub fn go_to_type_definition_split(
17989        &mut self,
17990        _: &GoToTypeDefinitionSplit,
17991        window: &mut Window,
17992        cx: &mut Context<Self>,
17993    ) -> Task<Result<Navigated>> {
17994        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17995    }
17996
17997    fn go_to_definition_of_kind(
17998        &mut self,
17999        kind: GotoDefinitionKind,
18000        split: bool,
18001        window: &mut Window,
18002        cx: &mut Context<Self>,
18003    ) -> Task<Result<Navigated>> {
18004        let Some(provider) = self.semantics_provider.clone() else {
18005            return Task::ready(Ok(Navigated::No));
18006        };
18007        let head = self
18008            .selections
18009            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18010            .head();
18011        let buffer = self.buffer.read(cx);
18012        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18013            return Task::ready(Ok(Navigated::No));
18014        };
18015        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18016            return Task::ready(Ok(Navigated::No));
18017        };
18018
18019        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18020
18021        cx.spawn_in(window, async move |editor, cx| {
18022            let Some(definitions) = definitions.await? else {
18023                return Ok(Navigated::No);
18024            };
18025            let navigated = editor
18026                .update_in(cx, |editor, window, cx| {
18027                    editor.navigate_to_hover_links(
18028                        Some(kind),
18029                        definitions
18030                            .into_iter()
18031                            .filter(|location| {
18032                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18033                            })
18034                            .map(HoverLink::Text)
18035                            .collect::<Vec<_>>(),
18036                        nav_entry,
18037                        split,
18038                        window,
18039                        cx,
18040                    )
18041                })?
18042                .await?;
18043            anyhow::Ok(navigated)
18044        })
18045    }
18046
18047    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18048        let selection = self.selections.newest_anchor();
18049        let head = selection.head();
18050        let tail = selection.tail();
18051
18052        let Some((buffer, start_position)) =
18053            self.buffer.read(cx).text_anchor_for_position(head, cx)
18054        else {
18055            return;
18056        };
18057
18058        let end_position = if head != tail {
18059            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18060                return;
18061            };
18062            Some(pos)
18063        } else {
18064            None
18065        };
18066
18067        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18068            let url = if let Some(end_pos) = end_position {
18069                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18070            } else {
18071                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18072            };
18073
18074            if let Some(url) = url {
18075                cx.update(|window, cx| {
18076                    if parse_zed_link(&url, cx).is_some() {
18077                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18078                    } else {
18079                        cx.open_url(&url);
18080                    }
18081                })?;
18082            }
18083
18084            anyhow::Ok(())
18085        });
18086
18087        url_finder.detach();
18088    }
18089
18090    pub fn open_selected_filename(
18091        &mut self,
18092        _: &OpenSelectedFilename,
18093        window: &mut Window,
18094        cx: &mut Context<Self>,
18095    ) {
18096        let Some(workspace) = self.workspace() else {
18097            return;
18098        };
18099
18100        let position = self.selections.newest_anchor().head();
18101
18102        let Some((buffer, buffer_position)) =
18103            self.buffer.read(cx).text_anchor_for_position(position, cx)
18104        else {
18105            return;
18106        };
18107
18108        let project = self.project.clone();
18109
18110        cx.spawn_in(window, async move |_, cx| {
18111            let result = find_file(&buffer, project, buffer_position, cx).await;
18112
18113            if let Some((_, path)) = result {
18114                workspace
18115                    .update_in(cx, |workspace, window, cx| {
18116                        workspace.open_resolved_path(path, window, cx)
18117                    })?
18118                    .await?;
18119            }
18120            anyhow::Ok(())
18121        })
18122        .detach();
18123    }
18124
18125    pub(crate) fn navigate_to_hover_links(
18126        &mut self,
18127        kind: Option<GotoDefinitionKind>,
18128        definitions: Vec<HoverLink>,
18129        origin: Option<NavigationEntry>,
18130        split: bool,
18131        window: &mut Window,
18132        cx: &mut Context<Editor>,
18133    ) -> Task<Result<Navigated>> {
18134        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18135        let mut first_url_or_file = None;
18136        let definitions: Vec<_> = definitions
18137            .into_iter()
18138            .filter_map(|def| match def {
18139                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18140                HoverLink::InlayHint(lsp_location, server_id) => {
18141                    let computation =
18142                        self.compute_target_location(lsp_location, server_id, window, cx);
18143                    Some(cx.background_spawn(computation))
18144                }
18145                HoverLink::Url(url) => {
18146                    first_url_or_file = Some(Either::Left(url));
18147                    None
18148                }
18149                HoverLink::File(path) => {
18150                    first_url_or_file = Some(Either::Right(path));
18151                    None
18152                }
18153            })
18154            .collect();
18155
18156        let workspace = self.workspace();
18157
18158        cx.spawn_in(window, async move |editor, cx| {
18159            let locations: Vec<Location> = future::join_all(definitions)
18160                .await
18161                .into_iter()
18162                .filter_map(|location| location.transpose())
18163                .collect::<Result<_>>()
18164                .context("location tasks")?;
18165            let mut locations = cx.update(|_, cx| {
18166                locations
18167                    .into_iter()
18168                    .map(|location| {
18169                        let buffer = location.buffer.read(cx);
18170                        (location.buffer, location.range.to_point(buffer))
18171                    })
18172                    .into_group_map()
18173            })?;
18174            let mut num_locations = 0;
18175            for ranges in locations.values_mut() {
18176                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18177                ranges.dedup();
18178                num_locations += ranges.len();
18179            }
18180
18181            if num_locations > 1 {
18182                let tab_kind = match kind {
18183                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18184                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18185                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18186                    Some(GotoDefinitionKind::Type) => "Types",
18187                };
18188                let title = editor
18189                    .update_in(cx, |_, _, cx| {
18190                        let target = locations
18191                            .iter()
18192                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18193                            .map(|(buffer, location)| {
18194                                buffer
18195                                    .read(cx)
18196                                    .text_for_range(location.clone())
18197                                    .collect::<String>()
18198                            })
18199                            .filter(|text| !text.contains('\n'))
18200                            .unique()
18201                            .take(3)
18202                            .join(", ");
18203                        if target.is_empty() {
18204                            tab_kind.to_owned()
18205                        } else {
18206                            format!("{tab_kind} for {target}")
18207                        }
18208                    })
18209                    .context("buffer title")?;
18210
18211                let Some(workspace) = workspace else {
18212                    return Ok(Navigated::No);
18213                };
18214
18215                let opened = workspace
18216                    .update_in(cx, |workspace, window, cx| {
18217                        let allow_preview = PreviewTabsSettings::get_global(cx)
18218                            .enable_preview_multibuffer_from_code_navigation;
18219                        if let Some((target_editor, target_pane)) =
18220                            Self::open_locations_in_multibuffer(
18221                                workspace,
18222                                locations,
18223                                title,
18224                                split,
18225                                allow_preview,
18226                                MultibufferSelectionMode::First,
18227                                window,
18228                                cx,
18229                            )
18230                        {
18231                            // We create our own nav history instead of using
18232                            // `target_editor.nav_history` because `nav_history`
18233                            // seems to be populated asynchronously when an item
18234                            // is added to a pane
18235                            let mut nav_history = target_pane
18236                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18237                            target_editor.update(cx, |editor, cx| {
18238                                let nav_data = editor
18239                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18240                                let target =
18241                                    Some(nav_history.navigation_entry(Some(
18242                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18243                                    )));
18244                                nav_history.push_tag(origin, target);
18245                            })
18246                        }
18247                    })
18248                    .is_ok();
18249
18250                anyhow::Ok(Navigated::from_bool(opened))
18251            } else if num_locations == 0 {
18252                // If there is one url or file, open it directly
18253                match first_url_or_file {
18254                    Some(Either::Left(url)) => {
18255                        cx.update(|window, cx| {
18256                            if parse_zed_link(&url, cx).is_some() {
18257                                window
18258                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18259                            } else {
18260                                cx.open_url(&url);
18261                            }
18262                        })?;
18263                        Ok(Navigated::Yes)
18264                    }
18265                    Some(Either::Right(path)) => {
18266                        // TODO(andrew): respect preview tab settings
18267                        //               `enable_keep_preview_on_code_navigation` and
18268                        //               `enable_preview_file_from_code_navigation`
18269                        let Some(workspace) = workspace else {
18270                            return Ok(Navigated::No);
18271                        };
18272                        workspace
18273                            .update_in(cx, |workspace, window, cx| {
18274                                workspace.open_resolved_path(path, window, cx)
18275                            })?
18276                            .await?;
18277                        Ok(Navigated::Yes)
18278                    }
18279                    None => Ok(Navigated::No),
18280                }
18281            } else {
18282                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18283                let target_range = target_ranges.first().unwrap().clone();
18284
18285                editor.update_in(cx, |editor, window, cx| {
18286                    let range = editor.range_for_match(&target_range);
18287                    let range = collapse_multiline_range(range);
18288
18289                    if !split
18290                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18291                    {
18292                        editor.go_to_singleton_buffer_range(range, window, cx);
18293
18294                        let target =
18295                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18296                        if let Some(mut nav_history) = editor.nav_history.clone() {
18297                            nav_history.push_tag(origin, target);
18298                        }
18299                    } else {
18300                        let Some(workspace) = workspace else {
18301                            return Navigated::No;
18302                        };
18303                        let pane = workspace.read(cx).active_pane().clone();
18304                        window.defer(cx, move |window, cx| {
18305                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18306                                workspace.update(cx, |workspace, cx| {
18307                                    let pane = if split {
18308                                        workspace.adjacent_pane(window, cx)
18309                                    } else {
18310                                        workspace.active_pane().clone()
18311                                    };
18312
18313                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18314                                    let keep_old_preview = preview_tabs_settings
18315                                        .enable_keep_preview_on_code_navigation;
18316                                    let allow_new_preview = preview_tabs_settings
18317                                        .enable_preview_file_from_code_navigation;
18318
18319                                    let editor = workspace.open_project_item(
18320                                        pane.clone(),
18321                                        target_buffer.clone(),
18322                                        true,
18323                                        true,
18324                                        keep_old_preview,
18325                                        allow_new_preview,
18326                                        window,
18327                                        cx,
18328                                    );
18329                                    (editor, pane)
18330                                });
18331                            // We create our own nav history instead of using
18332                            // `target_editor.nav_history` because `nav_history`
18333                            // seems to be populated asynchronously when an item
18334                            // is added to a pane
18335                            let mut nav_history = target_pane
18336                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18337                            target_editor.update(cx, |target_editor, cx| {
18338                                // When selecting a definition in a different buffer, disable the nav history
18339                                // to avoid creating a history entry at the previous cursor location.
18340                                pane.update(cx, |pane, _| pane.disable_history());
18341                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18342
18343                                let nav_data = target_editor.navigation_data(
18344                                    target_editor.selections.newest_anchor().head(),
18345                                    cx,
18346                                );
18347                                let target =
18348                                    Some(nav_history.navigation_entry(Some(
18349                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18350                                    )));
18351                                nav_history.push_tag(origin, target);
18352                                pane.update(cx, |pane, _| pane.enable_history());
18353                            });
18354                        });
18355                    }
18356                    Navigated::Yes
18357                })
18358            }
18359        })
18360    }
18361
18362    fn compute_target_location(
18363        &self,
18364        lsp_location: lsp::Location,
18365        server_id: LanguageServerId,
18366        window: &mut Window,
18367        cx: &mut Context<Self>,
18368    ) -> Task<anyhow::Result<Option<Location>>> {
18369        let Some(project) = self.project.clone() else {
18370            return Task::ready(Ok(None));
18371        };
18372
18373        cx.spawn_in(window, async move |editor, cx| {
18374            let location_task = editor.update(cx, |_, cx| {
18375                project.update(cx, |project, cx| {
18376                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18377                })
18378            })?;
18379            let location = Some({
18380                let target_buffer_handle = location_task.await.context("open local buffer")?;
18381                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18382                    let target_start = target_buffer
18383                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18384                    let target_end = target_buffer
18385                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18386                    target_buffer.anchor_after(target_start)
18387                        ..target_buffer.anchor_before(target_end)
18388                });
18389                Location {
18390                    buffer: target_buffer_handle,
18391                    range,
18392                }
18393            });
18394            Ok(location)
18395        })
18396    }
18397
18398    fn go_to_next_reference(
18399        &mut self,
18400        _: &GoToNextReference,
18401        window: &mut Window,
18402        cx: &mut Context<Self>,
18403    ) {
18404        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18405        if let Some(task) = task {
18406            task.detach();
18407        };
18408    }
18409
18410    fn go_to_prev_reference(
18411        &mut self,
18412        _: &GoToPreviousReference,
18413        window: &mut Window,
18414        cx: &mut Context<Self>,
18415    ) {
18416        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18417        if let Some(task) = task {
18418            task.detach();
18419        };
18420    }
18421
18422    pub fn go_to_reference_before_or_after_position(
18423        &mut self,
18424        direction: Direction,
18425        count: usize,
18426        window: &mut Window,
18427        cx: &mut Context<Self>,
18428    ) -> Option<Task<Result<()>>> {
18429        let selection = self.selections.newest_anchor();
18430        let head = selection.head();
18431
18432        let multi_buffer = self.buffer.read(cx);
18433
18434        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18435        let workspace = self.workspace()?;
18436        let project = workspace.read(cx).project().clone();
18437        let references =
18438            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18439        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18440            let Some(locations) = references.await? else {
18441                return Ok(());
18442            };
18443
18444            if locations.is_empty() {
18445                // totally normal - the cursor may be on something which is not
18446                // a symbol (e.g. a keyword)
18447                log::info!("no references found under cursor");
18448                return Ok(());
18449            }
18450
18451            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18452
18453            let (locations, current_location_index) =
18454                multi_buffer.update(cx, |multi_buffer, cx| {
18455                    let mut locations = locations
18456                        .into_iter()
18457                        .filter_map(|loc| {
18458                            let start = multi_buffer.buffer_anchor_to_anchor(
18459                                &loc.buffer,
18460                                loc.range.start,
18461                                cx,
18462                            )?;
18463                            let end = multi_buffer.buffer_anchor_to_anchor(
18464                                &loc.buffer,
18465                                loc.range.end,
18466                                cx,
18467                            )?;
18468                            Some(start..end)
18469                        })
18470                        .collect::<Vec<_>>();
18471
18472                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18473                    // There is an O(n) implementation, but given this list will be
18474                    // small (usually <100 items), the extra O(log(n)) factor isn't
18475                    // worth the (surprisingly large amount of) extra complexity.
18476                    locations
18477                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18478
18479                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18480
18481                    let current_location_index = locations.iter().position(|loc| {
18482                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18483                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18484                    });
18485
18486                    (locations, current_location_index)
18487                });
18488
18489            let Some(current_location_index) = current_location_index else {
18490                // This indicates something has gone wrong, because we already
18491                // handle the "no references" case above
18492                log::error!(
18493                    "failed to find current reference under cursor. Total references: {}",
18494                    locations.len()
18495                );
18496                return Ok(());
18497            };
18498
18499            let destination_location_index = match direction {
18500                Direction::Next => (current_location_index + count) % locations.len(),
18501                Direction::Prev => {
18502                    (current_location_index + locations.len() - count % locations.len())
18503                        % locations.len()
18504                }
18505            };
18506
18507            // TODO(cameron): is this needed?
18508            // the thinking is to avoid "jumping to the current location" (avoid
18509            // polluting "jumplist" in vim terms)
18510            if current_location_index == destination_location_index {
18511                return Ok(());
18512            }
18513
18514            let Range { start, end } = locations[destination_location_index];
18515
18516            editor.update_in(cx, |editor, window, cx| {
18517                let effects = SelectionEffects::default();
18518
18519                editor.unfold_ranges(&[start..end], false, false, cx);
18520                editor.change_selections(effects, window, cx, |s| {
18521                    s.select_ranges([start..start]);
18522                });
18523            })?;
18524
18525            Ok(())
18526        }))
18527    }
18528
18529    pub fn find_all_references(
18530        &mut self,
18531        action: &FindAllReferences,
18532        window: &mut Window,
18533        cx: &mut Context<Self>,
18534    ) -> Option<Task<Result<Navigated>>> {
18535        let always_open_multibuffer = action.always_open_multibuffer;
18536        let selection = self.selections.newest_anchor();
18537        let multi_buffer = self.buffer.read(cx);
18538        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18539        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18540        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18541        let head = selection_offset.head();
18542
18543        let head_anchor = multi_buffer_snapshot.anchor_at(
18544            head,
18545            if head < selection_offset.tail() {
18546                Bias::Right
18547            } else {
18548                Bias::Left
18549            },
18550        );
18551
18552        match self
18553            .find_all_references_task_sources
18554            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18555        {
18556            Ok(_) => {
18557                log::info!(
18558                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18559                );
18560                return None;
18561            }
18562            Err(i) => {
18563                self.find_all_references_task_sources.insert(i, head_anchor);
18564            }
18565        }
18566
18567        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18568        let workspace = self.workspace()?;
18569        let project = workspace.read(cx).project().clone();
18570        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18571        Some(cx.spawn_in(window, async move |editor, cx| {
18572            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18573                if let Ok(i) = editor
18574                    .find_all_references_task_sources
18575                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18576                {
18577                    editor.find_all_references_task_sources.remove(i);
18578                }
18579            });
18580
18581            let Some(locations) = references.await? else {
18582                return anyhow::Ok(Navigated::No);
18583            };
18584            let mut locations = cx.update(|_, cx| {
18585                locations
18586                    .into_iter()
18587                    .map(|location| {
18588                        let buffer = location.buffer.read(cx);
18589                        (location.buffer, location.range.to_point(buffer))
18590                    })
18591                    // if special-casing the single-match case, remove ranges
18592                    // that intersect current selection
18593                    .filter(|(location_buffer, location)| {
18594                        if always_open_multibuffer || &buffer != location_buffer {
18595                            return true;
18596                        }
18597
18598                        !location.contains_inclusive(&selection_point.range())
18599                    })
18600                    .into_group_map()
18601            })?;
18602            if locations.is_empty() {
18603                return anyhow::Ok(Navigated::No);
18604            }
18605            for ranges in locations.values_mut() {
18606                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18607                ranges.dedup();
18608            }
18609            let mut num_locations = 0;
18610            for ranges in locations.values_mut() {
18611                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18612                ranges.dedup();
18613                num_locations += ranges.len();
18614            }
18615
18616            if num_locations == 1 && !always_open_multibuffer {
18617                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18618                let target_range = target_ranges.first().unwrap().clone();
18619
18620                return editor.update_in(cx, |editor, window, cx| {
18621                    let range = target_range.to_point(target_buffer.read(cx));
18622                    let range = editor.range_for_match(&range);
18623                    let range = range.start..range.start;
18624
18625                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18626                        editor.go_to_singleton_buffer_range(range, window, cx);
18627                    } else {
18628                        let pane = workspace.read(cx).active_pane().clone();
18629                        window.defer(cx, move |window, cx| {
18630                            let target_editor: Entity<Self> =
18631                                workspace.update(cx, |workspace, cx| {
18632                                    let pane = workspace.active_pane().clone();
18633
18634                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18635                                    let keep_old_preview = preview_tabs_settings
18636                                        .enable_keep_preview_on_code_navigation;
18637                                    let allow_new_preview = preview_tabs_settings
18638                                        .enable_preview_file_from_code_navigation;
18639
18640                                    workspace.open_project_item(
18641                                        pane,
18642                                        target_buffer.clone(),
18643                                        true,
18644                                        true,
18645                                        keep_old_preview,
18646                                        allow_new_preview,
18647                                        window,
18648                                        cx,
18649                                    )
18650                                });
18651                            target_editor.update(cx, |target_editor, cx| {
18652                                // When selecting a definition in a different buffer, disable the nav history
18653                                // to avoid creating a history entry at the previous cursor location.
18654                                pane.update(cx, |pane, _| pane.disable_history());
18655                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18656                                pane.update(cx, |pane, _| pane.enable_history());
18657                            });
18658                        });
18659                    }
18660                    Navigated::No
18661                });
18662            }
18663
18664            workspace.update_in(cx, |workspace, window, cx| {
18665                let target = locations
18666                    .iter()
18667                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18668                    .map(|(buffer, location)| {
18669                        buffer
18670                            .read(cx)
18671                            .text_for_range(location.clone())
18672                            .collect::<String>()
18673                    })
18674                    .filter(|text| !text.contains('\n'))
18675                    .unique()
18676                    .take(3)
18677                    .join(", ");
18678                let title = if target.is_empty() {
18679                    "References".to_owned()
18680                } else {
18681                    format!("References to {target}")
18682                };
18683                let allow_preview = PreviewTabsSettings::get_global(cx)
18684                    .enable_preview_multibuffer_from_code_navigation;
18685                Self::open_locations_in_multibuffer(
18686                    workspace,
18687                    locations,
18688                    title,
18689                    false,
18690                    allow_preview,
18691                    MultibufferSelectionMode::First,
18692                    window,
18693                    cx,
18694                );
18695                Navigated::Yes
18696            })
18697        }))
18698    }
18699
18700    /// Opens a multibuffer with the given project locations in it.
18701    pub fn open_locations_in_multibuffer(
18702        workspace: &mut Workspace,
18703        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18704        title: String,
18705        split: bool,
18706        allow_preview: bool,
18707        multibuffer_selection_mode: MultibufferSelectionMode,
18708        window: &mut Window,
18709        cx: &mut Context<Workspace>,
18710    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18711        if locations.is_empty() {
18712            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18713            return None;
18714        }
18715
18716        let capability = workspace.project().read(cx).capability();
18717        let mut ranges = <Vec<Range<Anchor>>>::new();
18718
18719        // a key to find existing multibuffer editors with the same set of locations
18720        // to prevent us from opening more and more multibuffer tabs for searches and the like
18721        let mut key = (title.clone(), vec![]);
18722        let excerpt_buffer = cx.new(|cx| {
18723            let key = &mut key.1;
18724            let mut multibuffer = MultiBuffer::new(capability);
18725            for (buffer, mut ranges_for_buffer) in locations {
18726                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18727                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18728                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18729                    PathKey::for_buffer(&buffer, cx),
18730                    buffer.clone(),
18731                    ranges_for_buffer,
18732                    multibuffer_context_lines(cx),
18733                    cx,
18734                );
18735                ranges.extend(new_ranges)
18736            }
18737
18738            multibuffer.with_title(title)
18739        });
18740        let existing = workspace.active_pane().update(cx, |pane, cx| {
18741            pane.items()
18742                .filter_map(|item| item.downcast::<Editor>())
18743                .find(|editor| {
18744                    editor
18745                        .read(cx)
18746                        .lookup_key
18747                        .as_ref()
18748                        .and_then(|it| {
18749                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18750                        })
18751                        .is_some_and(|it| *it == key)
18752                })
18753        });
18754        let was_existing = existing.is_some();
18755        let editor = existing.unwrap_or_else(|| {
18756            cx.new(|cx| {
18757                let mut editor = Editor::for_multibuffer(
18758                    excerpt_buffer,
18759                    Some(workspace.project().clone()),
18760                    window,
18761                    cx,
18762                );
18763                editor.lookup_key = Some(Box::new(key));
18764                editor
18765            })
18766        });
18767        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18768            MultibufferSelectionMode::First => {
18769                if let Some(first_range) = ranges.first() {
18770                    editor.change_selections(
18771                        SelectionEffects::no_scroll(),
18772                        window,
18773                        cx,
18774                        |selections| {
18775                            selections.clear_disjoint();
18776                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18777                        },
18778                    );
18779                }
18780                editor.highlight_background(
18781                    HighlightKey::Editor,
18782                    &ranges,
18783                    |_, theme| theme.colors().editor_highlighted_line_background,
18784                    cx,
18785                );
18786            }
18787            MultibufferSelectionMode::All => {
18788                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18789                    selections.clear_disjoint();
18790                    selections.select_anchor_ranges(ranges);
18791                });
18792            }
18793        });
18794
18795        let item = Box::new(editor.clone());
18796
18797        let pane = if split {
18798            workspace.adjacent_pane(window, cx)
18799        } else {
18800            workspace.active_pane().clone()
18801        };
18802        let activate_pane = split;
18803
18804        let mut destination_index = None;
18805        pane.update(cx, |pane, cx| {
18806            if allow_preview && !was_existing {
18807                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18808            }
18809            if was_existing && !allow_preview {
18810                pane.unpreview_item_if_preview(item.item_id());
18811            }
18812            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18813        });
18814
18815        Some((editor, pane))
18816    }
18817
18818    pub fn rename(
18819        &mut self,
18820        _: &Rename,
18821        window: &mut Window,
18822        cx: &mut Context<Self>,
18823    ) -> Option<Task<Result<()>>> {
18824        use language::ToOffset as _;
18825
18826        let provider = self.semantics_provider.clone()?;
18827        let selection = self.selections.newest_anchor().clone();
18828        let (cursor_buffer, cursor_buffer_position) = self
18829            .buffer
18830            .read(cx)
18831            .text_anchor_for_position(selection.head(), cx)?;
18832        let (tail_buffer, cursor_buffer_position_end) = self
18833            .buffer
18834            .read(cx)
18835            .text_anchor_for_position(selection.tail(), cx)?;
18836        if tail_buffer != cursor_buffer {
18837            return None;
18838        }
18839
18840        let snapshot = cursor_buffer.read(cx).snapshot();
18841        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18842        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18843        let prepare_rename = provider
18844            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18845            .unwrap_or_else(|| Task::ready(Ok(None)));
18846        drop(snapshot);
18847
18848        Some(cx.spawn_in(window, async move |this, cx| {
18849            let rename_range = if let Some(range) = prepare_rename.await? {
18850                Some(range)
18851            } else {
18852                this.update(cx, |this, cx| {
18853                    let buffer = this.buffer.read(cx).snapshot(cx);
18854                    let mut buffer_highlights = this
18855                        .document_highlights_for_position(selection.head(), &buffer)
18856                        .filter(|highlight| {
18857                            highlight.start.excerpt_id == selection.head().excerpt_id
18858                                && highlight.end.excerpt_id == selection.head().excerpt_id
18859                        });
18860                    buffer_highlights
18861                        .next()
18862                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18863                })?
18864            };
18865            if let Some(rename_range) = rename_range {
18866                this.update_in(cx, |this, window, cx| {
18867                    let snapshot = cursor_buffer.read(cx).snapshot();
18868                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18869                    let cursor_offset_in_rename_range =
18870                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18871                    let cursor_offset_in_rename_range_end =
18872                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18873
18874                    this.take_rename(false, window, cx);
18875                    let buffer = this.buffer.read(cx).read(cx);
18876                    let cursor_offset = selection.head().to_offset(&buffer);
18877                    let rename_start =
18878                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18879                    let rename_end = rename_start + rename_buffer_range.len();
18880                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18881                    let mut old_highlight_id = None;
18882                    let old_name: Arc<str> = buffer
18883                        .chunks(rename_start..rename_end, true)
18884                        .map(|chunk| {
18885                            if old_highlight_id.is_none() {
18886                                old_highlight_id = chunk.syntax_highlight_id;
18887                            }
18888                            chunk.text
18889                        })
18890                        .collect::<String>()
18891                        .into();
18892
18893                    drop(buffer);
18894
18895                    // Position the selection in the rename editor so that it matches the current selection.
18896                    this.show_local_selections = false;
18897                    let rename_editor = cx.new(|cx| {
18898                        let mut editor = Editor::single_line(window, cx);
18899                        editor.buffer.update(cx, |buffer, cx| {
18900                            buffer.edit(
18901                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18902                                None,
18903                                cx,
18904                            )
18905                        });
18906                        let cursor_offset_in_rename_range =
18907                            MultiBufferOffset(cursor_offset_in_rename_range);
18908                        let cursor_offset_in_rename_range_end =
18909                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18910                        let rename_selection_range = match cursor_offset_in_rename_range
18911                            .cmp(&cursor_offset_in_rename_range_end)
18912                        {
18913                            Ordering::Equal => {
18914                                editor.select_all(&SelectAll, window, cx);
18915                                return editor;
18916                            }
18917                            Ordering::Less => {
18918                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18919                            }
18920                            Ordering::Greater => {
18921                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18922                            }
18923                        };
18924                        if rename_selection_range.end.0 > old_name.len() {
18925                            editor.select_all(&SelectAll, window, cx);
18926                        } else {
18927                            editor.change_selections(Default::default(), window, cx, |s| {
18928                                s.select_ranges([rename_selection_range]);
18929                            });
18930                        }
18931                        editor
18932                    });
18933                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18934                        if e == &EditorEvent::Focused {
18935                            cx.emit(EditorEvent::FocusedIn)
18936                        }
18937                    })
18938                    .detach();
18939
18940                    let write_highlights =
18941                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18942                    let read_highlights =
18943                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18944                    let ranges = write_highlights
18945                        .iter()
18946                        .flat_map(|(_, ranges)| ranges.iter())
18947                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18948                        .cloned()
18949                        .collect();
18950
18951                    this.highlight_text(
18952                        HighlightKey::Rename,
18953                        ranges,
18954                        HighlightStyle {
18955                            fade_out: Some(0.6),
18956                            ..Default::default()
18957                        },
18958                        cx,
18959                    );
18960                    let rename_focus_handle = rename_editor.focus_handle(cx);
18961                    window.focus(&rename_focus_handle, cx);
18962                    let block_id = this.insert_blocks(
18963                        [BlockProperties {
18964                            style: BlockStyle::Flex,
18965                            placement: BlockPlacement::Below(range.start),
18966                            height: Some(1),
18967                            render: Arc::new({
18968                                let rename_editor = rename_editor.clone();
18969                                move |cx: &mut BlockContext| {
18970                                    let mut text_style = cx.editor_style.text.clone();
18971                                    if let Some(highlight_style) = old_highlight_id
18972                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18973                                    {
18974                                        text_style = text_style.highlight(highlight_style);
18975                                    }
18976                                    div()
18977                                        .block_mouse_except_scroll()
18978                                        .pl(cx.anchor_x)
18979                                        .child(EditorElement::new(
18980                                            &rename_editor,
18981                                            EditorStyle {
18982                                                background: cx.theme().system().transparent,
18983                                                local_player: cx.editor_style.local_player,
18984                                                text: text_style,
18985                                                scrollbar_width: cx.editor_style.scrollbar_width,
18986                                                syntax: cx.editor_style.syntax.clone(),
18987                                                status: cx.editor_style.status.clone(),
18988                                                inlay_hints_style: HighlightStyle {
18989                                                    font_weight: Some(FontWeight::BOLD),
18990                                                    ..make_inlay_hints_style(cx.app)
18991                                                },
18992                                                edit_prediction_styles: make_suggestion_styles(
18993                                                    cx.app,
18994                                                ),
18995                                                ..EditorStyle::default()
18996                                            },
18997                                        ))
18998                                        .into_any_element()
18999                                }
19000                            }),
19001                            priority: 0,
19002                        }],
19003                        Some(Autoscroll::fit()),
19004                        cx,
19005                    )[0];
19006                    this.pending_rename = Some(RenameState {
19007                        range,
19008                        old_name,
19009                        editor: rename_editor,
19010                        block_id,
19011                    });
19012                })?;
19013            }
19014
19015            Ok(())
19016        }))
19017    }
19018
19019    pub fn confirm_rename(
19020        &mut self,
19021        _: &ConfirmRename,
19022        window: &mut Window,
19023        cx: &mut Context<Self>,
19024    ) -> Option<Task<Result<()>>> {
19025        let rename = self.take_rename(false, window, cx)?;
19026        let workspace = self.workspace()?.downgrade();
19027        let (buffer, start) = self
19028            .buffer
19029            .read(cx)
19030            .text_anchor_for_position(rename.range.start, cx)?;
19031        let (end_buffer, _) = self
19032            .buffer
19033            .read(cx)
19034            .text_anchor_for_position(rename.range.end, cx)?;
19035        if buffer != end_buffer {
19036            return None;
19037        }
19038
19039        let old_name = rename.old_name;
19040        let new_name = rename.editor.read(cx).text(cx);
19041
19042        let rename = self.semantics_provider.as_ref()?.perform_rename(
19043            &buffer,
19044            start,
19045            new_name.clone(),
19046            cx,
19047        )?;
19048
19049        Some(cx.spawn_in(window, async move |editor, cx| {
19050            let project_transaction = rename.await?;
19051            Self::open_project_transaction(
19052                &editor,
19053                workspace,
19054                project_transaction,
19055                format!("Rename: {}{}", old_name, new_name),
19056                cx,
19057            )
19058            .await?;
19059
19060            editor.update(cx, |editor, cx| {
19061                editor.refresh_document_highlights(cx);
19062            })?;
19063            Ok(())
19064        }))
19065    }
19066
19067    fn take_rename(
19068        &mut self,
19069        moving_cursor: bool,
19070        window: &mut Window,
19071        cx: &mut Context<Self>,
19072    ) -> Option<RenameState> {
19073        let rename = self.pending_rename.take()?;
19074        if rename.editor.focus_handle(cx).is_focused(window) {
19075            window.focus(&self.focus_handle, cx);
19076        }
19077
19078        self.remove_blocks(
19079            [rename.block_id].into_iter().collect(),
19080            Some(Autoscroll::fit()),
19081            cx,
19082        );
19083        self.clear_highlights(HighlightKey::Rename, cx);
19084        self.show_local_selections = true;
19085
19086        if moving_cursor {
19087            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19088                editor
19089                    .selections
19090                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19091                    .head()
19092            });
19093
19094            // Update the selection to match the position of the selection inside
19095            // the rename editor.
19096            let snapshot = self.buffer.read(cx).read(cx);
19097            let rename_range = rename.range.to_offset(&snapshot);
19098            let cursor_in_editor = snapshot
19099                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19100                .min(rename_range.end);
19101            drop(snapshot);
19102
19103            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19104                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19105            });
19106        } else {
19107            self.refresh_document_highlights(cx);
19108        }
19109
19110        Some(rename)
19111    }
19112
19113    pub fn pending_rename(&self) -> Option<&RenameState> {
19114        self.pending_rename.as_ref()
19115    }
19116
19117    fn format(
19118        &mut self,
19119        _: &Format,
19120        window: &mut Window,
19121        cx: &mut Context<Self>,
19122    ) -> Option<Task<Result<()>>> {
19123        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19124
19125        let project = match &self.project {
19126            Some(project) => project.clone(),
19127            None => return None,
19128        };
19129
19130        Some(self.perform_format(
19131            project,
19132            FormatTrigger::Manual,
19133            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19134            window,
19135            cx,
19136        ))
19137    }
19138
19139    fn format_selections(
19140        &mut self,
19141        _: &FormatSelections,
19142        window: &mut Window,
19143        cx: &mut Context<Self>,
19144    ) -> Option<Task<Result<()>>> {
19145        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19146
19147        let project = match &self.project {
19148            Some(project) => project.clone(),
19149            None => return None,
19150        };
19151
19152        let ranges = self
19153            .selections
19154            .all_adjusted(&self.display_snapshot(cx))
19155            .into_iter()
19156            .map(|selection| selection.range())
19157            .collect_vec();
19158
19159        Some(self.perform_format(
19160            project,
19161            FormatTrigger::Manual,
19162            FormatTarget::Ranges(ranges),
19163            window,
19164            cx,
19165        ))
19166    }
19167
19168    fn perform_format(
19169        &mut self,
19170        project: Entity<Project>,
19171        trigger: FormatTrigger,
19172        target: FormatTarget,
19173        window: &mut Window,
19174        cx: &mut Context<Self>,
19175    ) -> Task<Result<()>> {
19176        let buffer = self.buffer.clone();
19177        let (buffers, target) = match target {
19178            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19179            FormatTarget::Ranges(selection_ranges) => {
19180                let multi_buffer = buffer.read(cx);
19181                let snapshot = multi_buffer.read(cx);
19182                let mut buffers = HashSet::default();
19183                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19184                    BTreeMap::new();
19185                for selection_range in selection_ranges {
19186                    for (buffer, buffer_range, _) in
19187                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19188                    {
19189                        let buffer_id = buffer.remote_id();
19190                        let start = buffer.anchor_before(buffer_range.start);
19191                        let end = buffer.anchor_after(buffer_range.end);
19192                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19193                        buffer_id_to_ranges
19194                            .entry(buffer_id)
19195                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19196                            .or_insert_with(|| vec![start..end]);
19197                    }
19198                }
19199                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19200            }
19201        };
19202
19203        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19204        let selections_prev = transaction_id_prev
19205            .and_then(|transaction_id_prev| {
19206                // default to selections as they were after the last edit, if we have them,
19207                // instead of how they are now.
19208                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19209                // will take you back to where you made the last edit, instead of staying where you scrolled
19210                self.selection_history
19211                    .transaction(transaction_id_prev)
19212                    .map(|t| t.0.clone())
19213            })
19214            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19215
19216        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19217        let format = project.update(cx, |project, cx| {
19218            project.format(buffers, target, true, trigger, cx)
19219        });
19220
19221        cx.spawn_in(window, async move |editor, cx| {
19222            let transaction = futures::select_biased! {
19223                transaction = format.log_err().fuse() => transaction,
19224                () = timeout => {
19225                    log::warn!("timed out waiting for formatting");
19226                    None
19227                }
19228            };
19229
19230            buffer.update(cx, |buffer, cx| {
19231                if let Some(transaction) = transaction
19232                    && !buffer.is_singleton()
19233                {
19234                    buffer.push_transaction(&transaction.0, cx);
19235                }
19236                cx.notify();
19237            });
19238
19239            if let Some(transaction_id_now) =
19240                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19241            {
19242                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19243                if has_new_transaction {
19244                    editor
19245                        .update(cx, |editor, _| {
19246                            editor
19247                                .selection_history
19248                                .insert_transaction(transaction_id_now, selections_prev);
19249                        })
19250                        .ok();
19251                }
19252            }
19253
19254            Ok(())
19255        })
19256    }
19257
19258    fn organize_imports(
19259        &mut self,
19260        _: &OrganizeImports,
19261        window: &mut Window,
19262        cx: &mut Context<Self>,
19263    ) -> Option<Task<Result<()>>> {
19264        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19265        let project = match &self.project {
19266            Some(project) => project.clone(),
19267            None => return None,
19268        };
19269        Some(self.perform_code_action_kind(
19270            project,
19271            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19272            window,
19273            cx,
19274        ))
19275    }
19276
19277    fn perform_code_action_kind(
19278        &mut self,
19279        project: Entity<Project>,
19280        kind: CodeActionKind,
19281        window: &mut Window,
19282        cx: &mut Context<Self>,
19283    ) -> Task<Result<()>> {
19284        let buffer = self.buffer.clone();
19285        let buffers = buffer.read(cx).all_buffers();
19286        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19287        let apply_action = project.update(cx, |project, cx| {
19288            project.apply_code_action_kind(buffers, kind, true, cx)
19289        });
19290        cx.spawn_in(window, async move |_, cx| {
19291            let transaction = futures::select_biased! {
19292                () = timeout => {
19293                    log::warn!("timed out waiting for executing code action");
19294                    None
19295                }
19296                transaction = apply_action.log_err().fuse() => transaction,
19297            };
19298            buffer.update(cx, |buffer, cx| {
19299                // check if we need this
19300                if let Some(transaction) = transaction
19301                    && !buffer.is_singleton()
19302                {
19303                    buffer.push_transaction(&transaction.0, cx);
19304                }
19305                cx.notify();
19306            });
19307            Ok(())
19308        })
19309    }
19310
19311    pub fn restart_language_server(
19312        &mut self,
19313        _: &RestartLanguageServer,
19314        _: &mut Window,
19315        cx: &mut Context<Self>,
19316    ) {
19317        if let Some(project) = self.project.clone() {
19318            self.buffer.update(cx, |multi_buffer, cx| {
19319                project.update(cx, |project, cx| {
19320                    project.restart_language_servers_for_buffers(
19321                        multi_buffer.all_buffers().into_iter().collect(),
19322                        HashSet::default(),
19323                        cx,
19324                    );
19325                });
19326            })
19327        }
19328    }
19329
19330    pub fn stop_language_server(
19331        &mut self,
19332        _: &StopLanguageServer,
19333        _: &mut Window,
19334        cx: &mut Context<Self>,
19335    ) {
19336        if let Some(project) = self.project.clone() {
19337            self.buffer.update(cx, |multi_buffer, cx| {
19338                project.update(cx, |project, cx| {
19339                    project.stop_language_servers_for_buffers(
19340                        multi_buffer.all_buffers().into_iter().collect(),
19341                        HashSet::default(),
19342                        cx,
19343                    );
19344                });
19345            });
19346        }
19347    }
19348
19349    fn cancel_language_server_work(
19350        workspace: &mut Workspace,
19351        _: &actions::CancelLanguageServerWork,
19352        _: &mut Window,
19353        cx: &mut Context<Workspace>,
19354    ) {
19355        let project = workspace.project();
19356        let buffers = workspace
19357            .active_item(cx)
19358            .and_then(|item| item.act_as::<Editor>(cx))
19359            .map_or(HashSet::default(), |editor| {
19360                editor.read(cx).buffer.read(cx).all_buffers()
19361            });
19362        project.update(cx, |project, cx| {
19363            project.cancel_language_server_work_for_buffers(buffers, cx);
19364        });
19365    }
19366
19367    fn show_character_palette(
19368        &mut self,
19369        _: &ShowCharacterPalette,
19370        window: &mut Window,
19371        _: &mut Context<Self>,
19372    ) {
19373        window.show_character_palette();
19374    }
19375
19376    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19377        if !self.diagnostics_enabled() {
19378            return;
19379        }
19380
19381        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19382            let buffer = self.buffer.read(cx).snapshot(cx);
19383            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19384            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19385            let is_valid = buffer
19386                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19387                .any(|entry| {
19388                    entry.diagnostic.is_primary
19389                        && !entry.range.is_empty()
19390                        && entry.range.start == primary_range_start
19391                        && entry.diagnostic.message == active_diagnostics.active_message
19392                });
19393
19394            if !is_valid {
19395                self.dismiss_diagnostics(cx);
19396            }
19397        }
19398    }
19399
19400    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19401        match &self.active_diagnostics {
19402            ActiveDiagnostic::Group(group) => Some(group),
19403            _ => None,
19404        }
19405    }
19406
19407    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19408        if !self.diagnostics_enabled() {
19409            return;
19410        }
19411        self.dismiss_diagnostics(cx);
19412        self.active_diagnostics = ActiveDiagnostic::All;
19413    }
19414
19415    fn activate_diagnostics(
19416        &mut self,
19417        buffer_id: BufferId,
19418        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19419        window: &mut Window,
19420        cx: &mut Context<Self>,
19421    ) {
19422        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19423            return;
19424        }
19425        self.dismiss_diagnostics(cx);
19426        let snapshot = self.snapshot(window, cx);
19427        let buffer = self.buffer.read(cx).snapshot(cx);
19428        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19429            return;
19430        };
19431
19432        let diagnostic_group = buffer
19433            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19434            .collect::<Vec<_>>();
19435
19436        let language_registry = self
19437            .project()
19438            .map(|project| project.read(cx).languages().clone());
19439
19440        let blocks = renderer.render_group(
19441            diagnostic_group,
19442            buffer_id,
19443            snapshot,
19444            cx.weak_entity(),
19445            language_registry,
19446            cx,
19447        );
19448
19449        let blocks = self.display_map.update(cx, |display_map, cx| {
19450            display_map.insert_blocks(blocks, cx).into_iter().collect()
19451        });
19452        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19453            active_range: buffer.anchor_before(diagnostic.range.start)
19454                ..buffer.anchor_after(diagnostic.range.end),
19455            active_message: diagnostic.diagnostic.message.clone(),
19456            group_id: diagnostic.diagnostic.group_id,
19457            blocks,
19458        });
19459        cx.notify();
19460    }
19461
19462    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19463        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19464            return;
19465        };
19466
19467        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19468        if let ActiveDiagnostic::Group(group) = prev {
19469            self.display_map.update(cx, |display_map, cx| {
19470                display_map.remove_blocks(group.blocks, cx);
19471            });
19472            cx.notify();
19473        }
19474    }
19475
19476    /// Disable inline diagnostics rendering for this editor.
19477    pub fn disable_inline_diagnostics(&mut self) {
19478        self.inline_diagnostics_enabled = false;
19479        self.inline_diagnostics_update = Task::ready(());
19480        self.inline_diagnostics.clear();
19481    }
19482
19483    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19484        self.diagnostics_enabled = false;
19485        self.dismiss_diagnostics(cx);
19486        self.inline_diagnostics_update = Task::ready(());
19487        self.inline_diagnostics.clear();
19488    }
19489
19490    pub fn disable_word_completions(&mut self) {
19491        self.word_completions_enabled = false;
19492    }
19493
19494    pub fn diagnostics_enabled(&self) -> bool {
19495        self.diagnostics_enabled && self.mode.is_full()
19496    }
19497
19498    pub fn inline_diagnostics_enabled(&self) -> bool {
19499        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19500    }
19501
19502    pub fn show_inline_diagnostics(&self) -> bool {
19503        self.show_inline_diagnostics
19504    }
19505
19506    pub fn toggle_inline_diagnostics(
19507        &mut self,
19508        _: &ToggleInlineDiagnostics,
19509        window: &mut Window,
19510        cx: &mut Context<Editor>,
19511    ) {
19512        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19513        self.refresh_inline_diagnostics(false, window, cx);
19514    }
19515
19516    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19517        self.diagnostics_max_severity = severity;
19518        self.display_map.update(cx, |display_map, _| {
19519            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19520        });
19521    }
19522
19523    pub fn toggle_diagnostics(
19524        &mut self,
19525        _: &ToggleDiagnostics,
19526        window: &mut Window,
19527        cx: &mut Context<Editor>,
19528    ) {
19529        if !self.diagnostics_enabled() {
19530            return;
19531        }
19532
19533        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19534            EditorSettings::get_global(cx)
19535                .diagnostics_max_severity
19536                .filter(|severity| severity != &DiagnosticSeverity::Off)
19537                .unwrap_or(DiagnosticSeverity::Hint)
19538        } else {
19539            DiagnosticSeverity::Off
19540        };
19541        self.set_max_diagnostics_severity(new_severity, cx);
19542        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19543            self.active_diagnostics = ActiveDiagnostic::None;
19544            self.inline_diagnostics_update = Task::ready(());
19545            self.inline_diagnostics.clear();
19546        } else {
19547            self.refresh_inline_diagnostics(false, window, cx);
19548        }
19549
19550        cx.notify();
19551    }
19552
19553    pub fn toggle_minimap(
19554        &mut self,
19555        _: &ToggleMinimap,
19556        window: &mut Window,
19557        cx: &mut Context<Editor>,
19558    ) {
19559        if self.supports_minimap(cx) {
19560            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19561        }
19562    }
19563
19564    fn refresh_inline_diagnostics(
19565        &mut self,
19566        debounce: bool,
19567        window: &mut Window,
19568        cx: &mut Context<Self>,
19569    ) {
19570        let max_severity = ProjectSettings::get_global(cx)
19571            .diagnostics
19572            .inline
19573            .max_severity
19574            .unwrap_or(self.diagnostics_max_severity);
19575
19576        if !self.inline_diagnostics_enabled()
19577            || !self.diagnostics_enabled()
19578            || !self.show_inline_diagnostics
19579            || max_severity == DiagnosticSeverity::Off
19580        {
19581            self.inline_diagnostics_update = Task::ready(());
19582            self.inline_diagnostics.clear();
19583            return;
19584        }
19585
19586        let debounce_ms = ProjectSettings::get_global(cx)
19587            .diagnostics
19588            .inline
19589            .update_debounce_ms;
19590        let debounce = if debounce && debounce_ms > 0 {
19591            Some(Duration::from_millis(debounce_ms))
19592        } else {
19593            None
19594        };
19595        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19596            if let Some(debounce) = debounce {
19597                cx.background_executor().timer(debounce).await;
19598            }
19599            let Some(snapshot) = editor.upgrade().map(|editor| {
19600                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19601            }) else {
19602                return;
19603            };
19604
19605            let new_inline_diagnostics = cx
19606                .background_spawn(async move {
19607                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19608                    for diagnostic_entry in
19609                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19610                    {
19611                        let message = diagnostic_entry
19612                            .diagnostic
19613                            .message
19614                            .split_once('\n')
19615                            .map(|(line, _)| line)
19616                            .map(SharedString::new)
19617                            .unwrap_or_else(|| {
19618                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19619                            });
19620                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19621                        let (Ok(i) | Err(i)) = inline_diagnostics
19622                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19623                        inline_diagnostics.insert(
19624                            i,
19625                            (
19626                                start_anchor,
19627                                InlineDiagnostic {
19628                                    message,
19629                                    group_id: diagnostic_entry.diagnostic.group_id,
19630                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19631                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19632                                    severity: diagnostic_entry.diagnostic.severity,
19633                                },
19634                            ),
19635                        );
19636                    }
19637                    inline_diagnostics
19638                })
19639                .await;
19640
19641            editor
19642                .update(cx, |editor, cx| {
19643                    editor.inline_diagnostics = new_inline_diagnostics;
19644                    cx.notify();
19645                })
19646                .ok();
19647        });
19648    }
19649
19650    fn pull_diagnostics(
19651        &mut self,
19652        buffer_id: BufferId,
19653        _window: &Window,
19654        cx: &mut Context<Self>,
19655    ) -> Option<()> {
19656        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19657        // skip any LSP updates for it.
19658
19659        if self.active_diagnostics == ActiveDiagnostic::All
19660            || !self.mode().is_full()
19661            || !self.diagnostics_enabled()
19662        {
19663            return None;
19664        }
19665        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19666            .diagnostics
19667            .lsp_pull_diagnostics;
19668        if !pull_diagnostics_settings.enabled {
19669            return None;
19670        }
19671        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19672        let project = self.project()?.downgrade();
19673        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19674
19675        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19676            cx.background_executor().timer(debounce).await;
19677            if let Ok(task) = project.update(cx, |project, cx| {
19678                project.lsp_store().update(cx, |lsp_store, cx| {
19679                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19680                })
19681            }) {
19682                task.await.log_err();
19683            }
19684            project
19685                .update(cx, |project, cx| {
19686                    project.lsp_store().update(cx, |lsp_store, cx| {
19687                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19688                    })
19689                })
19690                .log_err();
19691        });
19692
19693        Some(())
19694    }
19695
19696    pub fn set_selections_from_remote(
19697        &mut self,
19698        selections: Vec<Selection<Anchor>>,
19699        pending_selection: Option<Selection<Anchor>>,
19700        window: &mut Window,
19701        cx: &mut Context<Self>,
19702    ) {
19703        let old_cursor_position = self.selections.newest_anchor().head();
19704        self.selections
19705            .change_with(&self.display_snapshot(cx), |s| {
19706                s.select_anchors(selections);
19707                if let Some(pending_selection) = pending_selection {
19708                    s.set_pending(pending_selection, SelectMode::Character);
19709                } else {
19710                    s.clear_pending();
19711                }
19712            });
19713        self.selections_did_change(
19714            false,
19715            &old_cursor_position,
19716            SelectionEffects::default(),
19717            window,
19718            cx,
19719        );
19720    }
19721
19722    pub fn transact(
19723        &mut self,
19724        window: &mut Window,
19725        cx: &mut Context<Self>,
19726        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19727    ) -> Option<TransactionId> {
19728        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19729            this.start_transaction_at(Instant::now(), window, cx);
19730            update(this, window, cx);
19731            this.end_transaction_at(Instant::now(), cx)
19732        })
19733    }
19734
19735    pub fn start_transaction_at(
19736        &mut self,
19737        now: Instant,
19738        window: &mut Window,
19739        cx: &mut Context<Self>,
19740    ) -> Option<TransactionId> {
19741        self.end_selection(window, cx);
19742        if let Some(tx_id) = self
19743            .buffer
19744            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19745        {
19746            self.selection_history
19747                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19748            cx.emit(EditorEvent::TransactionBegun {
19749                transaction_id: tx_id,
19750            });
19751            Some(tx_id)
19752        } else {
19753            None
19754        }
19755    }
19756
19757    pub fn end_transaction_at(
19758        &mut self,
19759        now: Instant,
19760        cx: &mut Context<Self>,
19761    ) -> Option<TransactionId> {
19762        if let Some(transaction_id) = self
19763            .buffer
19764            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19765        {
19766            if let Some((_, end_selections)) =
19767                self.selection_history.transaction_mut(transaction_id)
19768            {
19769                *end_selections = Some(self.selections.disjoint_anchors_arc());
19770            } else {
19771                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19772            }
19773
19774            cx.emit(EditorEvent::Edited { transaction_id });
19775            Some(transaction_id)
19776        } else {
19777            None
19778        }
19779    }
19780
19781    pub fn modify_transaction_selection_history(
19782        &mut self,
19783        transaction_id: TransactionId,
19784        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19785    ) -> bool {
19786        self.selection_history
19787            .transaction_mut(transaction_id)
19788            .map(modify)
19789            .is_some()
19790    }
19791
19792    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19793        if self.selection_mark_mode {
19794            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19795                s.move_with(&mut |_, sel| {
19796                    sel.collapse_to(sel.head(), SelectionGoal::None);
19797                });
19798            })
19799        }
19800        self.selection_mark_mode = true;
19801        cx.notify();
19802    }
19803
19804    pub fn swap_selection_ends(
19805        &mut self,
19806        _: &actions::SwapSelectionEnds,
19807        window: &mut Window,
19808        cx: &mut Context<Self>,
19809    ) {
19810        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19811            s.move_with(&mut |_, sel| {
19812                if sel.start != sel.end {
19813                    sel.reversed = !sel.reversed
19814                }
19815            });
19816        });
19817        self.request_autoscroll(Autoscroll::newest(), cx);
19818        cx.notify();
19819    }
19820
19821    pub fn toggle_focus(
19822        workspace: &mut Workspace,
19823        _: &actions::ToggleFocus,
19824        window: &mut Window,
19825        cx: &mut Context<Workspace>,
19826    ) {
19827        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19828            return;
19829        };
19830        workspace.activate_item(&item, true, true, window, cx);
19831    }
19832
19833    pub fn toggle_fold(
19834        &mut self,
19835        _: &actions::ToggleFold,
19836        window: &mut Window,
19837        cx: &mut Context<Self>,
19838    ) {
19839        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19840            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19841            let selection = self.selections.newest::<Point>(&display_map);
19842
19843            let range = if selection.is_empty() {
19844                let point = selection.head().to_display_point(&display_map);
19845                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19846                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19847                    .to_point(&display_map);
19848                start..end
19849            } else {
19850                selection.range()
19851            };
19852            if display_map.folds_in_range(range).next().is_some() {
19853                self.unfold_lines(&Default::default(), window, cx)
19854            } else {
19855                self.fold(&Default::default(), window, cx)
19856            }
19857        } else {
19858            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19859            let buffer_ids: HashSet<_> = self
19860                .selections
19861                .disjoint_anchor_ranges()
19862                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19863                .collect();
19864
19865            let should_unfold = buffer_ids
19866                .iter()
19867                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19868
19869            for buffer_id in buffer_ids {
19870                if should_unfold {
19871                    self.unfold_buffer(buffer_id, cx);
19872                } else {
19873                    self.fold_buffer(buffer_id, cx);
19874                }
19875            }
19876        }
19877    }
19878
19879    pub fn toggle_fold_recursive(
19880        &mut self,
19881        _: &actions::ToggleFoldRecursive,
19882        window: &mut Window,
19883        cx: &mut Context<Self>,
19884    ) {
19885        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19886
19887        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19888        let range = if selection.is_empty() {
19889            let point = selection.head().to_display_point(&display_map);
19890            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19891            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19892                .to_point(&display_map);
19893            start..end
19894        } else {
19895            selection.range()
19896        };
19897        if display_map.folds_in_range(range).next().is_some() {
19898            self.unfold_recursive(&Default::default(), window, cx)
19899        } else {
19900            self.fold_recursive(&Default::default(), window, cx)
19901        }
19902    }
19903
19904    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19905        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19906            let mut to_fold = Vec::new();
19907            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19908            let selections = self.selections.all_adjusted(&display_map);
19909
19910            for selection in selections {
19911                let range = selection.range().sorted();
19912                let buffer_start_row = range.start.row;
19913
19914                if range.start.row != range.end.row {
19915                    let mut found = false;
19916                    let mut row = range.start.row;
19917                    while row <= range.end.row {
19918                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19919                        {
19920                            found = true;
19921                            row = crease.range().end.row + 1;
19922                            to_fold.push(crease);
19923                        } else {
19924                            row += 1
19925                        }
19926                    }
19927                    if found {
19928                        continue;
19929                    }
19930                }
19931
19932                for row in (0..=range.start.row).rev() {
19933                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19934                        && crease.range().end.row >= buffer_start_row
19935                    {
19936                        to_fold.push(crease);
19937                        if row <= range.start.row {
19938                            break;
19939                        }
19940                    }
19941                }
19942            }
19943
19944            self.fold_creases(to_fold, true, window, cx);
19945        } else {
19946            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19947            let buffer_ids = self
19948                .selections
19949                .disjoint_anchor_ranges()
19950                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19951                .collect::<HashSet<_>>();
19952            for buffer_id in buffer_ids {
19953                self.fold_buffer(buffer_id, cx);
19954            }
19955        }
19956    }
19957
19958    pub fn toggle_fold_all(
19959        &mut self,
19960        _: &actions::ToggleFoldAll,
19961        window: &mut Window,
19962        cx: &mut Context<Self>,
19963    ) {
19964        let has_folds = if self.buffer.read(cx).is_singleton() {
19965            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19966            let has_folds = display_map
19967                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19968                .next()
19969                .is_some();
19970            has_folds
19971        } else {
19972            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19973            let has_folds = buffer_ids
19974                .iter()
19975                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19976            has_folds
19977        };
19978
19979        if has_folds {
19980            self.unfold_all(&actions::UnfoldAll, window, cx);
19981        } else {
19982            self.fold_all(&actions::FoldAll, window, cx);
19983        }
19984    }
19985
19986    fn fold_at_level(
19987        &mut self,
19988        fold_at: &FoldAtLevel,
19989        window: &mut Window,
19990        cx: &mut Context<Self>,
19991    ) {
19992        if !self.buffer.read(cx).is_singleton() {
19993            return;
19994        }
19995
19996        let fold_at_level = fold_at.0;
19997        let snapshot = self.buffer.read(cx).snapshot(cx);
19998        let mut to_fold = Vec::new();
19999        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20000
20001        let row_ranges_to_keep: Vec<Range<u32>> = self
20002            .selections
20003            .all::<Point>(&self.display_snapshot(cx))
20004            .into_iter()
20005            .map(|sel| sel.start.row..sel.end.row)
20006            .collect();
20007
20008        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20009            while start_row < end_row {
20010                match self
20011                    .snapshot(window, cx)
20012                    .crease_for_buffer_row(MultiBufferRow(start_row))
20013                {
20014                    Some(crease) => {
20015                        let nested_start_row = crease.range().start.row + 1;
20016                        let nested_end_row = crease.range().end.row;
20017
20018                        if current_level < fold_at_level {
20019                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20020                        } else if current_level == fold_at_level {
20021                            // Fold iff there is no selection completely contained within the fold region
20022                            if !row_ranges_to_keep.iter().any(|selection| {
20023                                selection.end >= nested_start_row
20024                                    && selection.start <= nested_end_row
20025                            }) {
20026                                to_fold.push(crease);
20027                            }
20028                        }
20029
20030                        start_row = nested_end_row + 1;
20031                    }
20032                    None => start_row += 1,
20033                }
20034            }
20035        }
20036
20037        self.fold_creases(to_fold, true, window, cx);
20038    }
20039
20040    pub fn fold_at_level_1(
20041        &mut self,
20042        _: &actions::FoldAtLevel1,
20043        window: &mut Window,
20044        cx: &mut Context<Self>,
20045    ) {
20046        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20047    }
20048
20049    pub fn fold_at_level_2(
20050        &mut self,
20051        _: &actions::FoldAtLevel2,
20052        window: &mut Window,
20053        cx: &mut Context<Self>,
20054    ) {
20055        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20056    }
20057
20058    pub fn fold_at_level_3(
20059        &mut self,
20060        _: &actions::FoldAtLevel3,
20061        window: &mut Window,
20062        cx: &mut Context<Self>,
20063    ) {
20064        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20065    }
20066
20067    pub fn fold_at_level_4(
20068        &mut self,
20069        _: &actions::FoldAtLevel4,
20070        window: &mut Window,
20071        cx: &mut Context<Self>,
20072    ) {
20073        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20074    }
20075
20076    pub fn fold_at_level_5(
20077        &mut self,
20078        _: &actions::FoldAtLevel5,
20079        window: &mut Window,
20080        cx: &mut Context<Self>,
20081    ) {
20082        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20083    }
20084
20085    pub fn fold_at_level_6(
20086        &mut self,
20087        _: &actions::FoldAtLevel6,
20088        window: &mut Window,
20089        cx: &mut Context<Self>,
20090    ) {
20091        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20092    }
20093
20094    pub fn fold_at_level_7(
20095        &mut self,
20096        _: &actions::FoldAtLevel7,
20097        window: &mut Window,
20098        cx: &mut Context<Self>,
20099    ) {
20100        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20101    }
20102
20103    pub fn fold_at_level_8(
20104        &mut self,
20105        _: &actions::FoldAtLevel8,
20106        window: &mut Window,
20107        cx: &mut Context<Self>,
20108    ) {
20109        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20110    }
20111
20112    pub fn fold_at_level_9(
20113        &mut self,
20114        _: &actions::FoldAtLevel9,
20115        window: &mut Window,
20116        cx: &mut Context<Self>,
20117    ) {
20118        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20119    }
20120
20121    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20122        if self.buffer.read(cx).is_singleton() {
20123            let mut fold_ranges = Vec::new();
20124            let snapshot = self.buffer.read(cx).snapshot(cx);
20125
20126            for row in 0..snapshot.max_row().0 {
20127                if let Some(foldable_range) = self
20128                    .snapshot(window, cx)
20129                    .crease_for_buffer_row(MultiBufferRow(row))
20130                {
20131                    fold_ranges.push(foldable_range);
20132                }
20133            }
20134
20135            self.fold_creases(fold_ranges, true, window, cx);
20136        } else {
20137            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20138                editor
20139                    .update_in(cx, |editor, _, cx| {
20140                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20141                            editor.fold_buffer(buffer_id, cx);
20142                        }
20143                    })
20144                    .ok();
20145            });
20146        }
20147    }
20148
20149    pub fn fold_function_bodies(
20150        &mut self,
20151        _: &actions::FoldFunctionBodies,
20152        window: &mut Window,
20153        cx: &mut Context<Self>,
20154    ) {
20155        let snapshot = self.buffer.read(cx).snapshot(cx);
20156
20157        let ranges = snapshot
20158            .text_object_ranges(
20159                MultiBufferOffset(0)..snapshot.len(),
20160                TreeSitterOptions::default(),
20161            )
20162            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20163            .collect::<Vec<_>>();
20164
20165        let creases = ranges
20166            .into_iter()
20167            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20168            .collect();
20169
20170        self.fold_creases(creases, true, window, cx);
20171    }
20172
20173    pub fn fold_recursive(
20174        &mut self,
20175        _: &actions::FoldRecursive,
20176        window: &mut Window,
20177        cx: &mut Context<Self>,
20178    ) {
20179        let mut to_fold = Vec::new();
20180        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20181        let selections = self.selections.all_adjusted(&display_map);
20182
20183        for selection in selections {
20184            let range = selection.range().sorted();
20185            let buffer_start_row = range.start.row;
20186
20187            if range.start.row != range.end.row {
20188                let mut found = false;
20189                for row in range.start.row..=range.end.row {
20190                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20191                        found = true;
20192                        to_fold.push(crease);
20193                    }
20194                }
20195                if found {
20196                    continue;
20197                }
20198            }
20199
20200            for row in (0..=range.start.row).rev() {
20201                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20202                    if crease.range().end.row >= buffer_start_row {
20203                        to_fold.push(crease);
20204                    } else {
20205                        break;
20206                    }
20207                }
20208            }
20209        }
20210
20211        self.fold_creases(to_fold, true, window, cx);
20212    }
20213
20214    pub fn fold_at(
20215        &mut self,
20216        buffer_row: MultiBufferRow,
20217        window: &mut Window,
20218        cx: &mut Context<Self>,
20219    ) {
20220        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20221
20222        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20223            let autoscroll = self
20224                .selections
20225                .all::<Point>(&display_map)
20226                .iter()
20227                .any(|selection| crease.range().overlaps(&selection.range()));
20228
20229            self.fold_creases(vec![crease], autoscroll, window, cx);
20230        }
20231    }
20232
20233    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20234        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20235            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20236            let buffer = display_map.buffer_snapshot();
20237            let selections = self.selections.all::<Point>(&display_map);
20238            let ranges = selections
20239                .iter()
20240                .map(|s| {
20241                    let range = s.display_range(&display_map).sorted();
20242                    let mut start = range.start.to_point(&display_map);
20243                    let mut end = range.end.to_point(&display_map);
20244                    start.column = 0;
20245                    end.column = buffer.line_len(MultiBufferRow(end.row));
20246                    start..end
20247                })
20248                .collect::<Vec<_>>();
20249
20250            self.unfold_ranges(&ranges, true, true, cx);
20251        } else {
20252            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20253            let buffer_ids = self
20254                .selections
20255                .disjoint_anchor_ranges()
20256                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20257                .collect::<HashSet<_>>();
20258            for buffer_id in buffer_ids {
20259                self.unfold_buffer(buffer_id, cx);
20260            }
20261        }
20262    }
20263
20264    pub fn unfold_recursive(
20265        &mut self,
20266        _: &UnfoldRecursive,
20267        _window: &mut Window,
20268        cx: &mut Context<Self>,
20269    ) {
20270        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20271        let selections = self.selections.all::<Point>(&display_map);
20272        let ranges = selections
20273            .iter()
20274            .map(|s| {
20275                let mut range = s.display_range(&display_map).sorted();
20276                *range.start.column_mut() = 0;
20277                *range.end.column_mut() = display_map.line_len(range.end.row());
20278                let start = range.start.to_point(&display_map);
20279                let end = range.end.to_point(&display_map);
20280                start..end
20281            })
20282            .collect::<Vec<_>>();
20283
20284        self.unfold_ranges(&ranges, true, true, cx);
20285    }
20286
20287    pub fn unfold_at(
20288        &mut self,
20289        buffer_row: MultiBufferRow,
20290        _window: &mut Window,
20291        cx: &mut Context<Self>,
20292    ) {
20293        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20294
20295        let intersection_range = Point::new(buffer_row.0, 0)
20296            ..Point::new(
20297                buffer_row.0,
20298                display_map.buffer_snapshot().line_len(buffer_row),
20299            );
20300
20301        let autoscroll = self
20302            .selections
20303            .all::<Point>(&display_map)
20304            .iter()
20305            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20306
20307        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20308    }
20309
20310    pub fn unfold_all(
20311        &mut self,
20312        _: &actions::UnfoldAll,
20313        _window: &mut Window,
20314        cx: &mut Context<Self>,
20315    ) {
20316        if self.buffer.read(cx).is_singleton() {
20317            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20318            self.unfold_ranges(
20319                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20320                true,
20321                true,
20322                cx,
20323            );
20324        } else {
20325            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20326                editor
20327                    .update(cx, |editor, cx| {
20328                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20329                            editor.unfold_buffer(buffer_id, cx);
20330                        }
20331                    })
20332                    .ok();
20333            });
20334        }
20335    }
20336
20337    pub fn fold_selected_ranges(
20338        &mut self,
20339        _: &FoldSelectedRanges,
20340        window: &mut Window,
20341        cx: &mut Context<Self>,
20342    ) {
20343        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20344        let selections = self.selections.all_adjusted(&display_map);
20345        let ranges = selections
20346            .into_iter()
20347            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20348            .collect::<Vec<_>>();
20349        self.fold_creases(ranges, true, window, cx);
20350    }
20351
20352    pub fn fold_ranges<T: ToOffset + Clone>(
20353        &mut self,
20354        ranges: Vec<Range<T>>,
20355        auto_scroll: bool,
20356        window: &mut Window,
20357        cx: &mut Context<Self>,
20358    ) {
20359        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20360        let ranges = ranges
20361            .into_iter()
20362            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20363            .collect::<Vec<_>>();
20364        self.fold_creases(ranges, auto_scroll, window, cx);
20365    }
20366
20367    pub fn fold_creases<T: ToOffset + Clone>(
20368        &mut self,
20369        creases: Vec<Crease<T>>,
20370        auto_scroll: bool,
20371        _window: &mut Window,
20372        cx: &mut Context<Self>,
20373    ) {
20374        if creases.is_empty() {
20375            return;
20376        }
20377
20378        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20379
20380        if auto_scroll {
20381            self.request_autoscroll(Autoscroll::fit(), cx);
20382        }
20383
20384        cx.notify();
20385
20386        self.scrollbar_marker_state.dirty = true;
20387        self.folds_did_change(cx);
20388    }
20389
20390    /// Removes any folds whose ranges intersect any of the given ranges.
20391    pub fn unfold_ranges<T: ToOffset + Clone>(
20392        &mut self,
20393        ranges: &[Range<T>],
20394        inclusive: bool,
20395        auto_scroll: bool,
20396        cx: &mut Context<Self>,
20397    ) {
20398        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20399            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20400        });
20401        self.folds_did_change(cx);
20402    }
20403
20404    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20405        self.fold_buffers([buffer_id], cx);
20406    }
20407
20408    pub fn fold_buffers(
20409        &mut self,
20410        buffer_ids: impl IntoIterator<Item = BufferId>,
20411        cx: &mut Context<Self>,
20412    ) {
20413        if self.buffer().read(cx).is_singleton() {
20414            return;
20415        }
20416
20417        let ids_to_fold: Vec<BufferId> = buffer_ids
20418            .into_iter()
20419            .filter(|id| !self.is_buffer_folded(*id, cx))
20420            .collect();
20421
20422        if ids_to_fold.is_empty() {
20423            return;
20424        }
20425
20426        let mut all_folded_excerpt_ids = Vec::new();
20427        for buffer_id in &ids_to_fold {
20428            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20429            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
20430        }
20431
20432        self.display_map.update(cx, |display_map, cx| {
20433            display_map.fold_buffers(ids_to_fold.clone(), cx)
20434        });
20435
20436        let snapshot = self.display_snapshot(cx);
20437        self.selections.change_with(&snapshot, |selections| {
20438            for buffer_id in ids_to_fold {
20439                selections.remove_selections_from_buffer(buffer_id);
20440            }
20441        });
20442
20443        cx.emit(EditorEvent::BufferFoldToggled {
20444            ids: all_folded_excerpt_ids,
20445            folded: true,
20446        });
20447        cx.notify();
20448    }
20449
20450    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20451        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20452            return;
20453        }
20454        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20455        self.display_map.update(cx, |display_map, cx| {
20456            display_map.unfold_buffers([buffer_id], cx);
20457        });
20458        cx.emit(EditorEvent::BufferFoldToggled {
20459            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20460            folded: false,
20461        });
20462        cx.notify();
20463    }
20464
20465    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20466        self.display_map.read(cx).is_buffer_folded(buffer)
20467    }
20468
20469    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20470        if self.buffer().read(cx).is_singleton() {
20471            return false;
20472        }
20473        !self.folded_buffers(cx).is_empty()
20474    }
20475
20476    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20477        self.display_map.read(cx).folded_buffers()
20478    }
20479
20480    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20481        self.display_map.update(cx, |display_map, cx| {
20482            display_map.disable_header_for_buffer(buffer_id, cx);
20483        });
20484        cx.notify();
20485    }
20486
20487    /// Removes any folds with the given ranges.
20488    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20489        &mut self,
20490        ranges: &[Range<T>],
20491        type_id: TypeId,
20492        auto_scroll: bool,
20493        cx: &mut Context<Self>,
20494    ) {
20495        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20496            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20497        });
20498        self.folds_did_change(cx);
20499    }
20500
20501    fn remove_folds_with<T: ToOffset + Clone>(
20502        &mut self,
20503        ranges: &[Range<T>],
20504        auto_scroll: bool,
20505        cx: &mut Context<Self>,
20506        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20507    ) {
20508        if ranges.is_empty() {
20509            return;
20510        }
20511
20512        let mut buffers_affected = HashSet::default();
20513        let multi_buffer = self.buffer().read(cx);
20514        for range in ranges {
20515            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20516                buffers_affected.insert(buffer.read(cx).remote_id());
20517            };
20518        }
20519
20520        self.display_map.update(cx, update);
20521
20522        if auto_scroll {
20523            self.request_autoscroll(Autoscroll::fit(), cx);
20524        }
20525
20526        cx.notify();
20527        self.scrollbar_marker_state.dirty = true;
20528        self.active_indent_guides_state.dirty = true;
20529    }
20530
20531    pub fn update_renderer_widths(
20532        &mut self,
20533        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20534        cx: &mut Context<Self>,
20535    ) -> bool {
20536        self.display_map
20537            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20538    }
20539
20540    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20541        self.display_map.read(cx).fold_placeholder.clone()
20542    }
20543
20544    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20545        self.buffer.update(cx, |buffer, cx| {
20546            buffer.set_all_diff_hunks_expanded(cx);
20547        });
20548    }
20549
20550    pub fn expand_all_diff_hunks(
20551        &mut self,
20552        _: &ExpandAllDiffHunks,
20553        _window: &mut Window,
20554        cx: &mut Context<Self>,
20555    ) {
20556        self.buffer.update(cx, |buffer, cx| {
20557            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20558        });
20559    }
20560
20561    pub fn collapse_all_diff_hunks(
20562        &mut self,
20563        _: &CollapseAllDiffHunks,
20564        _window: &mut Window,
20565        cx: &mut Context<Self>,
20566    ) {
20567        self.buffer.update(cx, |buffer, cx| {
20568            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20569        });
20570    }
20571
20572    pub fn toggle_selected_diff_hunks(
20573        &mut self,
20574        _: &ToggleSelectedDiffHunks,
20575        _window: &mut Window,
20576        cx: &mut Context<Self>,
20577    ) {
20578        let ranges: Vec<_> = self
20579            .selections
20580            .disjoint_anchors()
20581            .iter()
20582            .map(|s| s.range())
20583            .collect();
20584        self.toggle_diff_hunks_in_ranges(ranges, cx);
20585    }
20586
20587    pub fn diff_hunks_in_ranges<'a>(
20588        &'a self,
20589        ranges: &'a [Range<Anchor>],
20590        buffer: &'a MultiBufferSnapshot,
20591    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20592        ranges.iter().flat_map(move |range| {
20593            let end_excerpt_id = range.end.excerpt_id;
20594            let range = range.to_point(buffer);
20595            let mut peek_end = range.end;
20596            if range.end.row < buffer.max_row().0 {
20597                peek_end = Point::new(range.end.row + 1, 0);
20598            }
20599            buffer
20600                .diff_hunks_in_range(range.start..peek_end)
20601                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20602        })
20603    }
20604
20605    pub fn has_stageable_diff_hunks_in_ranges(
20606        &self,
20607        ranges: &[Range<Anchor>],
20608        snapshot: &MultiBufferSnapshot,
20609    ) -> bool {
20610        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20611        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20612    }
20613
20614    pub fn toggle_staged_selected_diff_hunks(
20615        &mut self,
20616        _: &::git::ToggleStaged,
20617        _: &mut Window,
20618        cx: &mut Context<Self>,
20619    ) {
20620        let snapshot = self.buffer.read(cx).snapshot(cx);
20621        let ranges: Vec<_> = self
20622            .selections
20623            .disjoint_anchors()
20624            .iter()
20625            .map(|s| s.range())
20626            .collect();
20627        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20628        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20629    }
20630
20631    pub fn set_render_diff_hunk_controls(
20632        &mut self,
20633        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20634        cx: &mut Context<Self>,
20635    ) {
20636        self.render_diff_hunk_controls = render_diff_hunk_controls;
20637        cx.notify();
20638    }
20639
20640    pub fn stage_and_next(
20641        &mut self,
20642        _: &::git::StageAndNext,
20643        window: &mut Window,
20644        cx: &mut Context<Self>,
20645    ) {
20646        self.do_stage_or_unstage_and_next(true, window, cx);
20647    }
20648
20649    pub fn unstage_and_next(
20650        &mut self,
20651        _: &::git::UnstageAndNext,
20652        window: &mut Window,
20653        cx: &mut Context<Self>,
20654    ) {
20655        self.do_stage_or_unstage_and_next(false, window, cx);
20656    }
20657
20658    pub fn stage_or_unstage_diff_hunks(
20659        &mut self,
20660        stage: bool,
20661        ranges: Vec<Range<Anchor>>,
20662        cx: &mut Context<Self>,
20663    ) {
20664        if self.delegate_stage_and_restore {
20665            let snapshot = self.buffer.read(cx).snapshot(cx);
20666            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20667            if !hunks.is_empty() {
20668                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20669            }
20670            return;
20671        }
20672        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20673        cx.spawn(async move |this, cx| {
20674            task.await?;
20675            this.update(cx, |this, cx| {
20676                let snapshot = this.buffer.read(cx).snapshot(cx);
20677                let chunk_by = this
20678                    .diff_hunks_in_ranges(&ranges, &snapshot)
20679                    .chunk_by(|hunk| hunk.buffer_id);
20680                for (buffer_id, hunks) in &chunk_by {
20681                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20682                }
20683            })
20684        })
20685        .detach_and_log_err(cx);
20686    }
20687
20688    fn save_buffers_for_ranges_if_needed(
20689        &mut self,
20690        ranges: &[Range<Anchor>],
20691        cx: &mut Context<Editor>,
20692    ) -> Task<Result<()>> {
20693        let multibuffer = self.buffer.read(cx);
20694        let snapshot = multibuffer.read(cx);
20695        let buffer_ids: HashSet<_> = ranges
20696            .iter()
20697            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20698            .collect();
20699        drop(snapshot);
20700
20701        let mut buffers = HashSet::default();
20702        for buffer_id in buffer_ids {
20703            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20704                let buffer = buffer_entity.read(cx);
20705                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20706                {
20707                    buffers.insert(buffer_entity);
20708                }
20709            }
20710        }
20711
20712        if let Some(project) = &self.project {
20713            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20714        } else {
20715            Task::ready(Ok(()))
20716        }
20717    }
20718
20719    fn do_stage_or_unstage_and_next(
20720        &mut self,
20721        stage: bool,
20722        window: &mut Window,
20723        cx: &mut Context<Self>,
20724    ) {
20725        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20726
20727        if ranges.iter().any(|range| range.start != range.end) {
20728            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20729            return;
20730        }
20731
20732        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20733        let snapshot = self.snapshot(window, cx);
20734        let position = self
20735            .selections
20736            .newest::<Point>(&snapshot.display_snapshot)
20737            .head();
20738        let mut row = snapshot
20739            .buffer_snapshot()
20740            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20741            .find(|hunk| hunk.row_range.start.0 > position.row)
20742            .map(|hunk| hunk.row_range.start);
20743
20744        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20745        // Outside of the project diff editor, wrap around to the beginning.
20746        if !all_diff_hunks_expanded {
20747            row = row.or_else(|| {
20748                snapshot
20749                    .buffer_snapshot()
20750                    .diff_hunks_in_range(Point::zero()..position)
20751                    .find(|hunk| hunk.row_range.end.0 < position.row)
20752                    .map(|hunk| hunk.row_range.start)
20753            });
20754        }
20755
20756        if let Some(row) = row {
20757            let destination = Point::new(row.0, 0);
20758            let autoscroll = Autoscroll::center();
20759
20760            self.unfold_ranges(&[destination..destination], false, false, cx);
20761            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20762                s.select_ranges([destination..destination]);
20763            });
20764        }
20765    }
20766
20767    pub(crate) fn do_stage_or_unstage(
20768        &self,
20769        stage: bool,
20770        buffer_id: BufferId,
20771        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20772        cx: &mut App,
20773    ) -> Option<()> {
20774        let project = self.project()?;
20775        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20776        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20777        let buffer_snapshot = buffer.read(cx).snapshot();
20778        let file_exists = buffer_snapshot
20779            .file()
20780            .is_some_and(|file| file.disk_state().exists());
20781        diff.update(cx, |diff, cx| {
20782            diff.stage_or_unstage_hunks(
20783                stage,
20784                &hunks
20785                    .map(|hunk| buffer_diff::DiffHunk {
20786                        buffer_range: hunk.buffer_range,
20787                        // We don't need to pass in word diffs here because they're only used for rendering and
20788                        // this function changes internal state
20789                        base_word_diffs: Vec::default(),
20790                        buffer_word_diffs: Vec::default(),
20791                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20792                            ..hunk.diff_base_byte_range.end.0,
20793                        secondary_status: hunk.status.secondary,
20794                        range: Point::zero()..Point::zero(), // unused
20795                    })
20796                    .collect::<Vec<_>>(),
20797                &buffer_snapshot,
20798                file_exists,
20799                cx,
20800            )
20801        });
20802        None
20803    }
20804
20805    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20806        let ranges: Vec<_> = self
20807            .selections
20808            .disjoint_anchors()
20809            .iter()
20810            .map(|s| s.range())
20811            .collect();
20812        self.buffer
20813            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20814    }
20815
20816    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20817        self.buffer.update(cx, |buffer, cx| {
20818            let ranges = vec![Anchor::min()..Anchor::max()];
20819            if !buffer.all_diff_hunks_expanded()
20820                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20821            {
20822                buffer.collapse_diff_hunks(ranges, cx);
20823                true
20824            } else {
20825                false
20826            }
20827        })
20828    }
20829
20830    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20831        if self.buffer.read(cx).all_diff_hunks_expanded() {
20832            return true;
20833        }
20834        let ranges = vec![Anchor::min()..Anchor::max()];
20835        self.buffer
20836            .read(cx)
20837            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20838    }
20839
20840    fn toggle_diff_hunks_in_ranges(
20841        &mut self,
20842        ranges: Vec<Range<Anchor>>,
20843        cx: &mut Context<Editor>,
20844    ) {
20845        self.buffer.update(cx, |buffer, cx| {
20846            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20847            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20848        })
20849    }
20850
20851    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20852        self.buffer.update(cx, |buffer, cx| {
20853            buffer.toggle_single_diff_hunk(range, cx);
20854        })
20855    }
20856
20857    pub(crate) fn apply_all_diff_hunks(
20858        &mut self,
20859        _: &ApplyAllDiffHunks,
20860        window: &mut Window,
20861        cx: &mut Context<Self>,
20862    ) {
20863        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20864
20865        let buffers = self.buffer.read(cx).all_buffers();
20866        for branch_buffer in buffers {
20867            branch_buffer.update(cx, |branch_buffer, cx| {
20868                branch_buffer.merge_into_base(Vec::new(), cx);
20869            });
20870        }
20871
20872        if let Some(project) = self.project.clone() {
20873            self.save(
20874                SaveOptions {
20875                    format: true,
20876                    autosave: false,
20877                },
20878                project,
20879                window,
20880                cx,
20881            )
20882            .detach_and_log_err(cx);
20883        }
20884    }
20885
20886    pub(crate) fn apply_selected_diff_hunks(
20887        &mut self,
20888        _: &ApplyDiffHunk,
20889        window: &mut Window,
20890        cx: &mut Context<Self>,
20891    ) {
20892        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20893        let snapshot = self.snapshot(window, cx);
20894        let hunks = snapshot.hunks_for_ranges(
20895            self.selections
20896                .all(&snapshot.display_snapshot)
20897                .into_iter()
20898                .map(|selection| selection.range()),
20899        );
20900        let mut ranges_by_buffer = HashMap::default();
20901        self.transact(window, cx, |editor, _window, cx| {
20902            for hunk in hunks {
20903                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20904                    ranges_by_buffer
20905                        .entry(buffer.clone())
20906                        .or_insert_with(Vec::new)
20907                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20908                }
20909            }
20910
20911            for (buffer, ranges) in ranges_by_buffer {
20912                buffer.update(cx, |buffer, cx| {
20913                    buffer.merge_into_base(ranges, cx);
20914                });
20915            }
20916        });
20917
20918        if let Some(project) = self.project.clone() {
20919            self.save(
20920                SaveOptions {
20921                    format: true,
20922                    autosave: false,
20923                },
20924                project,
20925                window,
20926                cx,
20927            )
20928            .detach_and_log_err(cx);
20929        }
20930    }
20931
20932    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20933        if hovered != self.gutter_hovered {
20934            self.gutter_hovered = hovered;
20935            cx.notify();
20936        }
20937    }
20938
20939    pub fn insert_blocks(
20940        &mut self,
20941        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20942        autoscroll: Option<Autoscroll>,
20943        cx: &mut Context<Self>,
20944    ) -> Vec<CustomBlockId> {
20945        let blocks = self
20946            .display_map
20947            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20948        if let Some(autoscroll) = autoscroll {
20949            self.request_autoscroll(autoscroll, cx);
20950        }
20951        cx.notify();
20952        blocks
20953    }
20954
20955    pub fn resize_blocks(
20956        &mut self,
20957        heights: HashMap<CustomBlockId, u32>,
20958        autoscroll: Option<Autoscroll>,
20959        cx: &mut Context<Self>,
20960    ) {
20961        self.display_map
20962            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20963        if let Some(autoscroll) = autoscroll {
20964            self.request_autoscroll(autoscroll, cx);
20965        }
20966        cx.notify();
20967    }
20968
20969    pub fn replace_blocks(
20970        &mut self,
20971        renderers: HashMap<CustomBlockId, RenderBlock>,
20972        autoscroll: Option<Autoscroll>,
20973        cx: &mut Context<Self>,
20974    ) {
20975        self.display_map
20976            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20977        if let Some(autoscroll) = autoscroll {
20978            self.request_autoscroll(autoscroll, cx);
20979        }
20980        cx.notify();
20981    }
20982
20983    pub fn remove_blocks(
20984        &mut self,
20985        block_ids: HashSet<CustomBlockId>,
20986        autoscroll: Option<Autoscroll>,
20987        cx: &mut Context<Self>,
20988    ) {
20989        self.display_map.update(cx, |display_map, cx| {
20990            display_map.remove_blocks(block_ids, cx)
20991        });
20992        if let Some(autoscroll) = autoscroll {
20993            self.request_autoscroll(autoscroll, cx);
20994        }
20995        cx.notify();
20996    }
20997
20998    pub fn row_for_block(
20999        &self,
21000        block_id: CustomBlockId,
21001        cx: &mut Context<Self>,
21002    ) -> Option<DisplayRow> {
21003        self.display_map
21004            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21005    }
21006
21007    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21008        self.focused_block = Some(focused_block);
21009    }
21010
21011    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21012        self.focused_block.take()
21013    }
21014
21015    pub fn insert_creases(
21016        &mut self,
21017        creases: impl IntoIterator<Item = Crease<Anchor>>,
21018        cx: &mut Context<Self>,
21019    ) -> Vec<CreaseId> {
21020        self.display_map
21021            .update(cx, |map, cx| map.insert_creases(creases, cx))
21022    }
21023
21024    pub fn remove_creases(
21025        &mut self,
21026        ids: impl IntoIterator<Item = CreaseId>,
21027        cx: &mut Context<Self>,
21028    ) -> Vec<(CreaseId, Range<Anchor>)> {
21029        self.display_map
21030            .update(cx, |map, cx| map.remove_creases(ids, cx))
21031    }
21032
21033    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21034        self.display_map
21035            .update(cx, |map, cx| map.snapshot(cx))
21036            .longest_row()
21037    }
21038
21039    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21040        self.display_map
21041            .update(cx, |map, cx| map.snapshot(cx))
21042            .max_point()
21043    }
21044
21045    pub fn text(&self, cx: &App) -> String {
21046        self.buffer.read(cx).read(cx).text()
21047    }
21048
21049    pub fn is_empty(&self, cx: &App) -> bool {
21050        self.buffer.read(cx).read(cx).is_empty()
21051    }
21052
21053    pub fn text_option(&self, cx: &App) -> Option<String> {
21054        let text = self.text(cx);
21055        let text = text.trim();
21056
21057        if text.is_empty() {
21058            return None;
21059        }
21060
21061        Some(text.to_string())
21062    }
21063
21064    pub fn set_text(
21065        &mut self,
21066        text: impl Into<Arc<str>>,
21067        window: &mut Window,
21068        cx: &mut Context<Self>,
21069    ) {
21070        self.transact(window, cx, |this, _, cx| {
21071            this.buffer
21072                .read(cx)
21073                .as_singleton()
21074                .expect("you can only call set_text on editors for singleton buffers")
21075                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21076        });
21077    }
21078
21079    pub fn display_text(&self, cx: &mut App) -> String {
21080        self.display_map
21081            .update(cx, |map, cx| map.snapshot(cx))
21082            .text()
21083    }
21084
21085    fn create_minimap(
21086        &self,
21087        minimap_settings: MinimapSettings,
21088        window: &mut Window,
21089        cx: &mut Context<Self>,
21090    ) -> Option<Entity<Self>> {
21091        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21092            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21093    }
21094
21095    fn initialize_new_minimap(
21096        &self,
21097        minimap_settings: MinimapSettings,
21098        window: &mut Window,
21099        cx: &mut Context<Self>,
21100    ) -> Entity<Self> {
21101        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21102        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21103
21104        let mut minimap = Editor::new_internal(
21105            EditorMode::Minimap {
21106                parent: cx.weak_entity(),
21107            },
21108            self.buffer.clone(),
21109            None,
21110            Some(self.display_map.clone()),
21111            window,
21112            cx,
21113        );
21114        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21115        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21116        minimap.scroll_manager.clone_state(
21117            &self.scroll_manager,
21118            &my_snapshot,
21119            &minimap_snapshot,
21120            cx,
21121        );
21122        minimap.set_text_style_refinement(TextStyleRefinement {
21123            font_size: Some(MINIMAP_FONT_SIZE),
21124            font_weight: Some(MINIMAP_FONT_WEIGHT),
21125            font_family: Some(MINIMAP_FONT_FAMILY),
21126            ..Default::default()
21127        });
21128        minimap.update_minimap_configuration(minimap_settings, cx);
21129        cx.new(|_| minimap)
21130    }
21131
21132    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21133        let current_line_highlight = minimap_settings
21134            .current_line_highlight
21135            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21136        self.set_current_line_highlight(Some(current_line_highlight));
21137    }
21138
21139    pub fn minimap(&self) -> Option<&Entity<Self>> {
21140        self.minimap
21141            .as_ref()
21142            .filter(|_| self.minimap_visibility.visible())
21143    }
21144
21145    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21146        let mut wrap_guides = smallvec![];
21147
21148        if self.show_wrap_guides == Some(false) {
21149            return wrap_guides;
21150        }
21151
21152        let settings = self.buffer.read(cx).language_settings(cx);
21153        if settings.show_wrap_guides {
21154            match self.soft_wrap_mode(cx) {
21155                SoftWrap::Column(soft_wrap) => {
21156                    wrap_guides.push((soft_wrap as usize, true));
21157                }
21158                SoftWrap::Bounded(soft_wrap) => {
21159                    wrap_guides.push((soft_wrap as usize, true));
21160                }
21161                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21162            }
21163            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21164        }
21165
21166        wrap_guides
21167    }
21168
21169    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21170        let settings = self.buffer.read(cx).language_settings(cx);
21171        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21172        match mode {
21173            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21174                SoftWrap::None
21175            }
21176            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21177            language_settings::SoftWrap::PreferredLineLength => {
21178                SoftWrap::Column(settings.preferred_line_length)
21179            }
21180            language_settings::SoftWrap::Bounded => {
21181                SoftWrap::Bounded(settings.preferred_line_length)
21182            }
21183        }
21184    }
21185
21186    pub fn set_soft_wrap_mode(
21187        &mut self,
21188        mode: language_settings::SoftWrap,
21189        cx: &mut Context<Self>,
21190    ) {
21191        self.soft_wrap_mode_override = Some(mode);
21192        cx.notify();
21193    }
21194
21195    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21196        self.hard_wrap = hard_wrap;
21197        cx.notify();
21198    }
21199
21200    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21201        self.text_style_refinement = Some(style);
21202    }
21203
21204    /// called by the Element so we know what style we were most recently rendered with.
21205    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21206        // We intentionally do not inform the display map about the minimap style
21207        // so that wrapping is not recalculated and stays consistent for the editor
21208        // and its linked minimap.
21209        if !self.mode.is_minimap() {
21210            let font = style.text.font();
21211            let font_size = style.text.font_size.to_pixels(window.rem_size());
21212            let display_map = self
21213                .placeholder_display_map
21214                .as_ref()
21215                .filter(|_| self.is_empty(cx))
21216                .unwrap_or(&self.display_map);
21217
21218            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21219        }
21220        self.style = Some(style);
21221    }
21222
21223    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21224        if self.style.is_none() {
21225            self.style = Some(self.create_style(cx));
21226        }
21227        self.style.as_ref().unwrap()
21228    }
21229
21230    // Called by the element. This method is not designed to be called outside of the editor
21231    // element's layout code because it does not notify when rewrapping is computed synchronously.
21232    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21233        if self.is_empty(cx) {
21234            self.placeholder_display_map
21235                .as_ref()
21236                .map_or(false, |display_map| {
21237                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21238                })
21239        } else {
21240            self.display_map
21241                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21242        }
21243    }
21244
21245    pub fn set_soft_wrap(&mut self) {
21246        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21247    }
21248
21249    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21250        if self.soft_wrap_mode_override.is_some() {
21251            self.soft_wrap_mode_override.take();
21252        } else {
21253            let soft_wrap = match self.soft_wrap_mode(cx) {
21254                SoftWrap::GitDiff => return,
21255                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21256                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21257                    language_settings::SoftWrap::None
21258                }
21259            };
21260            self.soft_wrap_mode_override = Some(soft_wrap);
21261        }
21262        cx.notify();
21263    }
21264
21265    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21266        let Some(workspace) = self.workspace() else {
21267            return;
21268        };
21269        let fs = workspace.read(cx).app_state().fs.clone();
21270        let current_show = TabBarSettings::get_global(cx).show;
21271        update_settings_file(fs, cx, move |setting, _| {
21272            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21273        });
21274    }
21275
21276    pub fn toggle_indent_guides(
21277        &mut self,
21278        _: &ToggleIndentGuides,
21279        _: &mut Window,
21280        cx: &mut Context<Self>,
21281    ) {
21282        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21283            self.buffer
21284                .read(cx)
21285                .language_settings(cx)
21286                .indent_guides
21287                .enabled
21288        });
21289        self.show_indent_guides = Some(!currently_enabled);
21290        cx.notify();
21291    }
21292
21293    fn should_show_indent_guides(&self) -> Option<bool> {
21294        self.show_indent_guides
21295    }
21296
21297    pub fn disable_indent_guides_for_buffer(
21298        &mut self,
21299        buffer_id: BufferId,
21300        cx: &mut Context<Self>,
21301    ) {
21302        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21303        cx.notify();
21304    }
21305
21306    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21307        self.buffers_with_disabled_indent_guides
21308            .contains(&buffer_id)
21309    }
21310
21311    pub fn toggle_line_numbers(
21312        &mut self,
21313        _: &ToggleLineNumbers,
21314        _: &mut Window,
21315        cx: &mut Context<Self>,
21316    ) {
21317        let mut editor_settings = EditorSettings::get_global(cx).clone();
21318        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21319        EditorSettings::override_global(editor_settings, cx);
21320    }
21321
21322    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21323        if let Some(show_line_numbers) = self.show_line_numbers {
21324            return show_line_numbers;
21325        }
21326        EditorSettings::get_global(cx).gutter.line_numbers
21327    }
21328
21329    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21330        match (
21331            self.use_relative_line_numbers,
21332            EditorSettings::get_global(cx).relative_line_numbers,
21333        ) {
21334            (None, setting) => setting,
21335            (Some(false), _) => RelativeLineNumbers::Disabled,
21336            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21337            (Some(true), _) => RelativeLineNumbers::Enabled,
21338        }
21339    }
21340
21341    pub fn toggle_relative_line_numbers(
21342        &mut self,
21343        _: &ToggleRelativeLineNumbers,
21344        _: &mut Window,
21345        cx: &mut Context<Self>,
21346    ) {
21347        let is_relative = self.relative_line_numbers(cx);
21348        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21349    }
21350
21351    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21352        self.use_relative_line_numbers = is_relative;
21353        cx.notify();
21354    }
21355
21356    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21357        self.show_gutter = show_gutter;
21358        cx.notify();
21359    }
21360
21361    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21362        self.show_scrollbars = ScrollbarAxes {
21363            horizontal: show,
21364            vertical: show,
21365        };
21366        cx.notify();
21367    }
21368
21369    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21370        self.show_scrollbars.vertical = show;
21371        cx.notify();
21372    }
21373
21374    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21375        self.show_scrollbars.horizontal = show;
21376        cx.notify();
21377    }
21378
21379    pub fn set_minimap_visibility(
21380        &mut self,
21381        minimap_visibility: MinimapVisibility,
21382        window: &mut Window,
21383        cx: &mut Context<Self>,
21384    ) {
21385        if self.minimap_visibility != minimap_visibility {
21386            if minimap_visibility.visible() && self.minimap.is_none() {
21387                let minimap_settings = EditorSettings::get_global(cx).minimap;
21388                self.minimap =
21389                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21390            }
21391            self.minimap_visibility = minimap_visibility;
21392            cx.notify();
21393        }
21394    }
21395
21396    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21397        self.set_show_scrollbars(false, cx);
21398        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21399    }
21400
21401    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21402        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21403    }
21404
21405    /// Normally the text in full mode and auto height editors is padded on the
21406    /// left side by roughly half a character width for improved hit testing.
21407    ///
21408    /// Use this method to disable this for cases where this is not wanted (e.g.
21409    /// if you want to align the editor text with some other text above or below)
21410    /// or if you want to add this padding to single-line editors.
21411    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21412        self.offset_content = offset_content;
21413        cx.notify();
21414    }
21415
21416    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21417        self.show_line_numbers = Some(show_line_numbers);
21418        cx.notify();
21419    }
21420
21421    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21422        self.disable_expand_excerpt_buttons = true;
21423        cx.notify();
21424    }
21425
21426    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21427        self.number_deleted_lines = number;
21428        cx.notify();
21429    }
21430
21431    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21432        self.delegate_expand_excerpts = delegate;
21433    }
21434
21435    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21436        self.delegate_stage_and_restore = delegate;
21437    }
21438
21439    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21440        self.delegate_open_excerpts = delegate;
21441    }
21442
21443    pub fn set_on_local_selections_changed(
21444        &mut self,
21445        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21446    ) {
21447        self.on_local_selections_changed = callback;
21448    }
21449
21450    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21451        self.suppress_selection_callback = suppress;
21452    }
21453
21454    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21455        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21456        cx.notify();
21457    }
21458
21459    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21460        self.show_code_actions = Some(show_code_actions);
21461        cx.notify();
21462    }
21463
21464    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21465        self.show_runnables = Some(show_runnables);
21466        cx.notify();
21467    }
21468
21469    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21470        self.show_breakpoints = Some(show_breakpoints);
21471        cx.notify();
21472    }
21473
21474    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21475        self.show_diff_review_button = show;
21476        cx.notify();
21477    }
21478
21479    pub fn show_diff_review_button(&self) -> bool {
21480        self.show_diff_review_button
21481    }
21482
21483    pub fn render_diff_review_button(
21484        &self,
21485        display_row: DisplayRow,
21486        width: Pixels,
21487        cx: &mut Context<Self>,
21488    ) -> impl IntoElement {
21489        let text_color = cx.theme().colors().text;
21490        let icon_color = cx.theme().colors().icon_accent;
21491
21492        h_flex()
21493            .id("diff_review_button")
21494            .cursor_pointer()
21495            .w(width - px(1.))
21496            .h(relative(0.9))
21497            .justify_center()
21498            .rounded_sm()
21499            .border_1()
21500            .border_color(text_color.opacity(0.1))
21501            .bg(text_color.opacity(0.15))
21502            .hover(|s| {
21503                s.bg(icon_color.opacity(0.4))
21504                    .border_color(icon_color.opacity(0.5))
21505            })
21506            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21507            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21508            .on_mouse_down(
21509                gpui::MouseButton::Left,
21510                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21511                    editor.start_diff_review_drag(display_row, window, cx);
21512                }),
21513            )
21514    }
21515
21516    pub fn start_diff_review_drag(
21517        &mut self,
21518        display_row: DisplayRow,
21519        window: &mut Window,
21520        cx: &mut Context<Self>,
21521    ) {
21522        let snapshot = self.snapshot(window, cx);
21523        let point = snapshot
21524            .display_snapshot
21525            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21526        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21527        self.diff_review_drag_state = Some(DiffReviewDragState {
21528            start_anchor: anchor,
21529            current_anchor: anchor,
21530        });
21531        cx.notify();
21532    }
21533
21534    pub fn update_diff_review_drag(
21535        &mut self,
21536        display_row: DisplayRow,
21537        window: &mut Window,
21538        cx: &mut Context<Self>,
21539    ) {
21540        if self.diff_review_drag_state.is_none() {
21541            return;
21542        }
21543        let snapshot = self.snapshot(window, cx);
21544        let point = snapshot
21545            .display_snapshot
21546            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21547        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21548        if let Some(drag_state) = &mut self.diff_review_drag_state {
21549            drag_state.current_anchor = anchor;
21550            cx.notify();
21551        }
21552    }
21553
21554    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21555        if let Some(drag_state) = self.diff_review_drag_state.take() {
21556            let snapshot = self.snapshot(window, cx);
21557            let range = drag_state.row_range(&snapshot.display_snapshot);
21558            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21559        }
21560        cx.notify();
21561    }
21562
21563    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21564        self.diff_review_drag_state = None;
21565        cx.notify();
21566    }
21567
21568    /// Calculates the appropriate block height for the diff review overlay.
21569    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21570    /// and 2 lines per comment when expanded.
21571    fn calculate_overlay_height(
21572        &self,
21573        hunk_key: &DiffHunkKey,
21574        comments_expanded: bool,
21575        snapshot: &MultiBufferSnapshot,
21576    ) -> u32 {
21577        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21578        let base_height: u32 = 2; // Input row with avatar and buttons
21579
21580        if comment_count == 0 {
21581            base_height
21582        } else if comments_expanded {
21583            // Header (1 line) + 2 lines per comment
21584            base_height + 1 + (comment_count as u32 * 2)
21585        } else {
21586            // Just header when collapsed
21587            base_height + 1
21588        }
21589    }
21590
21591    pub fn show_diff_review_overlay(
21592        &mut self,
21593        display_range: Range<DisplayRow>,
21594        window: &mut Window,
21595        cx: &mut Context<Self>,
21596    ) {
21597        let Range { start, end } = display_range.sorted();
21598
21599        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21600        let editor_snapshot = self.snapshot(window, cx);
21601
21602        // Convert display rows to multibuffer points
21603        let start_point = editor_snapshot
21604            .display_snapshot
21605            .display_point_to_point(start.as_display_point(), Bias::Left);
21606        let end_point = editor_snapshot
21607            .display_snapshot
21608            .display_point_to_point(end.as_display_point(), Bias::Left);
21609        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21610
21611        // Create anchor range for the selected lines (start of first line to end of last line)
21612        let line_end = Point::new(
21613            end_point.row,
21614            buffer_snapshot.line_len(end_multi_buffer_row),
21615        );
21616        let anchor_range =
21617            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21618
21619        // Compute the hunk key for this display row
21620        let file_path = buffer_snapshot
21621            .file_at(start_point)
21622            .map(|file: &Arc<dyn language::File>| file.path().clone())
21623            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21624        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21625        let new_hunk_key = DiffHunkKey {
21626            file_path,
21627            hunk_start_anchor,
21628        };
21629
21630        // Check if we already have an overlay for this hunk
21631        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21632            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21633        }) {
21634            // Just focus the existing overlay's prompt editor
21635            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21636            window.focus(&focus_handle, cx);
21637            return;
21638        }
21639
21640        // Dismiss overlays that have no comments for their hunks
21641        self.dismiss_overlays_without_comments(cx);
21642
21643        // Get the current user's avatar URI from the project's user_store
21644        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21645            let user_store = project.read(cx).user_store();
21646            user_store
21647                .read(cx)
21648                .current_user()
21649                .map(|user| user.avatar_uri.clone())
21650        });
21651
21652        // Create anchor at the end of the last row so the block appears immediately below it
21653        // Use multibuffer coordinates for anchor creation
21654        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21655        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21656
21657        // Use the hunk key we already computed
21658        let hunk_key = new_hunk_key;
21659
21660        // Create the prompt editor for the review input
21661        let prompt_editor = cx.new(|cx| {
21662            let mut editor = Editor::single_line(window, cx);
21663            editor.set_placeholder_text("Add a review comment...", window, cx);
21664            editor
21665        });
21666
21667        // Register the Newline action on the prompt editor to submit the review
21668        let parent_editor = cx.entity().downgrade();
21669        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21670            prompt_editor.register_action({
21671                let parent_editor = parent_editor.clone();
21672                move |_: &crate::actions::Newline, window, cx| {
21673                    if let Some(editor) = parent_editor.upgrade() {
21674                        editor.update(cx, |editor, cx| {
21675                            editor.submit_diff_review_comment(window, cx);
21676                        });
21677                    }
21678                }
21679            })
21680        });
21681
21682        // Calculate initial height based on existing comments for this hunk
21683        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21684
21685        // Create the overlay block
21686        let prompt_editor_for_render = prompt_editor.clone();
21687        let hunk_key_for_render = hunk_key.clone();
21688        let editor_handle = cx.entity().downgrade();
21689        let block = BlockProperties {
21690            style: BlockStyle::Sticky,
21691            placement: BlockPlacement::Below(anchor),
21692            height: Some(initial_height),
21693            render: Arc::new(move |cx| {
21694                Self::render_diff_review_overlay(
21695                    &prompt_editor_for_render,
21696                    &hunk_key_for_render,
21697                    &editor_handle,
21698                    cx,
21699                )
21700            }),
21701            priority: 0,
21702        };
21703
21704        let block_ids = self.insert_blocks([block], None, cx);
21705        let Some(block_id) = block_ids.into_iter().next() else {
21706            log::error!("Failed to insert diff review overlay block");
21707            return;
21708        };
21709
21710        self.diff_review_overlays.push(DiffReviewOverlay {
21711            anchor_range,
21712            block_id,
21713            prompt_editor: prompt_editor.clone(),
21714            hunk_key,
21715            comments_expanded: true,
21716            inline_edit_editors: HashMap::default(),
21717            inline_edit_subscriptions: HashMap::default(),
21718            user_avatar_uri,
21719            _subscription: subscription,
21720        });
21721
21722        // Focus the prompt editor
21723        let focus_handle = prompt_editor.focus_handle(cx);
21724        window.focus(&focus_handle, cx);
21725
21726        cx.notify();
21727    }
21728
21729    /// Dismisses all diff review overlays.
21730    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21731        if self.diff_review_overlays.is_empty() {
21732            return;
21733        }
21734        let block_ids: HashSet<_> = self
21735            .diff_review_overlays
21736            .drain(..)
21737            .map(|overlay| overlay.block_id)
21738            .collect();
21739        self.remove_blocks(block_ids, None, cx);
21740        cx.notify();
21741    }
21742
21743    /// Dismisses overlays that have no comments stored for their hunks.
21744    /// Keeps overlays that have at least one comment.
21745    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21746        let snapshot = self.buffer.read(cx).snapshot(cx);
21747
21748        // First, compute which overlays have comments (to avoid borrow issues with retain)
21749        let overlays_with_comments: Vec<bool> = self
21750            .diff_review_overlays
21751            .iter()
21752            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21753            .collect();
21754
21755        // Now collect block IDs to remove and retain overlays
21756        let mut block_ids_to_remove = HashSet::default();
21757        let mut index = 0;
21758        self.diff_review_overlays.retain(|overlay| {
21759            let has_comments = overlays_with_comments[index];
21760            index += 1;
21761            if !has_comments {
21762                block_ids_to_remove.insert(overlay.block_id);
21763            }
21764            has_comments
21765        });
21766
21767        if !block_ids_to_remove.is_empty() {
21768            self.remove_blocks(block_ids_to_remove, None, cx);
21769            cx.notify();
21770        }
21771    }
21772
21773    /// Refreshes the diff review overlay block to update its height and render function.
21774    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21775    fn refresh_diff_review_overlay_height(
21776        &mut self,
21777        hunk_key: &DiffHunkKey,
21778        _window: &mut Window,
21779        cx: &mut Context<Self>,
21780    ) {
21781        // Extract all needed data from overlay first to avoid borrow conflicts
21782        let snapshot = self.buffer.read(cx).snapshot(cx);
21783        let (comments_expanded, block_id, prompt_editor) = {
21784            let Some(overlay) = self
21785                .diff_review_overlays
21786                .iter()
21787                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21788            else {
21789                return;
21790            };
21791
21792            (
21793                overlay.comments_expanded,
21794                overlay.block_id,
21795                overlay.prompt_editor.clone(),
21796            )
21797        };
21798
21799        // Calculate new height
21800        let snapshot = self.buffer.read(cx).snapshot(cx);
21801        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21802
21803        // Update the block height using resize_blocks (avoids flicker)
21804        let mut heights = HashMap::default();
21805        heights.insert(block_id, new_height);
21806        self.resize_blocks(heights, None, cx);
21807
21808        // Update the render function using replace_blocks (avoids flicker)
21809        let hunk_key_for_render = hunk_key.clone();
21810        let editor_handle = cx.entity().downgrade();
21811        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21812            Arc::new(move |cx| {
21813                Self::render_diff_review_overlay(
21814                    &prompt_editor,
21815                    &hunk_key_for_render,
21816                    &editor_handle,
21817                    cx,
21818                )
21819            });
21820
21821        let mut renderers = HashMap::default();
21822        renderers.insert(block_id, render);
21823        self.replace_blocks(renderers, None, cx);
21824    }
21825
21826    /// Action handler for SubmitDiffReviewComment.
21827    pub fn submit_diff_review_comment_action(
21828        &mut self,
21829        _: &SubmitDiffReviewComment,
21830        window: &mut Window,
21831        cx: &mut Context<Self>,
21832    ) {
21833        self.submit_diff_review_comment(window, cx);
21834    }
21835
21836    /// Stores the diff review comment locally.
21837    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21838    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21839        // Find the overlay that currently has focus
21840        let overlay_index = self
21841            .diff_review_overlays
21842            .iter()
21843            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21844        let Some(overlay_index) = overlay_index else {
21845            return;
21846        };
21847        let overlay = &self.diff_review_overlays[overlay_index];
21848
21849        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21850        if comment_text.is_empty() {
21851            return;
21852        }
21853
21854        let anchor_range = overlay.anchor_range.clone();
21855        let hunk_key = overlay.hunk_key.clone();
21856
21857        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21858
21859        // Clear the prompt editor but keep the overlay open
21860        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21861            overlay.prompt_editor.update(cx, |editor, cx| {
21862                editor.clear(window, cx);
21863            });
21864        }
21865
21866        // Refresh the overlay to update the block height for the new comment
21867        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21868
21869        cx.notify();
21870    }
21871
21872    /// Returns the prompt editor for the diff review overlay, if one is active.
21873    /// This is primarily used for testing.
21874    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21875        self.diff_review_overlays
21876            .first()
21877            .map(|overlay| &overlay.prompt_editor)
21878    }
21879
21880    /// Returns the line range for the first diff review overlay, if one is active.
21881    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21882    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21883        let overlay = self.diff_review_overlays.first()?;
21884        let snapshot = self.buffer.read(cx).snapshot(cx);
21885        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21886        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21887        let start_row = snapshot
21888            .point_to_buffer_point(start_point)
21889            .map(|(_, p, _)| p.row)
21890            .unwrap_or(start_point.row);
21891        let end_row = snapshot
21892            .point_to_buffer_point(end_point)
21893            .map(|(_, p, _)| p.row)
21894            .unwrap_or(end_point.row);
21895        Some((start_row, end_row))
21896    }
21897
21898    /// Sets whether the comments section is expanded in the diff review overlay.
21899    /// This is primarily used for testing.
21900    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21901        for overlay in &mut self.diff_review_overlays {
21902            overlay.comments_expanded = expanded;
21903        }
21904        cx.notify();
21905    }
21906
21907    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21908    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21909        a.file_path == b.file_path
21910            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21911    }
21912
21913    /// Returns comments for a specific hunk, ordered by creation time.
21914    pub fn comments_for_hunk<'a>(
21915        &'a self,
21916        key: &DiffHunkKey,
21917        snapshot: &MultiBufferSnapshot,
21918    ) -> &'a [StoredReviewComment] {
21919        let key_point = key.hunk_start_anchor.to_point(snapshot);
21920        self.stored_review_comments
21921            .iter()
21922            .find(|(k, _)| {
21923                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21924            })
21925            .map(|(_, comments)| comments.as_slice())
21926            .unwrap_or(&[])
21927    }
21928
21929    /// Returns the total count of stored review comments across all hunks.
21930    pub fn total_review_comment_count(&self) -> usize {
21931        self.stored_review_comments
21932            .iter()
21933            .map(|(_, v)| v.len())
21934            .sum()
21935    }
21936
21937    /// Returns the count of comments for a specific hunk.
21938    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21939        let key_point = key.hunk_start_anchor.to_point(snapshot);
21940        self.stored_review_comments
21941            .iter()
21942            .find(|(k, _)| {
21943                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21944            })
21945            .map(|(_, v)| v.len())
21946            .unwrap_or(0)
21947    }
21948
21949    /// Adds a new review comment to a specific hunk.
21950    pub fn add_review_comment(
21951        &mut self,
21952        hunk_key: DiffHunkKey,
21953        comment: String,
21954        anchor_range: Range<Anchor>,
21955        cx: &mut Context<Self>,
21956    ) -> usize {
21957        let id = self.next_review_comment_id;
21958        self.next_review_comment_id += 1;
21959
21960        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21961
21962        let snapshot = self.buffer.read(cx).snapshot(cx);
21963        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21964
21965        // Find existing entry for this hunk or add a new one
21966        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21967            k.file_path == hunk_key.file_path
21968                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21969        }) {
21970            comments.push(stored_comment);
21971        } else {
21972            self.stored_review_comments
21973                .push((hunk_key, vec![stored_comment]));
21974        }
21975
21976        cx.emit(EditorEvent::ReviewCommentsChanged {
21977            total_count: self.total_review_comment_count(),
21978        });
21979        cx.notify();
21980        id
21981    }
21982
21983    /// Removes a review comment by ID from any hunk.
21984    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21985        for (_, comments) in self.stored_review_comments.iter_mut() {
21986            if let Some(index) = comments.iter().position(|c| c.id == id) {
21987                comments.remove(index);
21988                cx.emit(EditorEvent::ReviewCommentsChanged {
21989                    total_count: self.total_review_comment_count(),
21990                });
21991                cx.notify();
21992                return true;
21993            }
21994        }
21995        false
21996    }
21997
21998    /// Updates a review comment's text by ID.
21999    pub fn update_review_comment(
22000        &mut self,
22001        id: usize,
22002        new_comment: String,
22003        cx: &mut Context<Self>,
22004    ) -> bool {
22005        for (_, comments) in self.stored_review_comments.iter_mut() {
22006            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22007                comment.comment = new_comment;
22008                comment.is_editing = false;
22009                cx.emit(EditorEvent::ReviewCommentsChanged {
22010                    total_count: self.total_review_comment_count(),
22011                });
22012                cx.notify();
22013                return true;
22014            }
22015        }
22016        false
22017    }
22018
22019    /// Sets a comment's editing state.
22020    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22021        for (_, comments) in self.stored_review_comments.iter_mut() {
22022            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22023                comment.is_editing = is_editing;
22024                cx.notify();
22025                return;
22026            }
22027        }
22028    }
22029
22030    /// Takes all stored comments from all hunks, clearing the storage.
22031    /// Returns a Vec of (hunk_key, comments) pairs.
22032    pub fn take_all_review_comments(
22033        &mut self,
22034        cx: &mut Context<Self>,
22035    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22036        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22037        self.dismiss_all_diff_review_overlays(cx);
22038        let comments = std::mem::take(&mut self.stored_review_comments);
22039        // Reset the ID counter since all comments have been taken
22040        self.next_review_comment_id = 0;
22041        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22042        cx.notify();
22043        comments
22044    }
22045
22046    /// Removes review comments whose anchors are no longer valid or whose
22047    /// associated diff hunks no longer exist.
22048    ///
22049    /// This should be called when the buffer changes to prevent orphaned comments
22050    /// from accumulating.
22051    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22052        let snapshot = self.buffer.read(cx).snapshot(cx);
22053        let original_count = self.total_review_comment_count();
22054
22055        // Remove comments with invalid hunk anchors
22056        self.stored_review_comments
22057            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22058
22059        // Also clean up individual comments with invalid anchor ranges
22060        for (_, comments) in &mut self.stored_review_comments {
22061            comments.retain(|comment| {
22062                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22063            });
22064        }
22065
22066        // Remove empty hunk entries
22067        self.stored_review_comments
22068            .retain(|(_, comments)| !comments.is_empty());
22069
22070        let new_count = self.total_review_comment_count();
22071        if new_count != original_count {
22072            cx.emit(EditorEvent::ReviewCommentsChanged {
22073                total_count: new_count,
22074            });
22075            cx.notify();
22076        }
22077    }
22078
22079    /// Toggles the expanded state of the comments section in the overlay.
22080    pub fn toggle_review_comments_expanded(
22081        &mut self,
22082        _: &ToggleReviewCommentsExpanded,
22083        window: &mut Window,
22084        cx: &mut Context<Self>,
22085    ) {
22086        // Find the overlay that currently has focus, or use the first one
22087        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22088            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22089                overlay.comments_expanded = !overlay.comments_expanded;
22090                Some(overlay.hunk_key.clone())
22091            } else {
22092                None
22093            }
22094        });
22095
22096        // If no focused overlay found, toggle the first one
22097        let hunk_key = overlay_info.or_else(|| {
22098            self.diff_review_overlays.first_mut().map(|overlay| {
22099                overlay.comments_expanded = !overlay.comments_expanded;
22100                overlay.hunk_key.clone()
22101            })
22102        });
22103
22104        if let Some(hunk_key) = hunk_key {
22105            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22106            cx.notify();
22107        }
22108    }
22109
22110    /// Handles the EditReviewComment action - sets a comment into editing mode.
22111    pub fn edit_review_comment(
22112        &mut self,
22113        action: &EditReviewComment,
22114        window: &mut Window,
22115        cx: &mut Context<Self>,
22116    ) {
22117        let comment_id = action.id;
22118
22119        // Set the comment to editing mode
22120        self.set_comment_editing(comment_id, true, cx);
22121
22122        // Find the overlay that contains this comment and create an inline editor if needed
22123        // First, find which hunk this comment belongs to
22124        let hunk_key = self
22125            .stored_review_comments
22126            .iter()
22127            .find_map(|(key, comments)| {
22128                if comments.iter().any(|c| c.id == comment_id) {
22129                    Some(key.clone())
22130                } else {
22131                    None
22132                }
22133            });
22134
22135        let snapshot = self.buffer.read(cx).snapshot(cx);
22136        if let Some(hunk_key) = hunk_key {
22137            if let Some(overlay) = self
22138                .diff_review_overlays
22139                .iter_mut()
22140                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22141            {
22142                if let std::collections::hash_map::Entry::Vacant(entry) =
22143                    overlay.inline_edit_editors.entry(comment_id)
22144                {
22145                    // Find the comment text
22146                    let comment_text = self
22147                        .stored_review_comments
22148                        .iter()
22149                        .flat_map(|(_, comments)| comments)
22150                        .find(|c| c.id == comment_id)
22151                        .map(|c| c.comment.clone())
22152                        .unwrap_or_default();
22153
22154                    // Create inline editor
22155                    let parent_editor = cx.entity().downgrade();
22156                    let inline_editor = cx.new(|cx| {
22157                        let mut editor = Editor::single_line(window, cx);
22158                        editor.set_text(&*comment_text, window, cx);
22159                        // Select all text for easy replacement
22160                        editor.select_all(&crate::actions::SelectAll, window, cx);
22161                        editor
22162                    });
22163
22164                    // Register the Newline action to confirm the edit
22165                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22166                        inline_editor.register_action({
22167                            let parent_editor = parent_editor.clone();
22168                            move |_: &crate::actions::Newline, window, cx| {
22169                                if let Some(editor) = parent_editor.upgrade() {
22170                                    editor.update(cx, |editor, cx| {
22171                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22172                                    });
22173                                }
22174                            }
22175                        })
22176                    });
22177
22178                    // Store the subscription to keep the action handler alive
22179                    overlay
22180                        .inline_edit_subscriptions
22181                        .insert(comment_id, subscription);
22182
22183                    // Focus the inline editor
22184                    let focus_handle = inline_editor.focus_handle(cx);
22185                    window.focus(&focus_handle, cx);
22186
22187                    entry.insert(inline_editor);
22188                }
22189            }
22190        }
22191
22192        cx.notify();
22193    }
22194
22195    /// Confirms an inline edit of a review comment.
22196    pub fn confirm_edit_review_comment(
22197        &mut self,
22198        comment_id: usize,
22199        _window: &mut Window,
22200        cx: &mut Context<Self>,
22201    ) {
22202        // Get the new text from the inline editor
22203        // Find the overlay containing this comment's inline editor
22204        let snapshot = self.buffer.read(cx).snapshot(cx);
22205        let hunk_key = self
22206            .stored_review_comments
22207            .iter()
22208            .find_map(|(key, comments)| {
22209                if comments.iter().any(|c| c.id == comment_id) {
22210                    Some(key.clone())
22211                } else {
22212                    None
22213                }
22214            });
22215
22216        let new_text = hunk_key
22217            .as_ref()
22218            .and_then(|hunk_key| {
22219                self.diff_review_overlays
22220                    .iter()
22221                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22222            })
22223            .as_ref()
22224            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22225            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22226
22227        if let Some(new_text) = new_text {
22228            if !new_text.is_empty() {
22229                self.update_review_comment(comment_id, new_text, cx);
22230            }
22231        }
22232
22233        // Remove the inline editor and its subscription
22234        if let Some(hunk_key) = hunk_key {
22235            if let Some(overlay) = self
22236                .diff_review_overlays
22237                .iter_mut()
22238                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22239            {
22240                overlay.inline_edit_editors.remove(&comment_id);
22241                overlay.inline_edit_subscriptions.remove(&comment_id);
22242            }
22243        }
22244
22245        // Clear editing state
22246        self.set_comment_editing(comment_id, false, cx);
22247    }
22248
22249    /// Cancels an inline edit of a review comment.
22250    pub fn cancel_edit_review_comment(
22251        &mut self,
22252        comment_id: usize,
22253        _window: &mut Window,
22254        cx: &mut Context<Self>,
22255    ) {
22256        // Find which hunk this comment belongs to
22257        let hunk_key = self
22258            .stored_review_comments
22259            .iter()
22260            .find_map(|(key, comments)| {
22261                if comments.iter().any(|c| c.id == comment_id) {
22262                    Some(key.clone())
22263                } else {
22264                    None
22265                }
22266            });
22267
22268        // Remove the inline editor and its subscription
22269        if let Some(hunk_key) = hunk_key {
22270            let snapshot = self.buffer.read(cx).snapshot(cx);
22271            if let Some(overlay) = self
22272                .diff_review_overlays
22273                .iter_mut()
22274                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22275            {
22276                overlay.inline_edit_editors.remove(&comment_id);
22277                overlay.inline_edit_subscriptions.remove(&comment_id);
22278            }
22279        }
22280
22281        // Clear editing state
22282        self.set_comment_editing(comment_id, false, cx);
22283    }
22284
22285    /// Action handler for ConfirmEditReviewComment.
22286    pub fn confirm_edit_review_comment_action(
22287        &mut self,
22288        action: &ConfirmEditReviewComment,
22289        window: &mut Window,
22290        cx: &mut Context<Self>,
22291    ) {
22292        self.confirm_edit_review_comment(action.id, window, cx);
22293    }
22294
22295    /// Action handler for CancelEditReviewComment.
22296    pub fn cancel_edit_review_comment_action(
22297        &mut self,
22298        action: &CancelEditReviewComment,
22299        window: &mut Window,
22300        cx: &mut Context<Self>,
22301    ) {
22302        self.cancel_edit_review_comment(action.id, window, cx);
22303    }
22304
22305    /// Handles the DeleteReviewComment action - removes a comment.
22306    pub fn delete_review_comment(
22307        &mut self,
22308        action: &DeleteReviewComment,
22309        window: &mut Window,
22310        cx: &mut Context<Self>,
22311    ) {
22312        // Get the hunk key before removing the comment
22313        // Find the hunk key from the comment itself
22314        let comment_id = action.id;
22315        let hunk_key = self
22316            .stored_review_comments
22317            .iter()
22318            .find_map(|(key, comments)| {
22319                if comments.iter().any(|c| c.id == comment_id) {
22320                    Some(key.clone())
22321                } else {
22322                    None
22323                }
22324            });
22325
22326        // Also get it from the overlay for refresh purposes
22327        let overlay_hunk_key = self
22328            .diff_review_overlays
22329            .first()
22330            .map(|o| o.hunk_key.clone());
22331
22332        self.remove_review_comment(action.id, cx);
22333
22334        // Refresh the overlay height after removing a comment
22335        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22336            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22337        }
22338    }
22339
22340    fn render_diff_review_overlay(
22341        prompt_editor: &Entity<Editor>,
22342        hunk_key: &DiffHunkKey,
22343        editor_handle: &WeakEntity<Editor>,
22344        cx: &mut BlockContext,
22345    ) -> AnyElement {
22346        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22347            if ranges.is_empty() {
22348                return None;
22349            }
22350            let formatted: Vec<String> = ranges
22351                .iter()
22352                .map(|(start, end)| {
22353                    let start_line = start + 1;
22354                    let end_line = end + 1;
22355                    if start_line == end_line {
22356                        format!("Line {start_line}")
22357                    } else {
22358                        format!("Lines {start_line}-{end_line}")
22359                    }
22360                })
22361                .collect();
22362            // Don't show label for single line in single excerpt
22363            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22364                return None;
22365            }
22366            Some(formatted.join(""))
22367        }
22368
22369        let theme = cx.theme();
22370        let colors = theme.colors();
22371
22372        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22373            editor_handle
22374                .upgrade()
22375                .map(|editor| {
22376                    let editor = editor.read(cx);
22377                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22378                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22379                    let (expanded, editors, avatar_uri, line_ranges) = editor
22380                        .diff_review_overlays
22381                        .iter()
22382                        .find(|overlay| {
22383                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22384                        })
22385                        .map(|o| {
22386                            let start_point = o.anchor_range.start.to_point(&snapshot);
22387                            let end_point = o.anchor_range.end.to_point(&snapshot);
22388                            // Get line ranges per excerpt to detect discontinuities
22389                            let buffer_ranges =
22390                                snapshot.range_to_buffer_ranges(start_point..end_point);
22391                            let ranges: Vec<(u32, u32)> = buffer_ranges
22392                                .iter()
22393                                .map(|(buffer, range, _)| {
22394                                    let start = buffer.offset_to_point(range.start.0).row;
22395                                    let end = buffer.offset_to_point(range.end.0).row;
22396                                    (start, end)
22397                                })
22398                                .collect();
22399                            (
22400                                o.comments_expanded,
22401                                o.inline_edit_editors.clone(),
22402                                o.user_avatar_uri.clone(),
22403                                if ranges.is_empty() {
22404                                    None
22405                                } else {
22406                                    Some(ranges)
22407                                },
22408                            )
22409                        })
22410                        .unwrap_or((true, HashMap::default(), None, None));
22411                    (comments, expanded, editors, avatar_uri, line_ranges)
22412                })
22413                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22414
22415        let comment_count = comments.len();
22416        let avatar_size = px(20.);
22417        let action_icon_size = IconSize::XSmall;
22418
22419        v_flex()
22420            .w_full()
22421            .bg(colors.editor_background)
22422            .border_b_1()
22423            .border_color(colors.border)
22424            .px_2()
22425            .pb_2()
22426            .gap_2()
22427            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22428            .when_some(line_ranges, |el, ranges| {
22429                let label = format_line_ranges(&ranges);
22430                if let Some(label) = label {
22431                    el.child(
22432                        h_flex()
22433                            .w_full()
22434                            .px_2()
22435                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22436                    )
22437                } else {
22438                    el
22439                }
22440            })
22441            // Top row: editable input with user's avatar
22442            .child(
22443                h_flex()
22444                    .w_full()
22445                    .items_center()
22446                    .gap_2()
22447                    .px_2()
22448                    .py_1p5()
22449                    .rounded_md()
22450                    .bg(colors.surface_background)
22451                    .child(
22452                        div()
22453                            .size(avatar_size)
22454                            .flex_shrink_0()
22455                            .rounded_full()
22456                            .overflow_hidden()
22457                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22458                                Avatar::new(avatar_uri.clone())
22459                                    .size(avatar_size)
22460                                    .into_any_element()
22461                            } else {
22462                                Icon::new(IconName::Person)
22463                                    .size(IconSize::Small)
22464                                    .color(ui::Color::Muted)
22465                                    .into_any_element()
22466                            }),
22467                    )
22468                    .child(
22469                        div()
22470                            .flex_1()
22471                            .border_1()
22472                            .border_color(colors.border)
22473                            .rounded_md()
22474                            .bg(colors.editor_background)
22475                            .px_2()
22476                            .py_1()
22477                            .child(prompt_editor.clone()),
22478                    )
22479                    .child(
22480                        h_flex()
22481                            .flex_shrink_0()
22482                            .gap_1()
22483                            .child(
22484                                IconButton::new("diff-review-close", IconName::Close)
22485                                    .icon_color(ui::Color::Muted)
22486                                    .icon_size(action_icon_size)
22487                                    .tooltip(Tooltip::text("Close"))
22488                                    .on_click(|_, window, cx| {
22489                                        window
22490                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22491                                    }),
22492                            )
22493                            .child(
22494                                IconButton::new("diff-review-add", IconName::Return)
22495                                    .icon_color(ui::Color::Muted)
22496                                    .icon_size(action_icon_size)
22497                                    .tooltip(Tooltip::text("Add comment"))
22498                                    .on_click(|_, window, cx| {
22499                                        window.dispatch_action(
22500                                            Box::new(crate::actions::SubmitDiffReviewComment),
22501                                            cx,
22502                                        );
22503                                    }),
22504                            ),
22505                    ),
22506            )
22507            // Expandable comments section (only shown when there are comments)
22508            .when(comment_count > 0, |el| {
22509                el.child(Self::render_comments_section(
22510                    comments,
22511                    comments_expanded,
22512                    inline_editors,
22513                    user_avatar_uri,
22514                    avatar_size,
22515                    action_icon_size,
22516                    colors,
22517                ))
22518            })
22519            .into_any_element()
22520    }
22521
22522    fn render_comments_section(
22523        comments: Vec<StoredReviewComment>,
22524        expanded: bool,
22525        inline_editors: HashMap<usize, Entity<Editor>>,
22526        user_avatar_uri: Option<SharedUri>,
22527        avatar_size: Pixels,
22528        action_icon_size: IconSize,
22529        colors: &theme::ThemeColors,
22530    ) -> impl IntoElement {
22531        let comment_count = comments.len();
22532
22533        v_flex()
22534            .w_full()
22535            .gap_1()
22536            // Header with expand/collapse toggle
22537            .child(
22538                h_flex()
22539                    .id("review-comments-header")
22540                    .w_full()
22541                    .items_center()
22542                    .gap_1()
22543                    .px_2()
22544                    .py_1()
22545                    .cursor_pointer()
22546                    .rounded_md()
22547                    .hover(|style| style.bg(colors.ghost_element_hover))
22548                    .on_click(|_, window: &mut Window, cx| {
22549                        window.dispatch_action(
22550                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22551                            cx,
22552                        );
22553                    })
22554                    .child(
22555                        Icon::new(if expanded {
22556                            IconName::ChevronDown
22557                        } else {
22558                            IconName::ChevronRight
22559                        })
22560                        .size(IconSize::Small)
22561                        .color(ui::Color::Muted),
22562                    )
22563                    .child(
22564                        Label::new(format!(
22565                            "{} Comment{}",
22566                            comment_count,
22567                            if comment_count == 1 { "" } else { "s" }
22568                        ))
22569                        .size(LabelSize::Small)
22570                        .color(Color::Muted),
22571                    ),
22572            )
22573            // Comments list (when expanded)
22574            .when(expanded, |el| {
22575                el.children(comments.into_iter().map(|comment| {
22576                    let inline_editor = inline_editors.get(&comment.id).cloned();
22577                    Self::render_comment_row(
22578                        comment,
22579                        inline_editor,
22580                        user_avatar_uri.clone(),
22581                        avatar_size,
22582                        action_icon_size,
22583                        colors,
22584                    )
22585                }))
22586            })
22587    }
22588
22589    fn render_comment_row(
22590        comment: StoredReviewComment,
22591        inline_editor: Option<Entity<Editor>>,
22592        user_avatar_uri: Option<SharedUri>,
22593        avatar_size: Pixels,
22594        action_icon_size: IconSize,
22595        colors: &theme::ThemeColors,
22596    ) -> impl IntoElement {
22597        let comment_id = comment.id;
22598        let is_editing = inline_editor.is_some();
22599
22600        h_flex()
22601            .w_full()
22602            .items_center()
22603            .gap_2()
22604            .px_2()
22605            .py_1p5()
22606            .rounded_md()
22607            .bg(colors.surface_background)
22608            .child(
22609                div()
22610                    .size(avatar_size)
22611                    .flex_shrink_0()
22612                    .rounded_full()
22613                    .overflow_hidden()
22614                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22615                        Avatar::new(avatar_uri.clone())
22616                            .size(avatar_size)
22617                            .into_any_element()
22618                    } else {
22619                        Icon::new(IconName::Person)
22620                            .size(IconSize::Small)
22621                            .color(ui::Color::Muted)
22622                            .into_any_element()
22623                    }),
22624            )
22625            .child(if let Some(editor) = inline_editor {
22626                // Inline edit mode: show an editable text field
22627                div()
22628                    .flex_1()
22629                    .border_1()
22630                    .border_color(colors.border)
22631                    .rounded_md()
22632                    .bg(colors.editor_background)
22633                    .px_2()
22634                    .py_1()
22635                    .child(editor)
22636                    .into_any_element()
22637            } else {
22638                // Display mode: show the comment text
22639                div()
22640                    .flex_1()
22641                    .text_sm()
22642                    .text_color(colors.text)
22643                    .child(comment.comment)
22644                    .into_any_element()
22645            })
22646            .child(if is_editing {
22647                // Editing mode: show close and confirm buttons
22648                h_flex()
22649                    .gap_1()
22650                    .child(
22651                        IconButton::new(
22652                            format!("diff-review-cancel-edit-{comment_id}"),
22653                            IconName::Close,
22654                        )
22655                        .icon_color(ui::Color::Muted)
22656                        .icon_size(action_icon_size)
22657                        .tooltip(Tooltip::text("Cancel"))
22658                        .on_click(move |_, window, cx| {
22659                            window.dispatch_action(
22660                                Box::new(crate::actions::CancelEditReviewComment {
22661                                    id: comment_id,
22662                                }),
22663                                cx,
22664                            );
22665                        }),
22666                    )
22667                    .child(
22668                        IconButton::new(
22669                            format!("diff-review-confirm-edit-{comment_id}"),
22670                            IconName::Return,
22671                        )
22672                        .icon_color(ui::Color::Muted)
22673                        .icon_size(action_icon_size)
22674                        .tooltip(Tooltip::text("Confirm"))
22675                        .on_click(move |_, window, cx| {
22676                            window.dispatch_action(
22677                                Box::new(crate::actions::ConfirmEditReviewComment {
22678                                    id: comment_id,
22679                                }),
22680                                cx,
22681                            );
22682                        }),
22683                    )
22684                    .into_any_element()
22685            } else {
22686                // Display mode: no action buttons for now (edit/delete not yet implemented)
22687                gpui::Empty.into_any_element()
22688            })
22689    }
22690
22691    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22692        if self.display_map.read(cx).masked != masked {
22693            self.display_map.update(cx, |map, _| map.masked = masked);
22694        }
22695        cx.notify()
22696    }
22697
22698    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22699        self.show_wrap_guides = Some(show_wrap_guides);
22700        cx.notify();
22701    }
22702
22703    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22704        self.show_indent_guides = Some(show_indent_guides);
22705        cx.notify();
22706    }
22707
22708    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22709        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22710            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22711                && let Some(dir) = file.abs_path(cx).parent()
22712            {
22713                return Some(dir.to_owned());
22714            }
22715        }
22716
22717        None
22718    }
22719
22720    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22721        self.active_excerpt(cx)?
22722            .1
22723            .read(cx)
22724            .file()
22725            .and_then(|f| f.as_local())
22726    }
22727
22728    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22729        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22730            let buffer = buffer.read(cx);
22731            if let Some(project_path) = buffer.project_path(cx) {
22732                let project = self.project()?.read(cx);
22733                project.absolute_path(&project_path, cx)
22734            } else {
22735                buffer
22736                    .file()
22737                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22738            }
22739        })
22740    }
22741
22742    pub fn reveal_in_finder(
22743        &mut self,
22744        _: &RevealInFileManager,
22745        _window: &mut Window,
22746        cx: &mut Context<Self>,
22747    ) {
22748        if let Some(path) = self.target_file_abs_path(cx) {
22749            if let Some(project) = self.project() {
22750                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22751            } else {
22752                cx.reveal_path(&path);
22753            }
22754        }
22755    }
22756
22757    pub fn copy_path(
22758        &mut self,
22759        _: &zed_actions::workspace::CopyPath,
22760        _window: &mut Window,
22761        cx: &mut Context<Self>,
22762    ) {
22763        if let Some(path) = self.target_file_abs_path(cx)
22764            && let Some(path) = path.to_str()
22765        {
22766            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22767        } else {
22768            cx.propagate();
22769        }
22770    }
22771
22772    pub fn copy_relative_path(
22773        &mut self,
22774        _: &zed_actions::workspace::CopyRelativePath,
22775        _window: &mut Window,
22776        cx: &mut Context<Self>,
22777    ) {
22778        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22779            let project = self.project()?.read(cx);
22780            let path = buffer.read(cx).file()?.path();
22781            let path = path.display(project.path_style(cx));
22782            Some(path)
22783        }) {
22784            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22785        } else {
22786            cx.propagate();
22787        }
22788    }
22789
22790    /// Returns the project path for the editor's buffer, if any buffer is
22791    /// opened in the editor.
22792    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22793        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22794            buffer.read(cx).project_path(cx)
22795        } else {
22796            None
22797        }
22798    }
22799
22800    // Returns true if the editor handled a go-to-line request
22801    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22802        maybe!({
22803            let breakpoint_store = self.breakpoint_store.as_ref()?;
22804
22805            let (active_stack_frame, debug_line_pane_id) = {
22806                let store = breakpoint_store.read(cx);
22807                let active_stack_frame = store.active_position().cloned();
22808                let debug_line_pane_id = store.active_debug_line_pane_id();
22809                (active_stack_frame, debug_line_pane_id)
22810            };
22811
22812            let Some(active_stack_frame) = active_stack_frame else {
22813                self.clear_row_highlights::<ActiveDebugLine>();
22814                return None;
22815            };
22816
22817            if let Some(debug_line_pane_id) = debug_line_pane_id {
22818                if let Some(workspace) = self
22819                    .workspace
22820                    .as_ref()
22821                    .and_then(|(workspace, _)| workspace.upgrade())
22822                {
22823                    let editor_pane_id = workspace
22824                        .read(cx)
22825                        .pane_for_item_id(cx.entity_id())
22826                        .map(|pane| pane.entity_id());
22827
22828                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22829                        self.clear_row_highlights::<ActiveDebugLine>();
22830                        return None;
22831                    }
22832                }
22833            }
22834
22835            let position = active_stack_frame.position;
22836            let buffer_id = position.buffer_id?;
22837            let snapshot = self
22838                .project
22839                .as_ref()?
22840                .read(cx)
22841                .buffer_for_id(buffer_id, cx)?
22842                .read(cx)
22843                .snapshot();
22844
22845            let mut handled = false;
22846            for (id, ExcerptRange { context, .. }) in
22847                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22848            {
22849                if context.start.cmp(&position, &snapshot).is_ge()
22850                    || context.end.cmp(&position, &snapshot).is_lt()
22851                {
22852                    continue;
22853                }
22854                let snapshot = self.buffer.read(cx).snapshot(cx);
22855                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22856
22857                handled = true;
22858                self.clear_row_highlights::<ActiveDebugLine>();
22859
22860                self.go_to_line::<ActiveDebugLine>(
22861                    multibuffer_anchor,
22862                    Some(cx.theme().colors().editor_debugger_active_line_background),
22863                    window,
22864                    cx,
22865                );
22866
22867                cx.notify();
22868            }
22869
22870            handled.then_some(())
22871        })
22872        .is_some()
22873    }
22874
22875    pub fn copy_file_name_without_extension(
22876        &mut self,
22877        _: &CopyFileNameWithoutExtension,
22878        _: &mut Window,
22879        cx: &mut Context<Self>,
22880    ) {
22881        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22882            let file = buffer.read(cx).file()?;
22883            file.path().file_stem()
22884        }) {
22885            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22886        }
22887    }
22888
22889    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22890        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22891            let file = buffer.read(cx).file()?;
22892            Some(file.file_name(cx))
22893        }) {
22894            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22895        }
22896    }
22897
22898    pub fn toggle_git_blame(
22899        &mut self,
22900        _: &::git::Blame,
22901        window: &mut Window,
22902        cx: &mut Context<Self>,
22903    ) {
22904        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22905
22906        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22907            self.start_git_blame(true, window, cx);
22908        }
22909
22910        cx.notify();
22911    }
22912
22913    pub fn toggle_git_blame_inline(
22914        &mut self,
22915        _: &ToggleGitBlameInline,
22916        window: &mut Window,
22917        cx: &mut Context<Self>,
22918    ) {
22919        self.toggle_git_blame_inline_internal(true, window, cx);
22920        cx.notify();
22921    }
22922
22923    pub fn open_git_blame_commit(
22924        &mut self,
22925        _: &OpenGitBlameCommit,
22926        window: &mut Window,
22927        cx: &mut Context<Self>,
22928    ) {
22929        self.open_git_blame_commit_internal(window, cx);
22930    }
22931
22932    fn open_git_blame_commit_internal(
22933        &mut self,
22934        window: &mut Window,
22935        cx: &mut Context<Self>,
22936    ) -> Option<()> {
22937        let blame = self.blame.as_ref()?;
22938        let snapshot = self.snapshot(window, cx);
22939        let cursor = self
22940            .selections
22941            .newest::<Point>(&snapshot.display_snapshot)
22942            .head();
22943        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22944        let (_, blame_entry) = blame
22945            .update(cx, |blame, cx| {
22946                blame
22947                    .blame_for_rows(
22948                        &[RowInfo {
22949                            buffer_id: Some(buffer.remote_id()),
22950                            buffer_row: Some(point.row),
22951                            ..Default::default()
22952                        }],
22953                        cx,
22954                    )
22955                    .next()
22956            })
22957            .flatten()?;
22958        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22959        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22960        let workspace = self.workspace()?.downgrade();
22961        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22962        None
22963    }
22964
22965    pub fn git_blame_inline_enabled(&self) -> bool {
22966        self.git_blame_inline_enabled
22967    }
22968
22969    pub fn toggle_selection_menu(
22970        &mut self,
22971        _: &ToggleSelectionMenu,
22972        _: &mut Window,
22973        cx: &mut Context<Self>,
22974    ) {
22975        self.show_selection_menu = self
22976            .show_selection_menu
22977            .map(|show_selections_menu| !show_selections_menu)
22978            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22979
22980        cx.notify();
22981    }
22982
22983    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22984        self.show_selection_menu
22985            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22986    }
22987
22988    fn start_git_blame(
22989        &mut self,
22990        user_triggered: bool,
22991        window: &mut Window,
22992        cx: &mut Context<Self>,
22993    ) {
22994        if let Some(project) = self.project() {
22995            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22996                && buffer.read(cx).file().is_none()
22997            {
22998                return;
22999            }
23000
23001            let focused = self.focus_handle(cx).contains_focused(window, cx);
23002
23003            let project = project.clone();
23004            let blame = cx
23005                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23006            self.blame_subscription =
23007                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23008            self.blame = Some(blame);
23009        }
23010    }
23011
23012    fn toggle_git_blame_inline_internal(
23013        &mut self,
23014        user_triggered: bool,
23015        window: &mut Window,
23016        cx: &mut Context<Self>,
23017    ) {
23018        if self.git_blame_inline_enabled {
23019            self.git_blame_inline_enabled = false;
23020            self.show_git_blame_inline = false;
23021            self.show_git_blame_inline_delay_task.take();
23022        } else {
23023            self.git_blame_inline_enabled = true;
23024            self.start_git_blame_inline(user_triggered, window, cx);
23025        }
23026
23027        cx.notify();
23028    }
23029
23030    fn start_git_blame_inline(
23031        &mut self,
23032        user_triggered: bool,
23033        window: &mut Window,
23034        cx: &mut Context<Self>,
23035    ) {
23036        self.start_git_blame(user_triggered, window, cx);
23037
23038        if ProjectSettings::get_global(cx)
23039            .git
23040            .inline_blame_delay()
23041            .is_some()
23042        {
23043            self.start_inline_blame_timer(window, cx);
23044        } else {
23045            self.show_git_blame_inline = true
23046        }
23047    }
23048
23049    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23050        self.blame.as_ref()
23051    }
23052
23053    pub fn show_git_blame_gutter(&self) -> bool {
23054        self.show_git_blame_gutter
23055    }
23056
23057    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23058        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23059    }
23060
23061    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23062        self.show_git_blame_inline
23063            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23064            && !self.newest_selection_head_on_empty_line(cx)
23065            && self.has_blame_entries(cx)
23066    }
23067
23068    fn has_blame_entries(&self, cx: &App) -> bool {
23069        self.blame()
23070            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23071    }
23072
23073    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23074        let cursor_anchor = self.selections.newest_anchor().head();
23075
23076        let snapshot = self.buffer.read(cx).snapshot(cx);
23077        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23078
23079        snapshot.line_len(buffer_row) == 0
23080    }
23081
23082    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23083        let buffer_and_selection = maybe!({
23084            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23085            let selection_range = selection.range();
23086
23087            let multi_buffer = self.buffer().read(cx);
23088            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23089            let buffer_ranges = multi_buffer_snapshot
23090                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23091
23092            let (buffer, range, _) = if selection.reversed {
23093                buffer_ranges.first()
23094            } else {
23095                buffer_ranges.last()
23096            }?;
23097
23098            let buffer_range = range.to_point(buffer);
23099
23100            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23101                return Some((
23102                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23103                    buffer_range.start.row..buffer_range.end.row,
23104                ));
23105            };
23106
23107            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23108            let start =
23109                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23110            let end =
23111                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23112
23113            Some((
23114                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23115                start.row..end.row,
23116            ))
23117        });
23118
23119        let Some((buffer, selection)) = buffer_and_selection else {
23120            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23121        };
23122
23123        let Some(project) = self.project() else {
23124            return Task::ready(Err(anyhow!("editor does not have project")));
23125        };
23126
23127        project.update(cx, |project, cx| {
23128            project.get_permalink_to_line(&buffer, selection, cx)
23129        })
23130    }
23131
23132    pub fn copy_permalink_to_line(
23133        &mut self,
23134        _: &CopyPermalinkToLine,
23135        window: &mut Window,
23136        cx: &mut Context<Self>,
23137    ) {
23138        let permalink_task = self.get_permalink_to_line(cx);
23139        let workspace = self.workspace();
23140
23141        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23142            Ok(permalink) => {
23143                cx.update(|_, cx| {
23144                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23145                })
23146                .ok();
23147            }
23148            Err(err) => {
23149                let message = format!("Failed to copy permalink: {err}");
23150
23151                anyhow::Result::<()>::Err(err).log_err();
23152
23153                if let Some(workspace) = workspace {
23154                    workspace
23155                        .update_in(cx, |workspace, _, cx| {
23156                            struct CopyPermalinkToLine;
23157
23158                            workspace.show_toast(
23159                                Toast::new(
23160                                    NotificationId::unique::<CopyPermalinkToLine>(),
23161                                    message,
23162                                ),
23163                                cx,
23164                            )
23165                        })
23166                        .ok();
23167                }
23168            }
23169        })
23170        .detach();
23171    }
23172
23173    pub fn copy_file_location(
23174        &mut self,
23175        _: &CopyFileLocation,
23176        _: &mut Window,
23177        cx: &mut Context<Self>,
23178    ) {
23179        let selection = self
23180            .selections
23181            .newest::<Point>(&self.display_snapshot(cx))
23182            .start
23183            .row
23184            + 1;
23185        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23186            let project = self.project()?.read(cx);
23187            let file = buffer.read(cx).file()?;
23188            let path = file.path().display(project.path_style(cx));
23189
23190            Some(format!("{path}:{selection}"))
23191        }) {
23192            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23193        }
23194    }
23195
23196    pub fn open_permalink_to_line(
23197        &mut self,
23198        _: &OpenPermalinkToLine,
23199        window: &mut Window,
23200        cx: &mut Context<Self>,
23201    ) {
23202        let permalink_task = self.get_permalink_to_line(cx);
23203        let workspace = self.workspace();
23204
23205        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23206            Ok(permalink) => {
23207                cx.update(|_, cx| {
23208                    cx.open_url(permalink.as_ref());
23209                })
23210                .ok();
23211            }
23212            Err(err) => {
23213                let message = format!("Failed to open permalink: {err}");
23214
23215                anyhow::Result::<()>::Err(err).log_err();
23216
23217                if let Some(workspace) = workspace {
23218                    workspace.update(cx, |workspace, cx| {
23219                        struct OpenPermalinkToLine;
23220
23221                        workspace.show_toast(
23222                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23223                            cx,
23224                        )
23225                    });
23226                }
23227            }
23228        })
23229        .detach();
23230    }
23231
23232    pub fn insert_uuid_v4(
23233        &mut self,
23234        _: &InsertUuidV4,
23235        window: &mut Window,
23236        cx: &mut Context<Self>,
23237    ) {
23238        self.insert_uuid(UuidVersion::V4, window, cx);
23239    }
23240
23241    pub fn insert_uuid_v7(
23242        &mut self,
23243        _: &InsertUuidV7,
23244        window: &mut Window,
23245        cx: &mut Context<Self>,
23246    ) {
23247        self.insert_uuid(UuidVersion::V7, window, cx);
23248    }
23249
23250    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23251        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23252        self.transact(window, cx, |this, window, cx| {
23253            let edits = this
23254                .selections
23255                .all::<Point>(&this.display_snapshot(cx))
23256                .into_iter()
23257                .map(|selection| {
23258                    let uuid = match version {
23259                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23260                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23261                    };
23262
23263                    (selection.range(), uuid.to_string())
23264                });
23265            this.edit(edits, cx);
23266            this.refresh_edit_prediction(true, false, window, cx);
23267        });
23268    }
23269
23270    pub fn open_selections_in_multibuffer(
23271        &mut self,
23272        _: &OpenSelectionsInMultibuffer,
23273        window: &mut Window,
23274        cx: &mut Context<Self>,
23275    ) {
23276        let multibuffer = self.buffer.read(cx);
23277
23278        let Some(buffer) = multibuffer.as_singleton() else {
23279            return;
23280        };
23281
23282        let Some(workspace) = self.workspace() else {
23283            return;
23284        };
23285
23286        let title = multibuffer.title(cx).to_string();
23287
23288        let locations = self
23289            .selections
23290            .all_anchors(&self.display_snapshot(cx))
23291            .iter()
23292            .map(|selection| {
23293                (
23294                    buffer.clone(),
23295                    (selection.start.text_anchor..selection.end.text_anchor)
23296                        .to_point(buffer.read(cx)),
23297                )
23298            })
23299            .into_group_map();
23300
23301        cx.spawn_in(window, async move |_, cx| {
23302            workspace.update_in(cx, |workspace, window, cx| {
23303                Self::open_locations_in_multibuffer(
23304                    workspace,
23305                    locations,
23306                    format!("Selections for '{title}'"),
23307                    false,
23308                    false,
23309                    MultibufferSelectionMode::All,
23310                    window,
23311                    cx,
23312                );
23313            })
23314        })
23315        .detach();
23316    }
23317
23318    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23319    /// last highlight added will be used.
23320    ///
23321    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23322    pub fn highlight_rows<T: 'static>(
23323        &mut self,
23324        range: Range<Anchor>,
23325        color: Hsla,
23326        options: RowHighlightOptions,
23327        cx: &mut Context<Self>,
23328    ) {
23329        let snapshot = self.buffer().read(cx).snapshot(cx);
23330        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23331        let ix = row_highlights.binary_search_by(|highlight| {
23332            Ordering::Equal
23333                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23334                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23335        });
23336
23337        if let Err(mut ix) = ix {
23338            let index = post_inc(&mut self.highlight_order);
23339
23340            // If this range intersects with the preceding highlight, then merge it with
23341            // the preceding highlight. Otherwise insert a new highlight.
23342            let mut merged = false;
23343            if ix > 0 {
23344                let prev_highlight = &mut row_highlights[ix - 1];
23345                if prev_highlight
23346                    .range
23347                    .end
23348                    .cmp(&range.start, &snapshot)
23349                    .is_ge()
23350                {
23351                    ix -= 1;
23352                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23353                        prev_highlight.range.end = range.end;
23354                    }
23355                    merged = true;
23356                    prev_highlight.index = index;
23357                    prev_highlight.color = color;
23358                    prev_highlight.options = options;
23359                }
23360            }
23361
23362            if !merged {
23363                row_highlights.insert(
23364                    ix,
23365                    RowHighlight {
23366                        range,
23367                        index,
23368                        color,
23369                        options,
23370                        type_id: TypeId::of::<T>(),
23371                    },
23372                );
23373            }
23374
23375            // If any of the following highlights intersect with this one, merge them.
23376            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23377                let highlight = &row_highlights[ix];
23378                if next_highlight
23379                    .range
23380                    .start
23381                    .cmp(&highlight.range.end, &snapshot)
23382                    .is_le()
23383                {
23384                    if next_highlight
23385                        .range
23386                        .end
23387                        .cmp(&highlight.range.end, &snapshot)
23388                        .is_gt()
23389                    {
23390                        row_highlights[ix].range.end = next_highlight.range.end;
23391                    }
23392                    row_highlights.remove(ix + 1);
23393                } else {
23394                    break;
23395                }
23396            }
23397        }
23398    }
23399
23400    /// Remove any highlighted row ranges of the given type that intersect the
23401    /// given ranges.
23402    pub fn remove_highlighted_rows<T: 'static>(
23403        &mut self,
23404        ranges_to_remove: Vec<Range<Anchor>>,
23405        cx: &mut Context<Self>,
23406    ) {
23407        let snapshot = self.buffer().read(cx).snapshot(cx);
23408        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23409        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23410        row_highlights.retain(|highlight| {
23411            while let Some(range_to_remove) = ranges_to_remove.peek() {
23412                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23413                    Ordering::Less | Ordering::Equal => {
23414                        ranges_to_remove.next();
23415                    }
23416                    Ordering::Greater => {
23417                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23418                            Ordering::Less | Ordering::Equal => {
23419                                return false;
23420                            }
23421                            Ordering::Greater => break,
23422                        }
23423                    }
23424                }
23425            }
23426
23427            true
23428        })
23429    }
23430
23431    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23432    pub fn clear_row_highlights<T: 'static>(&mut self) {
23433        self.highlighted_rows.remove(&TypeId::of::<T>());
23434    }
23435
23436    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23437    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23438        self.highlighted_rows
23439            .get(&TypeId::of::<T>())
23440            .map_or(&[] as &[_], |vec| vec.as_slice())
23441            .iter()
23442            .map(|highlight| (highlight.range.clone(), highlight.color))
23443    }
23444
23445    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23446    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23447    /// Allows to ignore certain kinds of highlights.
23448    pub fn highlighted_display_rows(
23449        &self,
23450        window: &mut Window,
23451        cx: &mut App,
23452    ) -> BTreeMap<DisplayRow, LineHighlight> {
23453        let snapshot = self.snapshot(window, cx);
23454        let mut used_highlight_orders = HashMap::default();
23455        self.highlighted_rows
23456            .iter()
23457            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23458            .fold(
23459                BTreeMap::<DisplayRow, LineHighlight>::new(),
23460                |mut unique_rows, highlight| {
23461                    let start = highlight.range.start.to_display_point(&snapshot);
23462                    let end = highlight.range.end.to_display_point(&snapshot);
23463                    let start_row = start.row().0;
23464                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23465                    {
23466                        end.row().0.saturating_sub(1)
23467                    } else {
23468                        end.row().0
23469                    };
23470                    for row in start_row..=end_row {
23471                        let used_index =
23472                            used_highlight_orders.entry(row).or_insert(highlight.index);
23473                        if highlight.index >= *used_index {
23474                            *used_index = highlight.index;
23475                            unique_rows.insert(
23476                                DisplayRow(row),
23477                                LineHighlight {
23478                                    include_gutter: highlight.options.include_gutter,
23479                                    border: None,
23480                                    background: highlight.color.into(),
23481                                    type_id: Some(highlight.type_id),
23482                                },
23483                            );
23484                        }
23485                    }
23486                    unique_rows
23487                },
23488            )
23489    }
23490
23491    pub fn highlighted_display_row_for_autoscroll(
23492        &self,
23493        snapshot: &DisplaySnapshot,
23494    ) -> Option<DisplayRow> {
23495        self.highlighted_rows
23496            .values()
23497            .flat_map(|highlighted_rows| highlighted_rows.iter())
23498            .filter_map(|highlight| {
23499                if highlight.options.autoscroll {
23500                    Some(highlight.range.start.to_display_point(snapshot).row())
23501                } else {
23502                    None
23503                }
23504            })
23505            .min()
23506    }
23507
23508    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23509        self.highlight_background(
23510            HighlightKey::SearchWithinRange,
23511            ranges,
23512            |_, colors| colors.colors().editor_document_highlight_read_background,
23513            cx,
23514        )
23515    }
23516
23517    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23518        self.breadcrumb_header = Some(new_header);
23519    }
23520
23521    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23522        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23523    }
23524
23525    pub fn highlight_background(
23526        &mut self,
23527        key: HighlightKey,
23528        ranges: &[Range<Anchor>],
23529        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23530        cx: &mut Context<Self>,
23531    ) {
23532        self.background_highlights
23533            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23534        self.scrollbar_marker_state.dirty = true;
23535        cx.notify();
23536    }
23537
23538    pub fn clear_background_highlights(
23539        &mut self,
23540        key: HighlightKey,
23541        cx: &mut Context<Self>,
23542    ) -> Option<BackgroundHighlight> {
23543        let text_highlights = self.background_highlights.remove(&key)?;
23544        if !text_highlights.1.is_empty() {
23545            self.scrollbar_marker_state.dirty = true;
23546            cx.notify();
23547        }
23548        Some(text_highlights)
23549    }
23550
23551    pub fn highlight_gutter<T: 'static>(
23552        &mut self,
23553        ranges: impl Into<Vec<Range<Anchor>>>,
23554        color_fetcher: fn(&App) -> Hsla,
23555        cx: &mut Context<Self>,
23556    ) {
23557        self.gutter_highlights
23558            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23559        cx.notify();
23560    }
23561
23562    pub fn clear_gutter_highlights<T: 'static>(
23563        &mut self,
23564        cx: &mut Context<Self>,
23565    ) -> Option<GutterHighlight> {
23566        cx.notify();
23567        self.gutter_highlights.remove(&TypeId::of::<T>())
23568    }
23569
23570    pub fn insert_gutter_highlight<T: 'static>(
23571        &mut self,
23572        range: Range<Anchor>,
23573        color_fetcher: fn(&App) -> Hsla,
23574        cx: &mut Context<Self>,
23575    ) {
23576        let snapshot = self.buffer().read(cx).snapshot(cx);
23577        let mut highlights = self
23578            .gutter_highlights
23579            .remove(&TypeId::of::<T>())
23580            .map(|(_, highlights)| highlights)
23581            .unwrap_or_default();
23582        let ix = highlights.binary_search_by(|highlight| {
23583            Ordering::Equal
23584                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23585                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23586        });
23587        if let Err(ix) = ix {
23588            highlights.insert(ix, range);
23589        }
23590        self.gutter_highlights
23591            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23592    }
23593
23594    pub fn remove_gutter_highlights<T: 'static>(
23595        &mut self,
23596        ranges_to_remove: Vec<Range<Anchor>>,
23597        cx: &mut Context<Self>,
23598    ) {
23599        let snapshot = self.buffer().read(cx).snapshot(cx);
23600        let Some((color_fetcher, mut gutter_highlights)) =
23601            self.gutter_highlights.remove(&TypeId::of::<T>())
23602        else {
23603            return;
23604        };
23605        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23606        gutter_highlights.retain(|highlight| {
23607            while let Some(range_to_remove) = ranges_to_remove.peek() {
23608                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23609                    Ordering::Less | Ordering::Equal => {
23610                        ranges_to_remove.next();
23611                    }
23612                    Ordering::Greater => {
23613                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23614                            Ordering::Less | Ordering::Equal => {
23615                                return false;
23616                            }
23617                            Ordering::Greater => break,
23618                        }
23619                    }
23620                }
23621            }
23622
23623            true
23624        });
23625        self.gutter_highlights
23626            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23627    }
23628
23629    #[cfg(any(test, feature = "test-support"))]
23630    pub fn all_text_highlights(
23631        &self,
23632        window: &mut Window,
23633        cx: &mut Context<Self>,
23634    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23635        let snapshot = self.snapshot(window, cx);
23636        self.display_map.update(cx, |display_map, _| {
23637            display_map
23638                .all_text_highlights()
23639                .map(|(_, highlight)| {
23640                    let (style, ranges) = highlight.as_ref();
23641                    (
23642                        *style,
23643                        ranges
23644                            .iter()
23645                            .map(|range| range.clone().to_display_points(&snapshot))
23646                            .collect(),
23647                    )
23648                })
23649                .collect()
23650        })
23651    }
23652
23653    #[cfg(any(test, feature = "test-support"))]
23654    pub fn all_text_background_highlights(
23655        &self,
23656        window: &mut Window,
23657        cx: &mut Context<Self>,
23658    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23659        let snapshot = self.snapshot(window, cx);
23660        let buffer = &snapshot.buffer_snapshot();
23661        let start = buffer.anchor_before(MultiBufferOffset(0));
23662        let end = buffer.anchor_after(buffer.len());
23663        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23664    }
23665
23666    #[cfg(any(test, feature = "test-support"))]
23667    pub fn sorted_background_highlights_in_range(
23668        &self,
23669        search_range: Range<Anchor>,
23670        display_snapshot: &DisplaySnapshot,
23671        theme: &Theme,
23672    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23673        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23674        res.sort_by(|a, b| {
23675            a.0.start
23676                .cmp(&b.0.start)
23677                .then_with(|| a.0.end.cmp(&b.0.end))
23678                .then_with(|| a.1.cmp(&b.1))
23679        });
23680        res
23681    }
23682
23683    #[cfg(any(test, feature = "test-support"))]
23684    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23685        let snapshot = self.buffer().read(cx).snapshot(cx);
23686
23687        let highlights = self
23688            .background_highlights
23689            .get(&HighlightKey::BufferSearchHighlights);
23690
23691        if let Some((_color, ranges)) = highlights {
23692            ranges
23693                .iter()
23694                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23695                .collect_vec()
23696        } else {
23697            vec![]
23698        }
23699    }
23700
23701    fn document_highlights_for_position<'a>(
23702        &'a self,
23703        position: Anchor,
23704        buffer: &'a MultiBufferSnapshot,
23705    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23706        let read_highlights = self
23707            .background_highlights
23708            .get(&HighlightKey::DocumentHighlightRead)
23709            .map(|h| &h.1);
23710        let write_highlights = self
23711            .background_highlights
23712            .get(&HighlightKey::DocumentHighlightWrite)
23713            .map(|h| &h.1);
23714        let left_position = position.bias_left(buffer);
23715        let right_position = position.bias_right(buffer);
23716        read_highlights
23717            .into_iter()
23718            .chain(write_highlights)
23719            .flat_map(move |ranges| {
23720                let start_ix = match ranges.binary_search_by(|probe| {
23721                    let cmp = probe.end.cmp(&left_position, buffer);
23722                    if cmp.is_ge() {
23723                        Ordering::Greater
23724                    } else {
23725                        Ordering::Less
23726                    }
23727                }) {
23728                    Ok(i) | Err(i) => i,
23729                };
23730
23731                ranges[start_ix..]
23732                    .iter()
23733                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23734            })
23735    }
23736
23737    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23738        self.background_highlights
23739            .get(&key)
23740            .is_some_and(|(_, highlights)| !highlights.is_empty())
23741    }
23742
23743    /// Returns all background highlights for a given range.
23744    ///
23745    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23746    pub fn background_highlights_in_range(
23747        &self,
23748        search_range: Range<Anchor>,
23749        display_snapshot: &DisplaySnapshot,
23750        theme: &Theme,
23751    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23752        let mut results = Vec::new();
23753        for (color_fetcher, ranges) in self.background_highlights.values() {
23754            let start_ix = match ranges.binary_search_by(|probe| {
23755                let cmp = probe
23756                    .end
23757                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23758                if cmp.is_gt() {
23759                    Ordering::Greater
23760                } else {
23761                    Ordering::Less
23762                }
23763            }) {
23764                Ok(i) | Err(i) => i,
23765            };
23766            for (index, range) in ranges[start_ix..].iter().enumerate() {
23767                if range
23768                    .start
23769                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23770                    .is_ge()
23771                {
23772                    break;
23773                }
23774
23775                let color = color_fetcher(&(start_ix + index), theme);
23776                let start = range.start.to_display_point(display_snapshot);
23777                let end = range.end.to_display_point(display_snapshot);
23778                results.push((start..end, color))
23779            }
23780        }
23781        results
23782    }
23783
23784    pub fn gutter_highlights_in_range(
23785        &self,
23786        search_range: Range<Anchor>,
23787        display_snapshot: &DisplaySnapshot,
23788        cx: &App,
23789    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23790        let mut results = Vec::new();
23791        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23792            let color = color_fetcher(cx);
23793            let start_ix = match ranges.binary_search_by(|probe| {
23794                let cmp = probe
23795                    .end
23796                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23797                if cmp.is_gt() {
23798                    Ordering::Greater
23799                } else {
23800                    Ordering::Less
23801                }
23802            }) {
23803                Ok(i) | Err(i) => i,
23804            };
23805            for range in &ranges[start_ix..] {
23806                if range
23807                    .start
23808                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23809                    .is_ge()
23810                {
23811                    break;
23812                }
23813
23814                let start = range.start.to_display_point(display_snapshot);
23815                let end = range.end.to_display_point(display_snapshot);
23816                results.push((start..end, color))
23817            }
23818        }
23819        results
23820    }
23821
23822    /// Get the text ranges corresponding to the redaction query
23823    pub fn redacted_ranges(
23824        &self,
23825        search_range: Range<Anchor>,
23826        display_snapshot: &DisplaySnapshot,
23827        cx: &App,
23828    ) -> Vec<Range<DisplayPoint>> {
23829        display_snapshot
23830            .buffer_snapshot()
23831            .redacted_ranges(search_range, |file| {
23832                if let Some(file) = file {
23833                    file.is_private()
23834                        && EditorSettings::get(
23835                            Some(SettingsLocation {
23836                                worktree_id: file.worktree_id(cx),
23837                                path: file.path().as_ref(),
23838                            }),
23839                            cx,
23840                        )
23841                        .redact_private_values
23842                } else {
23843                    false
23844                }
23845            })
23846            .map(|range| {
23847                range.start.to_display_point(display_snapshot)
23848                    ..range.end.to_display_point(display_snapshot)
23849            })
23850            .collect()
23851    }
23852
23853    pub fn highlight_text_key(
23854        &mut self,
23855        key: HighlightKey,
23856        ranges: Vec<Range<Anchor>>,
23857        style: HighlightStyle,
23858        merge: bool,
23859        cx: &mut Context<Self>,
23860    ) {
23861        self.display_map.update(cx, |map, cx| {
23862            map.highlight_text(key, ranges, style, merge, cx);
23863        });
23864        cx.notify();
23865    }
23866
23867    pub fn highlight_text(
23868        &mut self,
23869        key: HighlightKey,
23870        ranges: Vec<Range<Anchor>>,
23871        style: HighlightStyle,
23872        cx: &mut Context<Self>,
23873    ) {
23874        self.display_map.update(cx, |map, cx| {
23875            map.highlight_text(key, ranges, style, false, cx)
23876        });
23877        cx.notify();
23878    }
23879
23880    pub fn text_highlights<'a>(
23881        &'a self,
23882        key: HighlightKey,
23883        cx: &'a App,
23884    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23885        self.display_map.read(cx).text_highlights(key)
23886    }
23887
23888    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23889        let cleared = self
23890            .display_map
23891            .update(cx, |map, _| map.clear_highlights(key));
23892        if cleared {
23893            cx.notify();
23894        }
23895    }
23896
23897    pub fn clear_highlights_with(
23898        &mut self,
23899        f: &mut dyn FnMut(&HighlightKey) -> bool,
23900        cx: &mut Context<Self>,
23901    ) {
23902        let cleared = self
23903            .display_map
23904            .update(cx, |map, _| map.clear_highlights_with(f));
23905        if cleared {
23906            cx.notify();
23907        }
23908    }
23909
23910    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23911        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23912            && self.focus_handle.is_focused(window)
23913    }
23914
23915    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23916        self.show_cursor_when_unfocused = is_enabled;
23917        cx.notify();
23918    }
23919
23920    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23921        cx.notify();
23922    }
23923
23924    fn on_debug_session_event(
23925        &mut self,
23926        _session: Entity<Session>,
23927        event: &SessionEvent,
23928        cx: &mut Context<Self>,
23929    ) {
23930        if let SessionEvent::InvalidateInlineValue = event {
23931            self.refresh_inline_values(cx);
23932        }
23933    }
23934
23935    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23936        let Some(project) = self.project.clone() else {
23937            return;
23938        };
23939
23940        if !self.inline_value_cache.enabled {
23941            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23942            self.splice_inlays(&inlays, Vec::new(), cx);
23943            return;
23944        }
23945
23946        let current_execution_position = self
23947            .highlighted_rows
23948            .get(&TypeId::of::<ActiveDebugLine>())
23949            .and_then(|lines| lines.last().map(|line| line.range.end));
23950
23951        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23952            let inline_values = editor
23953                .update(cx, |editor, cx| {
23954                    let Some(current_execution_position) = current_execution_position else {
23955                        return Some(Task::ready(Ok(Vec::new())));
23956                    };
23957
23958                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23959                        let snapshot = buffer.snapshot(cx);
23960
23961                        let excerpt = snapshot.excerpt_containing(
23962                            current_execution_position..current_execution_position,
23963                        )?;
23964
23965                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23966                    })?;
23967
23968                    let range =
23969                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23970
23971                    project.inline_values(buffer, range, cx)
23972                })
23973                .ok()
23974                .flatten()?
23975                .await
23976                .context("refreshing debugger inlays")
23977                .log_err()?;
23978
23979            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23980
23981            for (buffer_id, inline_value) in inline_values
23982                .into_iter()
23983                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23984            {
23985                buffer_inline_values
23986                    .entry(buffer_id)
23987                    .or_default()
23988                    .push(inline_value);
23989            }
23990
23991            editor
23992                .update(cx, |editor, cx| {
23993                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23994                    let mut new_inlays = Vec::default();
23995
23996                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23997                        let buffer_id = buffer_snapshot.remote_id();
23998                        buffer_inline_values
23999                            .get(&buffer_id)
24000                            .into_iter()
24001                            .flatten()
24002                            .for_each(|hint| {
24003                                let inlay = Inlay::debugger(
24004                                    post_inc(&mut editor.next_inlay_id),
24005                                    Anchor::in_buffer(excerpt_id, hint.position),
24006                                    hint.text(),
24007                                );
24008                                if !inlay.text().chars().contains(&'\n') {
24009                                    new_inlays.push(inlay);
24010                                }
24011                            });
24012                    }
24013
24014                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24015                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24016
24017                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24018                })
24019                .ok()?;
24020            Some(())
24021        });
24022    }
24023
24024    fn on_buffer_event(
24025        &mut self,
24026        multibuffer: &Entity<MultiBuffer>,
24027        event: &multi_buffer::Event,
24028        window: &mut Window,
24029        cx: &mut Context<Self>,
24030    ) {
24031        match event {
24032            multi_buffer::Event::Edited { edited_buffer } => {
24033                self.scrollbar_marker_state.dirty = true;
24034                self.active_indent_guides_state.dirty = true;
24035                self.refresh_active_diagnostics(cx);
24036                self.refresh_code_actions(window, cx);
24037                self.refresh_single_line_folds(window, cx);
24038                let snapshot = self.snapshot(window, cx);
24039                self.refresh_matching_bracket_highlights(&snapshot, cx);
24040                self.refresh_outline_symbols_at_cursor(cx);
24041                self.refresh_sticky_headers(&snapshot, cx);
24042                if self.has_active_edit_prediction() {
24043                    self.update_visible_edit_prediction(window, cx);
24044                }
24045
24046                // Clean up orphaned review comments after edits
24047                self.cleanup_orphaned_review_comments(cx);
24048
24049                if let Some(buffer) = edited_buffer {
24050                    if buffer.read(cx).file().is_none() {
24051                        cx.emit(EditorEvent::TitleChanged);
24052                    }
24053
24054                    if self.project.is_some() {
24055                        let buffer_id = buffer.read(cx).remote_id();
24056                        self.register_buffer(buffer_id, cx);
24057                        self.update_lsp_data(Some(buffer_id), window, cx);
24058                        self.refresh_inlay_hints(
24059                            InlayHintRefreshReason::BufferEdited(buffer_id),
24060                            cx,
24061                        );
24062                    }
24063                }
24064
24065                cx.emit(EditorEvent::BufferEdited);
24066                cx.emit(SearchEvent::MatchesInvalidated);
24067
24068                let Some(project) = &self.project else { return };
24069                let (telemetry, is_via_ssh) = {
24070                    let project = project.read(cx);
24071                    let telemetry = project.client().telemetry().clone();
24072                    let is_via_ssh = project.is_via_remote_server();
24073                    (telemetry, is_via_ssh)
24074                };
24075                telemetry.log_edit_event("editor", is_via_ssh);
24076            }
24077            multi_buffer::Event::ExcerptsAdded {
24078                buffer,
24079                predecessor,
24080                excerpts,
24081            } => {
24082                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24083                let buffer_id = buffer.read(cx).remote_id();
24084                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24085                    && let Some(project) = &self.project
24086                {
24087                    update_uncommitted_diff_for_buffer(
24088                        cx.entity(),
24089                        project,
24090                        [buffer.clone()],
24091                        self.buffer.clone(),
24092                        cx,
24093                    )
24094                    .detach();
24095                }
24096                self.semantic_token_state
24097                    .invalidate_buffer(&buffer.read(cx).remote_id());
24098                self.update_lsp_data(Some(buffer_id), window, cx);
24099                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24100                self.colorize_brackets(false, cx);
24101                self.refresh_selected_text_highlights(true, window, cx);
24102                cx.emit(EditorEvent::ExcerptsAdded {
24103                    buffer: buffer.clone(),
24104                    predecessor: *predecessor,
24105                    excerpts: excerpts.clone(),
24106                });
24107            }
24108            multi_buffer::Event::ExcerptsRemoved {
24109                ids,
24110                removed_buffer_ids,
24111            } => {
24112                if let Some(inlay_hints) = &mut self.inlay_hints {
24113                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24114                }
24115                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24116                for buffer_id in removed_buffer_ids {
24117                    self.registered_buffers.remove(buffer_id);
24118                    self.tasks
24119                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24120                    self.semantic_token_state.invalidate_buffer(buffer_id);
24121                    self.display_map.update(cx, |display_map, cx| {
24122                        display_map.invalidate_semantic_highlights(*buffer_id);
24123                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24124                    });
24125                }
24126                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24127                cx.emit(EditorEvent::ExcerptsRemoved {
24128                    ids: ids.clone(),
24129                    removed_buffer_ids: removed_buffer_ids.clone(),
24130                });
24131            }
24132            multi_buffer::Event::ExcerptsEdited {
24133                excerpt_ids,
24134                buffer_ids,
24135            } => {
24136                self.display_map.update(cx, |map, cx| {
24137                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24138                });
24139                cx.emit(EditorEvent::ExcerptsEdited {
24140                    ids: excerpt_ids.clone(),
24141                });
24142            }
24143            multi_buffer::Event::ExcerptsExpanded { ids } => {
24144                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24145                self.refresh_document_highlights(cx);
24146                let snapshot = multibuffer.read(cx).snapshot(cx);
24147                for id in ids {
24148                    self.fetched_tree_sitter_chunks.remove(id);
24149                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24150                        self.semantic_token_state
24151                            .invalidate_buffer(&buffer.remote_id());
24152                    }
24153                }
24154                self.colorize_brackets(false, cx);
24155                self.update_lsp_data(None, window, cx);
24156                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24157            }
24158            multi_buffer::Event::Reparsed(buffer_id) => {
24159                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24160                self.refresh_selected_text_highlights(true, window, cx);
24161                self.colorize_brackets(true, cx);
24162                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24163
24164                cx.emit(EditorEvent::Reparsed(*buffer_id));
24165            }
24166            multi_buffer::Event::DiffHunksToggled => {
24167                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24168            }
24169            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24170                if !is_fresh_language {
24171                    self.registered_buffers.remove(&buffer_id);
24172                }
24173                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24174                cx.emit(EditorEvent::Reparsed(*buffer_id));
24175                cx.notify();
24176            }
24177            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24178            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24179            multi_buffer::Event::FileHandleChanged
24180            | multi_buffer::Event::Reloaded
24181            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24182            multi_buffer::Event::DiagnosticsUpdated => {
24183                self.update_diagnostics_state(window, cx);
24184            }
24185            _ => {}
24186        };
24187    }
24188
24189    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24190        if !self.diagnostics_enabled() {
24191            return;
24192        }
24193        self.refresh_active_diagnostics(cx);
24194        self.refresh_inline_diagnostics(true, window, cx);
24195        self.scrollbar_marker_state.dirty = true;
24196        cx.notify();
24197    }
24198
24199    pub fn start_temporary_diff_override(&mut self) {
24200        self.load_diff_task.take();
24201        self.temporary_diff_override = true;
24202    }
24203
24204    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24205        self.temporary_diff_override = false;
24206        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24207        self.buffer.update(cx, |buffer, cx| {
24208            buffer.set_all_diff_hunks_collapsed(cx);
24209        });
24210
24211        if let Some(project) = self.project.clone() {
24212            self.load_diff_task = Some(
24213                update_uncommitted_diff_for_buffer(
24214                    cx.entity(),
24215                    &project,
24216                    self.buffer.read(cx).all_buffers(),
24217                    self.buffer.clone(),
24218                    cx,
24219                )
24220                .shared(),
24221            );
24222        }
24223    }
24224
24225    fn on_display_map_changed(
24226        &mut self,
24227        _: Entity<DisplayMap>,
24228        _: &mut Window,
24229        cx: &mut Context<Self>,
24230    ) {
24231        cx.notify();
24232    }
24233
24234    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24235        if !self.mode.is_full() {
24236            return None;
24237        }
24238
24239        let theme_settings = theme::ThemeSettings::get_global(cx);
24240        let theme = cx.theme();
24241        let accent_colors = theme.accents().clone();
24242
24243        let accent_overrides = theme_settings
24244            .theme_overrides
24245            .get(theme.name.as_ref())
24246            .map(|theme_style| &theme_style.accents)
24247            .into_iter()
24248            .flatten()
24249            .chain(
24250                theme_settings
24251                    .experimental_theme_overrides
24252                    .as_ref()
24253                    .map(|overrides| &overrides.accents)
24254                    .into_iter()
24255                    .flatten(),
24256            )
24257            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24258            .collect();
24259
24260        Some(AccentData {
24261            colors: accent_colors,
24262            overrides: accent_overrides,
24263        })
24264    }
24265
24266    fn fetch_applicable_language_settings(
24267        &self,
24268        cx: &App,
24269    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24270        if !self.mode.is_full() {
24271            return HashMap::default();
24272        }
24273
24274        self.buffer().read(cx).all_buffers().into_iter().fold(
24275            HashMap::default(),
24276            |mut acc, buffer| {
24277                let buffer = buffer.read(cx);
24278                let language = buffer.language().map(|language| language.name());
24279                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24280                    let file = buffer.file();
24281                    v.insert(language_settings(language, file, cx).into_owned());
24282                }
24283                acc
24284            },
24285        )
24286    }
24287
24288    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24289        let new_language_settings = self.fetch_applicable_language_settings(cx);
24290        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24291        self.applicable_language_settings = new_language_settings;
24292
24293        let new_accents = self.fetch_accent_data(cx);
24294        let accents_changed = new_accents != self.accent_data;
24295        self.accent_data = new_accents;
24296
24297        if self.diagnostics_enabled() {
24298            let new_severity = EditorSettings::get_global(cx)
24299                .diagnostics_max_severity
24300                .unwrap_or(DiagnosticSeverity::Hint);
24301            self.set_max_diagnostics_severity(new_severity, cx);
24302        }
24303        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24304        self.update_edit_prediction_settings(cx);
24305        self.refresh_edit_prediction(true, false, window, cx);
24306        self.refresh_inline_values(cx);
24307
24308        let old_cursor_shape = self.cursor_shape;
24309        let old_show_breadcrumbs = self.show_breadcrumbs;
24310
24311        {
24312            let editor_settings = EditorSettings::get_global(cx);
24313            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24314            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24315            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24316            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24317        }
24318
24319        if old_cursor_shape != self.cursor_shape {
24320            cx.emit(EditorEvent::CursorShapeChanged);
24321        }
24322
24323        if old_show_breadcrumbs != self.show_breadcrumbs {
24324            cx.emit(EditorEvent::BreadcrumbsChanged);
24325        }
24326
24327        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24328            let project_settings = ProjectSettings::get_global(cx);
24329            (
24330                project_settings.session.restore_unsaved_buffers,
24331                project_settings.diagnostics.inline.enabled,
24332                project_settings.git.inline_blame.enabled,
24333            )
24334        };
24335        self.buffer_serialization = self
24336            .should_serialize_buffer()
24337            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24338
24339        if self.mode.is_full() {
24340            if self.show_inline_diagnostics != show_inline_diagnostics {
24341                self.show_inline_diagnostics = show_inline_diagnostics;
24342                self.refresh_inline_diagnostics(false, window, cx);
24343            }
24344
24345            if self.git_blame_inline_enabled != inline_blame_enabled {
24346                self.toggle_git_blame_inline_internal(false, window, cx);
24347            }
24348
24349            let minimap_settings = EditorSettings::get_global(cx).minimap;
24350            if self.minimap_visibility != MinimapVisibility::Disabled {
24351                if self.minimap_visibility.settings_visibility()
24352                    != minimap_settings.minimap_enabled()
24353                {
24354                    self.set_minimap_visibility(
24355                        MinimapVisibility::for_mode(self.mode(), cx),
24356                        window,
24357                        cx,
24358                    );
24359                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24360                    minimap_entity.update(cx, |minimap_editor, cx| {
24361                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24362                    })
24363                }
24364            }
24365
24366            if language_settings_changed || accents_changed {
24367                self.colorize_brackets(true, cx);
24368            }
24369
24370            if language_settings_changed {
24371                self.clear_disabled_lsp_folding_ranges(window, cx);
24372                self.refresh_document_symbols(None, cx);
24373            }
24374
24375            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24376                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24377            }) {
24378                if !inlay_splice.is_empty() {
24379                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24380                }
24381                self.refresh_document_colors(None, window, cx);
24382            }
24383
24384            self.refresh_inlay_hints(
24385                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24386                    self.selections.newest_anchor().head(),
24387                    &self.buffer.read(cx).snapshot(cx),
24388                    cx,
24389                )),
24390                cx,
24391            );
24392
24393            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24394                .global_lsp_settings
24395                .semantic_token_rules
24396                .clone();
24397            let semantic_token_rules_changed = self
24398                .semantic_token_state
24399                .update_rules(new_semantic_token_rules);
24400            if language_settings_changed || semantic_token_rules_changed {
24401                self.invalidate_semantic_tokens(None);
24402                self.refresh_semantic_tokens(None, None, cx);
24403            }
24404        }
24405
24406        cx.notify();
24407    }
24408
24409    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24410        if !self.mode.is_full() {
24411            return;
24412        }
24413
24414        let new_accents = self.fetch_accent_data(cx);
24415        if new_accents != self.accent_data {
24416            self.accent_data = new_accents;
24417            self.colorize_brackets(true, cx);
24418        }
24419
24420        self.invalidate_semantic_tokens(None);
24421        self.refresh_semantic_tokens(None, None, cx);
24422    }
24423
24424    pub fn set_searchable(&mut self, searchable: bool) {
24425        self.searchable = searchable;
24426    }
24427
24428    pub fn searchable(&self) -> bool {
24429        self.searchable
24430    }
24431
24432    pub fn open_excerpts_in_split(
24433        &mut self,
24434        _: &OpenExcerptsSplit,
24435        window: &mut Window,
24436        cx: &mut Context<Self>,
24437    ) {
24438        self.open_excerpts_common(None, true, window, cx)
24439    }
24440
24441    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24442        self.open_excerpts_common(None, false, window, cx)
24443    }
24444
24445    pub(crate) fn open_excerpts_common(
24446        &mut self,
24447        jump_data: Option<JumpData>,
24448        split: bool,
24449        window: &mut Window,
24450        cx: &mut Context<Self>,
24451    ) {
24452        if self.buffer.read(cx).is_singleton() {
24453            cx.propagate();
24454            return;
24455        }
24456
24457        let mut new_selections_by_buffer = HashMap::default();
24458        match &jump_data {
24459            Some(JumpData::MultiBufferPoint {
24460                excerpt_id,
24461                position,
24462                anchor,
24463                line_offset_from_top,
24464            }) => {
24465                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24466                if let Some(buffer) = multi_buffer_snapshot
24467                    .buffer_id_for_excerpt(*excerpt_id)
24468                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24469                {
24470                    let buffer_snapshot = buffer.read(cx).snapshot();
24471                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24472                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24473                    } else {
24474                        buffer_snapshot.clip_point(*position, Bias::Left)
24475                    };
24476                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24477                    new_selections_by_buffer.insert(
24478                        buffer,
24479                        (
24480                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24481                            Some(*line_offset_from_top),
24482                        ),
24483                    );
24484                }
24485            }
24486            Some(JumpData::MultiBufferRow {
24487                row,
24488                line_offset_from_top,
24489            }) => {
24490                let point = MultiBufferPoint::new(row.0, 0);
24491                if let Some((buffer, buffer_point, _)) =
24492                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24493                {
24494                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24495                    new_selections_by_buffer
24496                        .entry(buffer)
24497                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24498                        .0
24499                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24500                }
24501            }
24502            None => {
24503                let selections = self
24504                    .selections
24505                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24506                let multi_buffer = self.buffer.read(cx);
24507                for selection in selections {
24508                    for (snapshot, range, _, anchor) in multi_buffer
24509                        .snapshot(cx)
24510                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24511                    {
24512                        if let Some(anchor) = anchor {
24513                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24514                            else {
24515                                continue;
24516                            };
24517                            let offset = text::ToOffset::to_offset(
24518                                &anchor.text_anchor,
24519                                &buffer_handle.read(cx).snapshot(),
24520                            );
24521                            let range = BufferOffset(offset)..BufferOffset(offset);
24522                            new_selections_by_buffer
24523                                .entry(buffer_handle)
24524                                .or_insert((Vec::new(), None))
24525                                .0
24526                                .push(range)
24527                        } else {
24528                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24529                            else {
24530                                continue;
24531                            };
24532                            new_selections_by_buffer
24533                                .entry(buffer_handle)
24534                                .or_insert((Vec::new(), None))
24535                                .0
24536                                .push(range)
24537                        }
24538                    }
24539                }
24540            }
24541        }
24542
24543        if self.delegate_open_excerpts {
24544            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24545                .into_iter()
24546                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24547                .collect();
24548            if !selections_by_buffer.is_empty() {
24549                cx.emit(EditorEvent::OpenExcerptsRequested {
24550                    selections_by_buffer,
24551                    split,
24552                });
24553            }
24554            return;
24555        }
24556
24557        let Some(workspace) = self.workspace() else {
24558            cx.propagate();
24559            return;
24560        };
24561
24562        new_selections_by_buffer
24563            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24564
24565        if new_selections_by_buffer.is_empty() {
24566            return;
24567        }
24568
24569        Self::open_buffers_in_workspace(
24570            workspace.downgrade(),
24571            new_selections_by_buffer,
24572            split,
24573            window,
24574            cx,
24575        );
24576    }
24577
24578    pub(crate) fn open_buffers_in_workspace(
24579        workspace: WeakEntity<Workspace>,
24580        new_selections_by_buffer: HashMap<
24581            Entity<language::Buffer>,
24582            (Vec<Range<BufferOffset>>, Option<u32>),
24583        >,
24584        split: bool,
24585        window: &mut Window,
24586        cx: &mut App,
24587    ) {
24588        // We defer the pane interaction because we ourselves are a workspace item
24589        // and activating a new item causes the pane to call a method on us reentrantly,
24590        // which panics if we're on the stack.
24591        window.defer(cx, move |window, cx| {
24592            workspace
24593                .update(cx, |workspace, cx| {
24594                    let pane = if split {
24595                        workspace.adjacent_pane(window, cx)
24596                    } else {
24597                        workspace.active_pane().clone()
24598                    };
24599
24600                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24601                        let buffer_read = buffer.read(cx);
24602                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24603                            (true, project::File::from_dyn(Some(file)).is_some())
24604                        } else {
24605                            (false, false)
24606                        };
24607
24608                        // If project file is none workspace.open_project_item will fail to open the excerpt
24609                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24610                        // so we check if there's a tab match in that case first
24611                        let editor = (!has_file || !is_project_file)
24612                            .then(|| {
24613                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24614                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24615                                // Instead, we try to activate the existing editor in the pane first.
24616                                let (editor, pane_item_index, pane_item_id) =
24617                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24618                                        let editor = item.downcast::<Editor>()?;
24619                                        let singleton_buffer =
24620                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24621                                        if singleton_buffer == buffer {
24622                                            Some((editor, i, item.item_id()))
24623                                        } else {
24624                                            None
24625                                        }
24626                                    })?;
24627                                pane.update(cx, |pane, cx| {
24628                                    pane.activate_item(pane_item_index, true, true, window, cx);
24629                                    if !PreviewTabsSettings::get_global(cx)
24630                                        .enable_preview_from_multibuffer
24631                                    {
24632                                        pane.unpreview_item_if_preview(pane_item_id);
24633                                    }
24634                                });
24635                                Some(editor)
24636                            })
24637                            .flatten()
24638                            .unwrap_or_else(|| {
24639                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24640                                    .enable_keep_preview_on_code_navigation;
24641                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24642                                    .enable_preview_from_multibuffer;
24643                                workspace.open_project_item::<Self>(
24644                                    pane.clone(),
24645                                    buffer,
24646                                    true,
24647                                    true,
24648                                    keep_old_preview,
24649                                    allow_new_preview,
24650                                    window,
24651                                    cx,
24652                                )
24653                            });
24654
24655                        editor.update(cx, |editor, cx| {
24656                            if has_file && !is_project_file {
24657                                editor.set_read_only(true);
24658                            }
24659                            let autoscroll = match scroll_offset {
24660                                Some(scroll_offset) => {
24661                                    Autoscroll::top_relative(scroll_offset as usize)
24662                                }
24663                                None => Autoscroll::newest(),
24664                            };
24665                            let nav_history = editor.nav_history.take();
24666                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24667                            let Some((excerpt_id, _, buffer_snapshot)) =
24668                                multibuffer_snapshot.as_singleton()
24669                            else {
24670                                return;
24671                            };
24672                            editor.change_selections(
24673                                SelectionEffects::scroll(autoscroll),
24674                                window,
24675                                cx,
24676                                |s| {
24677                                    s.select_ranges(ranges.into_iter().map(|range| {
24678                                        let range = buffer_snapshot.anchor_before(range.start)
24679                                            ..buffer_snapshot.anchor_after(range.end);
24680                                        multibuffer_snapshot
24681                                            .anchor_range_in_excerpt(excerpt_id, range)
24682                                            .unwrap()
24683                                    }));
24684                                },
24685                            );
24686                            editor.nav_history = nav_history;
24687                        });
24688                    }
24689                })
24690                .ok();
24691        });
24692    }
24693
24694    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24695        let snapshot = self.buffer.read(cx).read(cx);
24696        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24697        Some(
24698            ranges
24699                .iter()
24700                .map(move |range| {
24701                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24702                })
24703                .collect(),
24704        )
24705    }
24706
24707    fn selection_replacement_ranges(
24708        &self,
24709        range: Range<MultiBufferOffsetUtf16>,
24710        cx: &mut App,
24711    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24712        let selections = self
24713            .selections
24714            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24715        let newest_selection = selections
24716            .iter()
24717            .max_by_key(|selection| selection.id)
24718            .unwrap();
24719        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24720        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24721        let snapshot = self.buffer.read(cx).read(cx);
24722        selections
24723            .into_iter()
24724            .map(|mut selection| {
24725                selection.start.0.0 =
24726                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24727                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24728                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24729                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24730            })
24731            .collect()
24732    }
24733
24734    fn report_editor_event(
24735        &self,
24736        reported_event: ReportEditorEvent,
24737        file_extension: Option<String>,
24738        cx: &App,
24739    ) {
24740        if cfg!(any(test, feature = "test-support")) {
24741            return;
24742        }
24743
24744        let Some(project) = &self.project else { return };
24745
24746        // If None, we are in a file without an extension
24747        let file = self
24748            .buffer
24749            .read(cx)
24750            .as_singleton()
24751            .and_then(|b| b.read(cx).file());
24752        let file_extension = file_extension.or(file
24753            .as_ref()
24754            .and_then(|file| Path::new(file.file_name(cx)).extension())
24755            .and_then(|e| e.to_str())
24756            .map(|a| a.to_string()));
24757
24758        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24759            .map(|vim_mode| vim_mode.0)
24760            .unwrap_or(false);
24761
24762        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24763        let copilot_enabled = edit_predictions_provider
24764            == language::language_settings::EditPredictionProvider::Copilot;
24765        let copilot_enabled_for_language = self
24766            .buffer
24767            .read(cx)
24768            .language_settings(cx)
24769            .show_edit_predictions;
24770
24771        let project = project.read(cx);
24772        let event_type = reported_event.event_type();
24773
24774        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24775            telemetry::event!(
24776                event_type,
24777                type = if auto_saved {"autosave"} else {"manual"},
24778                file_extension,
24779                vim_mode,
24780                copilot_enabled,
24781                copilot_enabled_for_language,
24782                edit_predictions_provider,
24783                is_via_ssh = project.is_via_remote_server(),
24784            );
24785        } else {
24786            telemetry::event!(
24787                event_type,
24788                file_extension,
24789                vim_mode,
24790                copilot_enabled,
24791                copilot_enabled_for_language,
24792                edit_predictions_provider,
24793                is_via_ssh = project.is_via_remote_server(),
24794            );
24795        };
24796    }
24797
24798    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24799    /// with each line being an array of {text, highlight} objects.
24800    fn copy_highlight_json(
24801        &mut self,
24802        _: &CopyHighlightJson,
24803        window: &mut Window,
24804        cx: &mut Context<Self>,
24805    ) {
24806        #[derive(Serialize)]
24807        struct Chunk<'a> {
24808            text: String,
24809            highlight: Option<&'a str>,
24810        }
24811
24812        let snapshot = self.buffer.read(cx).snapshot(cx);
24813        let range = self
24814            .selected_text_range(false, window, cx)
24815            .and_then(|selection| {
24816                if selection.range.is_empty() {
24817                    None
24818                } else {
24819                    Some(
24820                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24821                            selection.range.start,
24822                        )))
24823                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24824                                selection.range.end,
24825                            ))),
24826                    )
24827                }
24828            })
24829            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24830
24831        let chunks = snapshot.chunks(range, true);
24832        let mut lines = Vec::new();
24833        let mut line: VecDeque<Chunk> = VecDeque::new();
24834
24835        let Some(style) = self.style.as_ref() else {
24836            return;
24837        };
24838
24839        for chunk in chunks {
24840            let highlight = chunk
24841                .syntax_highlight_id
24842                .and_then(|id| id.name(&style.syntax));
24843            let mut chunk_lines = chunk.text.split('\n').peekable();
24844            while let Some(text) = chunk_lines.next() {
24845                let mut merged_with_last_token = false;
24846                if let Some(last_token) = line.back_mut()
24847                    && last_token.highlight == highlight
24848                {
24849                    last_token.text.push_str(text);
24850                    merged_with_last_token = true;
24851                }
24852
24853                if !merged_with_last_token {
24854                    line.push_back(Chunk {
24855                        text: text.into(),
24856                        highlight,
24857                    });
24858                }
24859
24860                if chunk_lines.peek().is_some() {
24861                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24862                        line.pop_front();
24863                    }
24864                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24865                        line.pop_back();
24866                    }
24867
24868                    lines.push(mem::take(&mut line));
24869                }
24870            }
24871        }
24872
24873        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24874            return;
24875        };
24876        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24877    }
24878
24879    pub fn open_context_menu(
24880        &mut self,
24881        _: &OpenContextMenu,
24882        window: &mut Window,
24883        cx: &mut Context<Self>,
24884    ) {
24885        self.request_autoscroll(Autoscroll::newest(), cx);
24886        let position = self
24887            .selections
24888            .newest_display(&self.display_snapshot(cx))
24889            .start;
24890        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24891    }
24892
24893    pub fn replay_insert_event(
24894        &mut self,
24895        text: &str,
24896        relative_utf16_range: Option<Range<isize>>,
24897        window: &mut Window,
24898        cx: &mut Context<Self>,
24899    ) {
24900        if !self.input_enabled {
24901            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24902            return;
24903        }
24904        if let Some(relative_utf16_range) = relative_utf16_range {
24905            let selections = self
24906                .selections
24907                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24908            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24909                let new_ranges = selections.into_iter().map(|range| {
24910                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24911                        range
24912                            .head()
24913                            .0
24914                            .0
24915                            .saturating_add_signed(relative_utf16_range.start),
24916                    ));
24917                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24918                        range
24919                            .head()
24920                            .0
24921                            .0
24922                            .saturating_add_signed(relative_utf16_range.end),
24923                    ));
24924                    start..end
24925                });
24926                s.select_ranges(new_ranges);
24927            });
24928        }
24929
24930        self.handle_input(text, window, cx);
24931    }
24932
24933    pub fn is_focused(&self, window: &Window) -> bool {
24934        self.focus_handle.is_focused(window)
24935    }
24936
24937    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24938        cx.emit(EditorEvent::Focused);
24939
24940        if let Some(descendant) = self
24941            .last_focused_descendant
24942            .take()
24943            .and_then(|descendant| descendant.upgrade())
24944        {
24945            window.focus(&descendant, cx);
24946        } else {
24947            if let Some(blame) = self.blame.as_ref() {
24948                blame.update(cx, GitBlame::focus)
24949            }
24950
24951            self.blink_manager.update(cx, BlinkManager::enable);
24952            self.show_cursor_names(window, cx);
24953            self.buffer.update(cx, |buffer, cx| {
24954                buffer.finalize_last_transaction(cx);
24955                if self.leader_id.is_none() {
24956                    buffer.set_active_selections(
24957                        &self.selections.disjoint_anchors_arc(),
24958                        self.selections.line_mode(),
24959                        self.cursor_shape,
24960                        cx,
24961                    );
24962                }
24963            });
24964
24965            if let Some(position_map) = self.last_position_map.clone() {
24966                EditorElement::mouse_moved(
24967                    self,
24968                    &MouseMoveEvent {
24969                        position: window.mouse_position(),
24970                        pressed_button: None,
24971                        modifiers: window.modifiers(),
24972                    },
24973                    &position_map,
24974                    None,
24975                    window,
24976                    cx,
24977                );
24978            }
24979        }
24980    }
24981
24982    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24983        cx.emit(EditorEvent::FocusedIn)
24984    }
24985
24986    fn handle_focus_out(
24987        &mut self,
24988        event: FocusOutEvent,
24989        _window: &mut Window,
24990        cx: &mut Context<Self>,
24991    ) {
24992        if event.blurred != self.focus_handle {
24993            self.last_focused_descendant = Some(event.blurred);
24994        }
24995        self.selection_drag_state = SelectionDragState::None;
24996        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24997    }
24998
24999    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25000        self.blink_manager.update(cx, BlinkManager::disable);
25001        self.buffer
25002            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25003
25004        if let Some(blame) = self.blame.as_ref() {
25005            blame.update(cx, GitBlame::blur)
25006        }
25007        if !self.hover_state.focused(window, cx) {
25008            hide_hover(self, cx);
25009        }
25010        if !self
25011            .context_menu
25012            .borrow()
25013            .as_ref()
25014            .is_some_and(|context_menu| context_menu.focused(window, cx))
25015        {
25016            self.hide_context_menu(window, cx);
25017        }
25018        self.take_active_edit_prediction(cx);
25019        cx.emit(EditorEvent::Blurred);
25020        cx.notify();
25021    }
25022
25023    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25024        let mut pending: String = window
25025            .pending_input_keystrokes()
25026            .into_iter()
25027            .flatten()
25028            .filter_map(|keystroke| keystroke.key_char.clone())
25029            .collect();
25030
25031        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25032            pending = "".to_string();
25033        }
25034
25035        let existing_pending = self
25036            .text_highlights(HighlightKey::PendingInput, cx)
25037            .map(|(_, ranges)| ranges.to_vec());
25038        if existing_pending.is_none() && pending.is_empty() {
25039            return;
25040        }
25041        let transaction =
25042            self.transact(window, cx, |this, window, cx| {
25043                let selections = this
25044                    .selections
25045                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25046                let edits = selections
25047                    .iter()
25048                    .map(|selection| (selection.end..selection.end, pending.clone()));
25049                this.edit(edits, cx);
25050                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25051                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25052                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25053                    }));
25054                });
25055                if let Some(existing_ranges) = existing_pending {
25056                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25057                    this.edit(edits, cx);
25058                }
25059            });
25060
25061        let snapshot = self.snapshot(window, cx);
25062        let ranges = self
25063            .selections
25064            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25065            .into_iter()
25066            .map(|selection| {
25067                snapshot.buffer_snapshot().anchor_after(selection.end)
25068                    ..snapshot
25069                        .buffer_snapshot()
25070                        .anchor_before(selection.end + pending.len())
25071            })
25072            .collect();
25073
25074        if pending.is_empty() {
25075            self.clear_highlights(HighlightKey::PendingInput, cx);
25076        } else {
25077            self.highlight_text(
25078                HighlightKey::PendingInput,
25079                ranges,
25080                HighlightStyle {
25081                    underline: Some(UnderlineStyle {
25082                        thickness: px(1.),
25083                        color: None,
25084                        wavy: false,
25085                    }),
25086                    ..Default::default()
25087                },
25088                cx,
25089            );
25090        }
25091
25092        self.ime_transaction = self.ime_transaction.or(transaction);
25093        if let Some(transaction) = self.ime_transaction {
25094            self.buffer.update(cx, |buffer, cx| {
25095                buffer.group_until_transaction(transaction, cx);
25096            });
25097        }
25098
25099        if self
25100            .text_highlights(HighlightKey::PendingInput, cx)
25101            .is_none()
25102        {
25103            self.ime_transaction.take();
25104        }
25105    }
25106
25107    pub fn register_action_renderer(
25108        &mut self,
25109        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25110    ) -> Subscription {
25111        let id = self.next_editor_action_id.post_inc();
25112        self.editor_actions
25113            .borrow_mut()
25114            .insert(id, Box::new(listener));
25115
25116        let editor_actions = self.editor_actions.clone();
25117        Subscription::new(move || {
25118            editor_actions.borrow_mut().remove(&id);
25119        })
25120    }
25121
25122    pub fn register_action<A: Action>(
25123        &mut self,
25124        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25125    ) -> Subscription {
25126        let id = self.next_editor_action_id.post_inc();
25127        let listener = Arc::new(listener);
25128        self.editor_actions.borrow_mut().insert(
25129            id,
25130            Box::new(move |_, window, _| {
25131                let listener = listener.clone();
25132                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25133                    let action = action.downcast_ref().unwrap();
25134                    if phase == DispatchPhase::Bubble {
25135                        listener(action, window, cx)
25136                    }
25137                })
25138            }),
25139        );
25140
25141        let editor_actions = self.editor_actions.clone();
25142        Subscription::new(move || {
25143            editor_actions.borrow_mut().remove(&id);
25144        })
25145    }
25146
25147    pub fn file_header_size(&self) -> u32 {
25148        FILE_HEADER_HEIGHT
25149    }
25150
25151    pub fn restore(
25152        &mut self,
25153        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25154        window: &mut Window,
25155        cx: &mut Context<Self>,
25156    ) {
25157        self.buffer().update(cx, |multi_buffer, cx| {
25158            for (buffer_id, changes) in revert_changes {
25159                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25160                    buffer.update(cx, |buffer, cx| {
25161                        buffer.edit(
25162                            changes
25163                                .into_iter()
25164                                .map(|(range, text)| (range, text.to_string())),
25165                            None,
25166                            cx,
25167                        );
25168                    });
25169                }
25170            }
25171        });
25172        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25173            selections.refresh()
25174        });
25175    }
25176
25177    pub fn to_pixel_point(
25178        &mut self,
25179        source: Anchor,
25180        editor_snapshot: &EditorSnapshot,
25181        window: &mut Window,
25182        cx: &mut App,
25183    ) -> Option<gpui::Point<Pixels>> {
25184        let source_point = source.to_display_point(editor_snapshot);
25185        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25186    }
25187
25188    pub fn display_to_pixel_point(
25189        &mut self,
25190        source: DisplayPoint,
25191        editor_snapshot: &EditorSnapshot,
25192        window: &mut Window,
25193        cx: &mut App,
25194    ) -> Option<gpui::Point<Pixels>> {
25195        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25196        let text_layout_details = self.text_layout_details(window, cx);
25197        let scroll_top = text_layout_details
25198            .scroll_anchor
25199            .scroll_position(editor_snapshot)
25200            .y;
25201
25202        if source.row().as_f64() < scroll_top.floor() {
25203            return None;
25204        }
25205        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25206        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25207        Some(gpui::Point::new(source_x, source_y))
25208    }
25209
25210    pub fn has_visible_completions_menu(&self) -> bool {
25211        !self.edit_prediction_preview_is_active()
25212            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25213                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25214            })
25215    }
25216
25217    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25218        if self.mode.is_minimap() {
25219            return;
25220        }
25221        self.addons
25222            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25223    }
25224
25225    pub fn unregister_addon<T: Addon>(&mut self) {
25226        self.addons.remove(&std::any::TypeId::of::<T>());
25227    }
25228
25229    pub fn addon<T: Addon>(&self) -> Option<&T> {
25230        let type_id = std::any::TypeId::of::<T>();
25231        self.addons
25232            .get(&type_id)
25233            .and_then(|item| item.to_any().downcast_ref::<T>())
25234    }
25235
25236    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25237        let type_id = std::any::TypeId::of::<T>();
25238        self.addons
25239            .get_mut(&type_id)
25240            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25241    }
25242
25243    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25244        let text_layout_details = self.text_layout_details(window, cx);
25245        let style = &text_layout_details.editor_style;
25246        let font_id = window.text_system().resolve_font(&style.text.font());
25247        let font_size = style.text.font_size.to_pixels(window.rem_size());
25248        let line_height = style.text.line_height_in_pixels(window.rem_size());
25249        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25250        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25251
25252        CharacterDimensions {
25253            em_width,
25254            em_advance,
25255            line_height,
25256        }
25257    }
25258
25259    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25260        self.load_diff_task.clone()
25261    }
25262
25263    fn read_metadata_from_db(
25264        &mut self,
25265        item_id: u64,
25266        workspace_id: WorkspaceId,
25267        window: &mut Window,
25268        cx: &mut Context<Editor>,
25269    ) {
25270        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25271            && !self.mode.is_minimap()
25272            && WorkspaceSettings::get(None, cx).restore_on_startup
25273                != RestoreOnStartupBehavior::EmptyTab
25274        {
25275            let buffer_snapshot = OnceCell::new();
25276
25277            // Get file path for path-based fold lookup
25278            let file_path: Option<Arc<Path>> =
25279                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25280                    project::File::from_dyn(buffer.read(cx).file())
25281                        .map(|file| Arc::from(file.abs_path(cx)))
25282                });
25283
25284            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25285            let (folds, needs_migration) = if let Some(ref path) = file_path {
25286                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
25287                    && !folds.is_empty()
25288                {
25289                    (Some(folds), false)
25290                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25291                    && !folds.is_empty()
25292                {
25293                    // Found old editor_folds data, will migrate to file_folds
25294                    (Some(folds), true)
25295                } else {
25296                    (None, false)
25297                }
25298            } else {
25299                // No file path, try editor_folds as fallback
25300                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
25301                (folds.filter(|f| !f.is_empty()), false)
25302            };
25303
25304            if let Some(folds) = folds {
25305                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25306                let snapshot_len = snapshot.len().0;
25307
25308                // Helper: search for fingerprint in buffer, return offset if found
25309                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25310                    // Ensure we start at a character boundary (defensive)
25311                    let search_start = snapshot
25312                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25313                        .0;
25314                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25315
25316                    let mut byte_offset = search_start;
25317                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25318                        if byte_offset > search_end {
25319                            break;
25320                        }
25321                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25322                            return Some(byte_offset);
25323                        }
25324                        byte_offset += ch.len_utf8();
25325                    }
25326                    None
25327                };
25328
25329                // Track search position to handle duplicate fingerprints correctly.
25330                // Folds are stored in document order, so we advance after each match.
25331                let mut search_start = 0usize;
25332
25333                // Collect db_folds for migration (only folds with valid fingerprints)
25334                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25335
25336                let valid_folds: Vec<_> = folds
25337                    .into_iter()
25338                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25339                        // Skip folds without fingerprints (old data before migration)
25340                        let sfp = start_fp?;
25341                        let efp = end_fp?;
25342                        let efp_len = efp.len();
25343
25344                        // Fast path: check if fingerprints match at stored offsets
25345                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25346                        let start_matches = stored_start < snapshot_len
25347                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25348                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25349                        let end_matches = efp_check_pos >= stored_start
25350                            && stored_end <= snapshot_len
25351                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25352
25353                        let (new_start, new_end) = if start_matches && end_matches {
25354                            // Offsets unchanged, use stored values
25355                            (stored_start, stored_end)
25356                        } else if sfp == efp {
25357                            // Short fold: identical fingerprints can only match once per search
25358                            // Use stored fold length to compute new_end
25359                            let new_start = find_fingerprint(&sfp, search_start)?;
25360                            let fold_len = stored_end - stored_start;
25361                            let new_end = new_start + fold_len;
25362                            (new_start, new_end)
25363                        } else {
25364                            // Slow path: search for fingerprints in buffer
25365                            let new_start = find_fingerprint(&sfp, search_start)?;
25366                            // Search for end_fp after start, then add efp_len to get actual fold end
25367                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25368                            let new_end = efp_pos + efp_len;
25369                            (new_start, new_end)
25370                        };
25371
25372                        // Advance search position for next fold
25373                        search_start = new_end;
25374
25375                        // Validate fold makes sense (end must be after start)
25376                        if new_end <= new_start {
25377                            return None;
25378                        }
25379
25380                        // Collect for migration if needed
25381                        if needs_migration {
25382                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25383                        }
25384
25385                        Some(
25386                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25387                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25388                        )
25389                    })
25390                    .collect();
25391
25392                if !valid_folds.is_empty() {
25393                    self.fold_ranges(valid_folds, false, window, cx);
25394
25395                    // Migrate from editor_folds to file_folds if we loaded from old table
25396                    if needs_migration {
25397                        if let Some(ref path) = file_path {
25398                            let path = path.clone();
25399                            cx.spawn(async move |_, _| {
25400                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25401                                    .await
25402                                    .log_err();
25403                            })
25404                            .detach();
25405                        }
25406                    }
25407                }
25408            }
25409
25410            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25411                && !selections.is_empty()
25412            {
25413                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25414                // skip adding the initial selection to selection history
25415                self.selection_history.mode = SelectionHistoryMode::Skipping;
25416                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25417                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25418                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25419                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25420                    }));
25421                });
25422                self.selection_history.mode = SelectionHistoryMode::Normal;
25423            };
25424        }
25425
25426        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25427    }
25428
25429    /// Load folds from the file_folds database table by file path.
25430    /// Used when manually opening a file that was previously closed.
25431    fn load_folds_from_db(
25432        &mut self,
25433        workspace_id: WorkspaceId,
25434        file_path: PathBuf,
25435        window: &mut Window,
25436        cx: &mut Context<Editor>,
25437    ) {
25438        if self.mode.is_minimap()
25439            || WorkspaceSettings::get(None, cx).restore_on_startup
25440                == RestoreOnStartupBehavior::EmptyTab
25441        {
25442            return;
25443        }
25444
25445        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25446            return;
25447        };
25448        if folds.is_empty() {
25449            return;
25450        }
25451
25452        let snapshot = self.buffer.read(cx).snapshot(cx);
25453        let snapshot_len = snapshot.len().0;
25454
25455        // Helper: search for fingerprint in buffer, return offset if found
25456        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25457            let search_start = snapshot
25458                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25459                .0;
25460            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25461
25462            let mut byte_offset = search_start;
25463            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25464                if byte_offset > search_end {
25465                    break;
25466                }
25467                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25468                    return Some(byte_offset);
25469                }
25470                byte_offset += ch.len_utf8();
25471            }
25472            None
25473        };
25474
25475        let mut search_start = 0usize;
25476
25477        let valid_folds: Vec<_> = folds
25478            .into_iter()
25479            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25480                let sfp = start_fp?;
25481                let efp = end_fp?;
25482                let efp_len = efp.len();
25483
25484                let start_matches = stored_start < snapshot_len
25485                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25486                let efp_check_pos = stored_end.saturating_sub(efp_len);
25487                let end_matches = efp_check_pos >= stored_start
25488                    && stored_end <= snapshot_len
25489                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25490
25491                let (new_start, new_end) = if start_matches && end_matches {
25492                    (stored_start, stored_end)
25493                } else if sfp == efp {
25494                    let new_start = find_fingerprint(&sfp, search_start)?;
25495                    let fold_len = stored_end - stored_start;
25496                    let new_end = new_start + fold_len;
25497                    (new_start, new_end)
25498                } else {
25499                    let new_start = find_fingerprint(&sfp, search_start)?;
25500                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25501                    let new_end = efp_pos + efp_len;
25502                    (new_start, new_end)
25503                };
25504
25505                search_start = new_end;
25506
25507                if new_end <= new_start {
25508                    return None;
25509                }
25510
25511                Some(
25512                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25513                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25514                )
25515            })
25516            .collect();
25517
25518        if !valid_folds.is_empty() {
25519            self.fold_ranges(valid_folds, false, window, cx);
25520        }
25521    }
25522
25523    fn update_lsp_data(
25524        &mut self,
25525        for_buffer: Option<BufferId>,
25526        window: &mut Window,
25527        cx: &mut Context<'_, Self>,
25528    ) {
25529        if !self.enable_lsp_data {
25530            return;
25531        }
25532
25533        if let Some(buffer_id) = for_buffer {
25534            self.pull_diagnostics(buffer_id, window, cx);
25535        }
25536        self.refresh_semantic_tokens(for_buffer, None, cx);
25537        self.refresh_document_colors(for_buffer, window, cx);
25538        self.refresh_folding_ranges(for_buffer, window, cx);
25539        self.refresh_document_symbols(for_buffer, cx);
25540    }
25541
25542    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25543        if !self.mode().is_full() {
25544            return;
25545        }
25546        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25547            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25548        }
25549    }
25550
25551    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25552        if !self.mode().is_full() {
25553            return;
25554        }
25555
25556        if !self.registered_buffers.contains_key(&buffer_id)
25557            && let Some(project) = self.project.as_ref()
25558        {
25559            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25560                project.update(cx, |project, cx| {
25561                    self.registered_buffers.insert(
25562                        buffer_id,
25563                        project.register_buffer_with_language_servers(&buffer, cx),
25564                    );
25565                });
25566            } else {
25567                self.registered_buffers.remove(&buffer_id);
25568            }
25569        }
25570    }
25571
25572    fn create_style(&self, cx: &App) -> EditorStyle {
25573        let settings = ThemeSettings::get_global(cx);
25574
25575        let mut text_style = match self.mode {
25576            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25577                color: cx.theme().colors().editor_foreground,
25578                font_family: settings.ui_font.family.clone(),
25579                font_features: settings.ui_font.features.clone(),
25580                font_fallbacks: settings.ui_font.fallbacks.clone(),
25581                font_size: rems(0.875).into(),
25582                font_weight: settings.ui_font.weight,
25583                line_height: relative(settings.buffer_line_height.value()),
25584                ..Default::default()
25585            },
25586            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25587                color: cx.theme().colors().editor_foreground,
25588                font_family: settings.buffer_font.family.clone(),
25589                font_features: settings.buffer_font.features.clone(),
25590                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25591                font_size: settings.buffer_font_size(cx).into(),
25592                font_weight: settings.buffer_font.weight,
25593                line_height: relative(settings.buffer_line_height.value()),
25594                ..Default::default()
25595            },
25596        };
25597        if let Some(text_style_refinement) = &self.text_style_refinement {
25598            text_style.refine(text_style_refinement)
25599        }
25600
25601        let background = match self.mode {
25602            EditorMode::SingleLine => cx.theme().system().transparent,
25603            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25604            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25605            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25606        };
25607
25608        EditorStyle {
25609            background,
25610            border: cx.theme().colors().border,
25611            local_player: cx.theme().players().local(),
25612            text: text_style,
25613            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25614            syntax: cx.theme().syntax().clone(),
25615            status: cx.theme().status().clone(),
25616            inlay_hints_style: make_inlay_hints_style(cx),
25617            edit_prediction_styles: make_suggestion_styles(cx),
25618            unnecessary_code_fade: settings.unnecessary_code_fade,
25619            show_underlines: self.diagnostics_enabled(),
25620        }
25621    }
25622
25623    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25624        let multibuffer = self.buffer().read(cx);
25625        let is_singleton = multibuffer.is_singleton();
25626        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25627        let buffer = multibuffer.buffer(*buffer_id)?;
25628
25629        let buffer = buffer.read(cx);
25630        let settings = ThemeSettings::get_global(cx);
25631        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25632        let mut breadcrumbs = if is_singleton {
25633            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25634                buffer
25635                    .snapshot()
25636                    .resolve_file_path(
25637                        self.project
25638                            .as_ref()
25639                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25640                            .unwrap_or_default(),
25641                        cx,
25642                    )
25643                    .unwrap_or_else(|| {
25644                        if multibuffer.is_singleton() {
25645                            multibuffer.title(cx).to_string()
25646                        } else {
25647                            "untitled".to_string()
25648                        }
25649                    })
25650            });
25651            vec![BreadcrumbText {
25652                text,
25653                highlights: None,
25654                font: Some(settings.buffer_font.clone()),
25655            }]
25656        } else {
25657            vec![]
25658        };
25659
25660        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25661            text: symbol.text.clone(),
25662            highlights: Some(symbol.highlight_ranges.clone()),
25663            font: Some(settings.buffer_font.clone()),
25664        }));
25665        Some(breadcrumbs)
25666    }
25667
25668    fn disable_lsp_data(&mut self) {
25669        self.enable_lsp_data = false;
25670    }
25671
25672    fn disable_runnables(&mut self) {
25673        self.enable_runnables = false;
25674    }
25675}
25676
25677fn edit_for_markdown_paste<'a>(
25678    buffer: &MultiBufferSnapshot,
25679    range: Range<MultiBufferOffset>,
25680    to_insert: &'a str,
25681    url: Option<url::Url>,
25682) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25683    if url.is_none() {
25684        return (range, Cow::Borrowed(to_insert));
25685    };
25686
25687    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25688
25689    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25690        Cow::Borrowed(to_insert)
25691    } else {
25692        Cow::Owned(format!("[{old_text}]({to_insert})"))
25693    };
25694    (range, new_text)
25695}
25696
25697fn process_completion_for_edit(
25698    completion: &Completion,
25699    intent: CompletionIntent,
25700    buffer: &Entity<Buffer>,
25701    cursor_position: &text::Anchor,
25702    cx: &mut Context<Editor>,
25703) -> CompletionEdit {
25704    let buffer = buffer.read(cx);
25705    let buffer_snapshot = buffer.snapshot();
25706    let (snippet, new_text) = if completion.is_snippet() {
25707        let mut snippet_source = completion.new_text.clone();
25708        // Workaround for typescript language server issues so that methods don't expand within
25709        // strings and functions with type expressions. The previous point is used because the query
25710        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25711        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25712        let previous_point = if previous_point.column > 0 {
25713            cursor_position.to_previous_offset(&buffer_snapshot)
25714        } else {
25715            cursor_position.to_offset(&buffer_snapshot)
25716        };
25717        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25718            && scope.prefers_label_for_snippet_in_completion()
25719            && let Some(label) = completion.label()
25720            && matches!(
25721                completion.kind(),
25722                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25723            )
25724        {
25725            snippet_source = label;
25726        }
25727        match Snippet::parse(&snippet_source).log_err() {
25728            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25729            None => (None, completion.new_text.clone()),
25730        }
25731    } else {
25732        (None, completion.new_text.clone())
25733    };
25734
25735    let mut range_to_replace = {
25736        let replace_range = &completion.replace_range;
25737        if let CompletionSource::Lsp {
25738            insert_range: Some(insert_range),
25739            ..
25740        } = &completion.source
25741        {
25742            debug_assert_eq!(
25743                insert_range.start, replace_range.start,
25744                "insert_range and replace_range should start at the same position"
25745            );
25746            debug_assert!(
25747                insert_range
25748                    .start
25749                    .cmp(cursor_position, &buffer_snapshot)
25750                    .is_le(),
25751                "insert_range should start before or at cursor position"
25752            );
25753            debug_assert!(
25754                replace_range
25755                    .start
25756                    .cmp(cursor_position, &buffer_snapshot)
25757                    .is_le(),
25758                "replace_range should start before or at cursor position"
25759            );
25760
25761            let should_replace = match intent {
25762                CompletionIntent::CompleteWithInsert => false,
25763                CompletionIntent::CompleteWithReplace => true,
25764                CompletionIntent::Complete | CompletionIntent::Compose => {
25765                    let insert_mode =
25766                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25767                            .completions
25768                            .lsp_insert_mode;
25769                    match insert_mode {
25770                        LspInsertMode::Insert => false,
25771                        LspInsertMode::Replace => true,
25772                        LspInsertMode::ReplaceSubsequence => {
25773                            let mut text_to_replace = buffer.chars_for_range(
25774                                buffer.anchor_before(replace_range.start)
25775                                    ..buffer.anchor_after(replace_range.end),
25776                            );
25777                            let mut current_needle = text_to_replace.next();
25778                            for haystack_ch in completion.label.text.chars() {
25779                                if let Some(needle_ch) = current_needle
25780                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25781                                {
25782                                    current_needle = text_to_replace.next();
25783                                }
25784                            }
25785                            current_needle.is_none()
25786                        }
25787                        LspInsertMode::ReplaceSuffix => {
25788                            if replace_range
25789                                .end
25790                                .cmp(cursor_position, &buffer_snapshot)
25791                                .is_gt()
25792                            {
25793                                let range_after_cursor = *cursor_position..replace_range.end;
25794                                let text_after_cursor = buffer
25795                                    .text_for_range(
25796                                        buffer.anchor_before(range_after_cursor.start)
25797                                            ..buffer.anchor_after(range_after_cursor.end),
25798                                    )
25799                                    .collect::<String>()
25800                                    .to_ascii_lowercase();
25801                                completion
25802                                    .label
25803                                    .text
25804                                    .to_ascii_lowercase()
25805                                    .ends_with(&text_after_cursor)
25806                            } else {
25807                                true
25808                            }
25809                        }
25810                    }
25811                }
25812            };
25813
25814            if should_replace {
25815                replace_range.clone()
25816            } else {
25817                insert_range.clone()
25818            }
25819        } else {
25820            replace_range.clone()
25821        }
25822    };
25823
25824    if range_to_replace
25825        .end
25826        .cmp(cursor_position, &buffer_snapshot)
25827        .is_lt()
25828    {
25829        range_to_replace.end = *cursor_position;
25830    }
25831
25832    let replace_range = range_to_replace.to_offset(buffer);
25833    CompletionEdit {
25834        new_text,
25835        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25836        snippet,
25837    }
25838}
25839
25840struct CompletionEdit {
25841    new_text: String,
25842    replace_range: Range<BufferOffset>,
25843    snippet: Option<Snippet>,
25844}
25845
25846fn comment_delimiter_for_newline(
25847    start_point: &Point,
25848    buffer: &MultiBufferSnapshot,
25849    language: &LanguageScope,
25850) -> Option<Arc<str>> {
25851    let delimiters = language.line_comment_prefixes();
25852    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25853    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25854
25855    let num_of_whitespaces = snapshot
25856        .chars_for_range(range.clone())
25857        .take_while(|c| c.is_whitespace())
25858        .count();
25859    let comment_candidate = snapshot
25860        .chars_for_range(range.clone())
25861        .skip(num_of_whitespaces)
25862        .take(max_len_of_delimiter + 2)
25863        .collect::<String>();
25864    let (delimiter, trimmed_len, is_repl) = delimiters
25865        .iter()
25866        .filter_map(|delimiter| {
25867            let prefix = delimiter.trim_end();
25868            if comment_candidate.starts_with(prefix) {
25869                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25870                {
25871                    stripped_comment.starts_with(" %%")
25872                } else {
25873                    false
25874                };
25875                Some((delimiter, prefix.len(), is_repl))
25876            } else {
25877                None
25878            }
25879        })
25880        .max_by_key(|(_, len, _)| *len)?;
25881
25882    if let Some(BlockCommentConfig {
25883        start: block_start, ..
25884    }) = language.block_comment()
25885    {
25886        let block_start_trimmed = block_start.trim_end();
25887        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25888            let line_content = snapshot
25889                .chars_for_range(range.clone())
25890                .skip(num_of_whitespaces)
25891                .take(block_start_trimmed.len())
25892                .collect::<String>();
25893
25894            if line_content.starts_with(block_start_trimmed) {
25895                return None;
25896            }
25897        }
25898    }
25899
25900    let cursor_is_placed_after_comment_marker =
25901        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25902    if cursor_is_placed_after_comment_marker {
25903        if !is_repl {
25904            return Some(delimiter.clone());
25905        }
25906
25907        let line_content_after_cursor: String = snapshot
25908            .chars_for_range(range)
25909            .skip(start_point.column as usize)
25910            .collect();
25911
25912        if line_content_after_cursor.trim().is_empty() {
25913            return None;
25914        } else {
25915            return Some(delimiter.clone());
25916        }
25917    } else {
25918        None
25919    }
25920}
25921
25922fn documentation_delimiter_for_newline(
25923    start_point: &Point,
25924    buffer: &MultiBufferSnapshot,
25925    language: &LanguageScope,
25926    newline_config: &mut NewlineConfig,
25927) -> Option<Arc<str>> {
25928    let BlockCommentConfig {
25929        start: start_tag,
25930        end: end_tag,
25931        prefix: delimiter,
25932        tab_size: len,
25933    } = language.documentation_comment()?;
25934    let is_within_block_comment = buffer
25935        .language_scope_at(*start_point)
25936        .is_some_and(|scope| scope.override_name() == Some("comment"));
25937    if !is_within_block_comment {
25938        return None;
25939    }
25940
25941    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25942
25943    let num_of_whitespaces = snapshot
25944        .chars_for_range(range.clone())
25945        .take_while(|c| c.is_whitespace())
25946        .count();
25947
25948    // 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.
25949    let column = start_point.column;
25950    let cursor_is_after_start_tag = {
25951        let start_tag_len = start_tag.len();
25952        let start_tag_line = snapshot
25953            .chars_for_range(range.clone())
25954            .skip(num_of_whitespaces)
25955            .take(start_tag_len)
25956            .collect::<String>();
25957        if start_tag_line.starts_with(start_tag.as_ref()) {
25958            num_of_whitespaces + start_tag_len <= column as usize
25959        } else {
25960            false
25961        }
25962    };
25963
25964    let cursor_is_after_delimiter = {
25965        let delimiter_trim = delimiter.trim_end();
25966        let delimiter_line = snapshot
25967            .chars_for_range(range.clone())
25968            .skip(num_of_whitespaces)
25969            .take(delimiter_trim.len())
25970            .collect::<String>();
25971        if delimiter_line.starts_with(delimiter_trim) {
25972            num_of_whitespaces + delimiter_trim.len() <= column as usize
25973        } else {
25974            false
25975        }
25976    };
25977
25978    let mut needs_extra_line = false;
25979    let mut extra_line_additional_indent = IndentSize::spaces(0);
25980
25981    let cursor_is_before_end_tag_if_exists = {
25982        let mut char_position = 0u32;
25983        let mut end_tag_offset = None;
25984
25985        'outer: for chunk in snapshot.text_for_range(range) {
25986            if let Some(byte_pos) = chunk.find(&**end_tag) {
25987                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25988                end_tag_offset = Some(char_position + chars_before_match);
25989                break 'outer;
25990            }
25991            char_position += chunk.chars().count() as u32;
25992        }
25993
25994        if let Some(end_tag_offset) = end_tag_offset {
25995            let cursor_is_before_end_tag = column <= end_tag_offset;
25996            if cursor_is_after_start_tag {
25997                if cursor_is_before_end_tag {
25998                    needs_extra_line = true;
25999                }
26000                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26001                if cursor_is_at_start_of_end_tag {
26002                    extra_line_additional_indent.len = *len;
26003                }
26004            }
26005            cursor_is_before_end_tag
26006        } else {
26007            true
26008        }
26009    };
26010
26011    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26012        && cursor_is_before_end_tag_if_exists
26013    {
26014        let additional_indent = if cursor_is_after_start_tag {
26015            IndentSize::spaces(*len)
26016        } else {
26017            IndentSize::spaces(0)
26018        };
26019
26020        *newline_config = NewlineConfig::Newline {
26021            additional_indent,
26022            extra_line_additional_indent: if needs_extra_line {
26023                Some(extra_line_additional_indent)
26024            } else {
26025                None
26026            },
26027            prevent_auto_indent: true,
26028        };
26029        Some(delimiter.clone())
26030    } else {
26031        None
26032    }
26033}
26034
26035const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26036
26037fn list_delimiter_for_newline(
26038    start_point: &Point,
26039    buffer: &MultiBufferSnapshot,
26040    language: &LanguageScope,
26041    newline_config: &mut NewlineConfig,
26042) -> Option<Arc<str>> {
26043    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26044
26045    let num_of_whitespaces = snapshot
26046        .chars_for_range(range.clone())
26047        .take_while(|c| c.is_whitespace())
26048        .count();
26049
26050    let task_list_entries: Vec<_> = language
26051        .task_list()
26052        .into_iter()
26053        .flat_map(|config| {
26054            config
26055                .prefixes
26056                .iter()
26057                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26058        })
26059        .collect();
26060    let unordered_list_entries: Vec<_> = language
26061        .unordered_list()
26062        .iter()
26063        .map(|marker| (marker.as_ref(), marker.as_ref()))
26064        .collect();
26065
26066    let all_entries: Vec<_> = task_list_entries
26067        .into_iter()
26068        .chain(unordered_list_entries)
26069        .collect();
26070
26071    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26072        let candidate: String = snapshot
26073            .chars_for_range(range.clone())
26074            .skip(num_of_whitespaces)
26075            .take(max_prefix_len)
26076            .collect();
26077
26078        if let Some((prefix, continuation)) = all_entries
26079            .iter()
26080            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26081            .max_by_key(|(prefix, _)| prefix.len())
26082        {
26083            let end_of_prefix = num_of_whitespaces + prefix.len();
26084            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26085            let has_content_after_marker = snapshot
26086                .chars_for_range(range)
26087                .skip(end_of_prefix)
26088                .any(|c| !c.is_whitespace());
26089
26090            if has_content_after_marker && cursor_is_after_prefix {
26091                return Some((*continuation).into());
26092            }
26093
26094            if start_point.column as usize == end_of_prefix {
26095                if num_of_whitespaces == 0 {
26096                    *newline_config = NewlineConfig::ClearCurrentLine;
26097                } else {
26098                    *newline_config = NewlineConfig::UnindentCurrentLine {
26099                        continuation: (*continuation).into(),
26100                    };
26101                }
26102            }
26103
26104            return None;
26105        }
26106    }
26107
26108    let candidate: String = snapshot
26109        .chars_for_range(range.clone())
26110        .skip(num_of_whitespaces)
26111        .take(ORDERED_LIST_MAX_MARKER_LEN)
26112        .collect();
26113
26114    for ordered_config in language.ordered_list() {
26115        let regex = match Regex::new(&ordered_config.pattern) {
26116            Ok(r) => r,
26117            Err(_) => continue,
26118        };
26119
26120        if let Some(captures) = regex.captures(&candidate) {
26121            let full_match = captures.get(0)?;
26122            let marker_len = full_match.len();
26123            let end_of_prefix = num_of_whitespaces + marker_len;
26124            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26125
26126            let has_content_after_marker = snapshot
26127                .chars_for_range(range)
26128                .skip(end_of_prefix)
26129                .any(|c| !c.is_whitespace());
26130
26131            if has_content_after_marker && cursor_is_after_prefix {
26132                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26133                let continuation = ordered_config
26134                    .format
26135                    .replace("{1}", &(number + 1).to_string());
26136                return Some(continuation.into());
26137            }
26138
26139            if start_point.column as usize == end_of_prefix {
26140                let continuation = ordered_config.format.replace("{1}", "1");
26141                if num_of_whitespaces == 0 {
26142                    *newline_config = NewlineConfig::ClearCurrentLine;
26143                } else {
26144                    *newline_config = NewlineConfig::UnindentCurrentLine {
26145                        continuation: continuation.into(),
26146                    };
26147                }
26148            }
26149
26150            return None;
26151        }
26152    }
26153
26154    None
26155}
26156
26157fn is_list_prefix_row(
26158    row: MultiBufferRow,
26159    buffer: &MultiBufferSnapshot,
26160    language: &LanguageScope,
26161) -> bool {
26162    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26163        return false;
26164    };
26165
26166    let num_of_whitespaces = snapshot
26167        .chars_for_range(range.clone())
26168        .take_while(|c| c.is_whitespace())
26169        .count();
26170
26171    let task_list_prefixes: Vec<_> = language
26172        .task_list()
26173        .into_iter()
26174        .flat_map(|config| {
26175            config
26176                .prefixes
26177                .iter()
26178                .map(|p| p.as_ref())
26179                .collect::<Vec<_>>()
26180        })
26181        .collect();
26182    let unordered_list_markers: Vec<_> = language
26183        .unordered_list()
26184        .iter()
26185        .map(|marker| marker.as_ref())
26186        .collect();
26187    let all_prefixes: Vec<_> = task_list_prefixes
26188        .into_iter()
26189        .chain(unordered_list_markers)
26190        .collect();
26191    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26192        let candidate: String = snapshot
26193            .chars_for_range(range.clone())
26194            .skip(num_of_whitespaces)
26195            .take(max_prefix_len)
26196            .collect();
26197        if all_prefixes
26198            .iter()
26199            .any(|prefix| candidate.starts_with(*prefix))
26200        {
26201            return true;
26202        }
26203    }
26204
26205    let ordered_list_candidate: String = snapshot
26206        .chars_for_range(range)
26207        .skip(num_of_whitespaces)
26208        .take(ORDERED_LIST_MAX_MARKER_LEN)
26209        .collect();
26210    for ordered_config in language.ordered_list() {
26211        let regex = match Regex::new(&ordered_config.pattern) {
26212            Ok(r) => r,
26213            Err(_) => continue,
26214        };
26215        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26216            return captures.get(0).is_some();
26217        }
26218    }
26219
26220    false
26221}
26222
26223#[derive(Debug)]
26224enum NewlineConfig {
26225    /// Insert newline with optional additional indent and optional extra blank line
26226    Newline {
26227        additional_indent: IndentSize,
26228        extra_line_additional_indent: Option<IndentSize>,
26229        prevent_auto_indent: bool,
26230    },
26231    /// Clear the current line
26232    ClearCurrentLine,
26233    /// Unindent the current line and add continuation
26234    UnindentCurrentLine { continuation: Arc<str> },
26235}
26236
26237impl NewlineConfig {
26238    fn has_extra_line(&self) -> bool {
26239        matches!(
26240            self,
26241            Self::Newline {
26242                extra_line_additional_indent: Some(_),
26243                ..
26244            }
26245        )
26246    }
26247
26248    fn insert_extra_newline_brackets(
26249        buffer: &MultiBufferSnapshot,
26250        range: Range<MultiBufferOffset>,
26251        language: &language::LanguageScope,
26252    ) -> bool {
26253        let leading_whitespace_len = buffer
26254            .reversed_chars_at(range.start)
26255            .take_while(|c| c.is_whitespace() && *c != '\n')
26256            .map(|c| c.len_utf8())
26257            .sum::<usize>();
26258        let trailing_whitespace_len = buffer
26259            .chars_at(range.end)
26260            .take_while(|c| c.is_whitespace() && *c != '\n')
26261            .map(|c| c.len_utf8())
26262            .sum::<usize>();
26263        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26264
26265        language.brackets().any(|(pair, enabled)| {
26266            let pair_start = pair.start.trim_end();
26267            let pair_end = pair.end.trim_start();
26268
26269            enabled
26270                && pair.newline
26271                && buffer.contains_str_at(range.end, pair_end)
26272                && buffer.contains_str_at(
26273                    range.start.saturating_sub_usize(pair_start.len()),
26274                    pair_start,
26275                )
26276        })
26277    }
26278
26279    fn insert_extra_newline_tree_sitter(
26280        buffer: &MultiBufferSnapshot,
26281        range: Range<MultiBufferOffset>,
26282    ) -> bool {
26283        let (buffer, range) = match buffer
26284            .range_to_buffer_ranges(range.start..=range.end)
26285            .as_slice()
26286        {
26287            [(buffer, range, _)] => (*buffer, range.clone()),
26288            _ => return false,
26289        };
26290        let pair = {
26291            let mut result: Option<BracketMatch<usize>> = None;
26292
26293            for pair in buffer
26294                .all_bracket_ranges(range.start.0..range.end.0)
26295                .filter(move |pair| {
26296                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26297                })
26298            {
26299                let len = pair.close_range.end - pair.open_range.start;
26300
26301                if let Some(existing) = &result {
26302                    let existing_len = existing.close_range.end - existing.open_range.start;
26303                    if len > existing_len {
26304                        continue;
26305                    }
26306                }
26307
26308                result = Some(pair);
26309            }
26310
26311            result
26312        };
26313        let Some(pair) = pair else {
26314            return false;
26315        };
26316        pair.newline_only
26317            && buffer
26318                .chars_for_range(pair.open_range.end..range.start.0)
26319                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26320                .all(|c| c.is_whitespace() && c != '\n')
26321    }
26322}
26323
26324fn update_uncommitted_diff_for_buffer(
26325    editor: Entity<Editor>,
26326    project: &Entity<Project>,
26327    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26328    buffer: Entity<MultiBuffer>,
26329    cx: &mut App,
26330) -> Task<()> {
26331    let mut tasks = Vec::new();
26332    project.update(cx, |project, cx| {
26333        for buffer in buffers {
26334            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26335                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26336            }
26337        }
26338    });
26339    cx.spawn(async move |cx| {
26340        let diffs = future::join_all(tasks).await;
26341        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26342            return;
26343        }
26344
26345        buffer.update(cx, |buffer, cx| {
26346            for diff in diffs.into_iter().flatten() {
26347                buffer.add_diff(diff, cx);
26348            }
26349        });
26350    })
26351}
26352
26353fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26354    let tab_size = tab_size.get() as usize;
26355    let mut width = offset;
26356
26357    for ch in text.chars() {
26358        width += if ch == '\t' {
26359            tab_size - (width % tab_size)
26360        } else {
26361            1
26362        };
26363    }
26364
26365    width - offset
26366}
26367
26368#[cfg(test)]
26369mod tests {
26370    use super::*;
26371
26372    #[test]
26373    fn test_string_size_with_expanded_tabs() {
26374        let nz = |val| NonZeroU32::new(val).unwrap();
26375        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26376        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26377        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26378        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26379        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26380        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26381        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26382        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26383    }
26384}
26385
26386/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26387struct WordBreakingTokenizer<'a> {
26388    input: &'a str,
26389}
26390
26391impl<'a> WordBreakingTokenizer<'a> {
26392    fn new(input: &'a str) -> Self {
26393        Self { input }
26394    }
26395}
26396
26397fn is_char_ideographic(ch: char) -> bool {
26398    use unicode_script::Script::*;
26399    use unicode_script::UnicodeScript;
26400    matches!(ch.script(), Han | Tangut | Yi)
26401}
26402
26403fn is_grapheme_ideographic(text: &str) -> bool {
26404    text.chars().any(is_char_ideographic)
26405}
26406
26407fn is_grapheme_whitespace(text: &str) -> bool {
26408    text.chars().any(|x| x.is_whitespace())
26409}
26410
26411fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26412    text.chars()
26413        .next()
26414        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26415}
26416
26417#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26418enum WordBreakToken<'a> {
26419    Word { token: &'a str, grapheme_len: usize },
26420    InlineWhitespace { token: &'a str, grapheme_len: usize },
26421    Newline,
26422}
26423
26424impl<'a> Iterator for WordBreakingTokenizer<'a> {
26425    /// Yields a span, the count of graphemes in the token, and whether it was
26426    /// whitespace. Note that it also breaks at word boundaries.
26427    type Item = WordBreakToken<'a>;
26428
26429    fn next(&mut self) -> Option<Self::Item> {
26430        use unicode_segmentation::UnicodeSegmentation;
26431        if self.input.is_empty() {
26432            return None;
26433        }
26434
26435        let mut iter = self.input.graphemes(true).peekable();
26436        let mut offset = 0;
26437        let mut grapheme_len = 0;
26438        if let Some(first_grapheme) = iter.next() {
26439            let is_newline = first_grapheme == "\n";
26440            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26441            offset += first_grapheme.len();
26442            grapheme_len += 1;
26443            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26444                if let Some(grapheme) = iter.peek().copied()
26445                    && should_stay_with_preceding_ideograph(grapheme)
26446                {
26447                    offset += grapheme.len();
26448                    grapheme_len += 1;
26449                }
26450            } else {
26451                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26452                let mut next_word_bound = words.peek().copied();
26453                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26454                    next_word_bound = words.next();
26455                }
26456                while let Some(grapheme) = iter.peek().copied() {
26457                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26458                        break;
26459                    };
26460                    if is_grapheme_whitespace(grapheme) != is_whitespace
26461                        || (grapheme == "\n") != is_newline
26462                    {
26463                        break;
26464                    };
26465                    offset += grapheme.len();
26466                    grapheme_len += 1;
26467                    iter.next();
26468                }
26469            }
26470            let token = &self.input[..offset];
26471            self.input = &self.input[offset..];
26472            if token == "\n" {
26473                Some(WordBreakToken::Newline)
26474            } else if is_whitespace {
26475                Some(WordBreakToken::InlineWhitespace {
26476                    token,
26477                    grapheme_len,
26478                })
26479            } else {
26480                Some(WordBreakToken::Word {
26481                    token,
26482                    grapheme_len,
26483                })
26484            }
26485        } else {
26486            None
26487        }
26488    }
26489}
26490
26491#[test]
26492fn test_word_breaking_tokenizer() {
26493    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26494        ("", &[]),
26495        ("  ", &[whitespace("  ", 2)]),
26496        ("Ʒ", &[word("Ʒ", 1)]),
26497        ("Ǽ", &[word("Ǽ", 1)]),
26498        ("", &[word("", 1)]),
26499        ("⋑⋑", &[word("⋑⋑", 2)]),
26500        (
26501            "原理,进而",
26502            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26503        ),
26504        (
26505            "hello world",
26506            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26507        ),
26508        (
26509            "hello, world",
26510            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26511        ),
26512        (
26513            "  hello world",
26514            &[
26515                whitespace("  ", 2),
26516                word("hello", 5),
26517                whitespace(" ", 1),
26518                word("world", 5),
26519            ],
26520        ),
26521        (
26522            "这是什么 \n 钢笔",
26523            &[
26524                word("", 1),
26525                word("", 1),
26526                word("", 1),
26527                word("", 1),
26528                whitespace(" ", 1),
26529                newline(),
26530                whitespace(" ", 1),
26531                word("", 1),
26532                word("", 1),
26533            ],
26534        ),
26535        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26536    ];
26537
26538    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26539        WordBreakToken::Word {
26540            token,
26541            grapheme_len,
26542        }
26543    }
26544
26545    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26546        WordBreakToken::InlineWhitespace {
26547            token,
26548            grapheme_len,
26549        }
26550    }
26551
26552    fn newline() -> WordBreakToken<'static> {
26553        WordBreakToken::Newline
26554    }
26555
26556    for (input, result) in tests {
26557        assert_eq!(
26558            WordBreakingTokenizer::new(input)
26559                .collect::<Vec<_>>()
26560                .as_slice(),
26561            *result,
26562        );
26563    }
26564}
26565
26566fn wrap_with_prefix(
26567    first_line_prefix: String,
26568    subsequent_lines_prefix: String,
26569    unwrapped_text: String,
26570    wrap_column: usize,
26571    tab_size: NonZeroU32,
26572    preserve_existing_whitespace: bool,
26573) -> String {
26574    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26575    let subsequent_lines_prefix_len =
26576        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26577    let mut wrapped_text = String::new();
26578    let mut current_line = first_line_prefix;
26579    let mut is_first_line = true;
26580
26581    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26582    let mut current_line_len = first_line_prefix_len;
26583    let mut in_whitespace = false;
26584    for token in tokenizer {
26585        let have_preceding_whitespace = in_whitespace;
26586        match token {
26587            WordBreakToken::Word {
26588                token,
26589                grapheme_len,
26590            } => {
26591                in_whitespace = false;
26592                let current_prefix_len = if is_first_line {
26593                    first_line_prefix_len
26594                } else {
26595                    subsequent_lines_prefix_len
26596                };
26597                if current_line_len + grapheme_len > wrap_column
26598                    && current_line_len != current_prefix_len
26599                {
26600                    wrapped_text.push_str(current_line.trim_end());
26601                    wrapped_text.push('\n');
26602                    is_first_line = false;
26603                    current_line = subsequent_lines_prefix.clone();
26604                    current_line_len = subsequent_lines_prefix_len;
26605                }
26606                current_line.push_str(token);
26607                current_line_len += grapheme_len;
26608            }
26609            WordBreakToken::InlineWhitespace {
26610                mut token,
26611                mut grapheme_len,
26612            } => {
26613                in_whitespace = true;
26614                if have_preceding_whitespace && !preserve_existing_whitespace {
26615                    continue;
26616                }
26617                if !preserve_existing_whitespace {
26618                    // Keep a single whitespace grapheme as-is
26619                    if let Some(first) =
26620                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26621                    {
26622                        token = first;
26623                    } else {
26624                        token = " ";
26625                    }
26626                    grapheme_len = 1;
26627                }
26628                let current_prefix_len = if is_first_line {
26629                    first_line_prefix_len
26630                } else {
26631                    subsequent_lines_prefix_len
26632                };
26633                if current_line_len + grapheme_len > wrap_column {
26634                    wrapped_text.push_str(current_line.trim_end());
26635                    wrapped_text.push('\n');
26636                    is_first_line = false;
26637                    current_line = subsequent_lines_prefix.clone();
26638                    current_line_len = subsequent_lines_prefix_len;
26639                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26640                    current_line.push_str(token);
26641                    current_line_len += grapheme_len;
26642                }
26643            }
26644            WordBreakToken::Newline => {
26645                in_whitespace = true;
26646                let current_prefix_len = if is_first_line {
26647                    first_line_prefix_len
26648                } else {
26649                    subsequent_lines_prefix_len
26650                };
26651                if preserve_existing_whitespace {
26652                    wrapped_text.push_str(current_line.trim_end());
26653                    wrapped_text.push('\n');
26654                    is_first_line = false;
26655                    current_line = subsequent_lines_prefix.clone();
26656                    current_line_len = subsequent_lines_prefix_len;
26657                } else if have_preceding_whitespace {
26658                    continue;
26659                } else if current_line_len + 1 > wrap_column
26660                    && current_line_len != current_prefix_len
26661                {
26662                    wrapped_text.push_str(current_line.trim_end());
26663                    wrapped_text.push('\n');
26664                    is_first_line = false;
26665                    current_line = subsequent_lines_prefix.clone();
26666                    current_line_len = subsequent_lines_prefix_len;
26667                } else if current_line_len != current_prefix_len {
26668                    current_line.push(' ');
26669                    current_line_len += 1;
26670                }
26671            }
26672        }
26673    }
26674
26675    if !current_line.is_empty() {
26676        wrapped_text.push_str(&current_line);
26677    }
26678    wrapped_text
26679}
26680
26681#[test]
26682fn test_wrap_with_prefix() {
26683    assert_eq!(
26684        wrap_with_prefix(
26685            "# ".to_string(),
26686            "# ".to_string(),
26687            "abcdefg".to_string(),
26688            4,
26689            NonZeroU32::new(4).unwrap(),
26690            false,
26691        ),
26692        "# abcdefg"
26693    );
26694    assert_eq!(
26695        wrap_with_prefix(
26696            "".to_string(),
26697            "".to_string(),
26698            "\thello world".to_string(),
26699            8,
26700            NonZeroU32::new(4).unwrap(),
26701            false,
26702        ),
26703        "hello\nworld"
26704    );
26705    assert_eq!(
26706        wrap_with_prefix(
26707            "// ".to_string(),
26708            "// ".to_string(),
26709            "xx \nyy zz aa bb cc".to_string(),
26710            12,
26711            NonZeroU32::new(4).unwrap(),
26712            false,
26713        ),
26714        "// xx yy zz\n// aa bb cc"
26715    );
26716    assert_eq!(
26717        wrap_with_prefix(
26718            String::new(),
26719            String::new(),
26720            "这是什么 \n 钢笔".to_string(),
26721            3,
26722            NonZeroU32::new(4).unwrap(),
26723            false,
26724        ),
26725        "这是什\n么 钢\n"
26726    );
26727    assert_eq!(
26728        wrap_with_prefix(
26729            String::new(),
26730            String::new(),
26731            format!("foo{}bar", '\u{2009}'), // thin space
26732            80,
26733            NonZeroU32::new(4).unwrap(),
26734            false,
26735        ),
26736        format!("foo{}bar", '\u{2009}')
26737    );
26738}
26739
26740pub trait CollaborationHub {
26741    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26742    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26743    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26744}
26745
26746impl CollaborationHub for Entity<Project> {
26747    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26748        self.read(cx).collaborators()
26749    }
26750
26751    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26752        self.read(cx).user_store().read(cx).participant_indices()
26753    }
26754
26755    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26756        let this = self.read(cx);
26757        let user_ids = this.collaborators().values().map(|c| c.user_id);
26758        this.user_store().read(cx).participant_names(user_ids, cx)
26759    }
26760}
26761
26762pub trait SemanticsProvider {
26763    fn hover(
26764        &self,
26765        buffer: &Entity<Buffer>,
26766        position: text::Anchor,
26767        cx: &mut App,
26768    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26769
26770    fn inline_values(
26771        &self,
26772        buffer_handle: Entity<Buffer>,
26773        range: Range<text::Anchor>,
26774        cx: &mut App,
26775    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26776
26777    fn applicable_inlay_chunks(
26778        &self,
26779        buffer: &Entity<Buffer>,
26780        ranges: &[Range<text::Anchor>],
26781        cx: &mut App,
26782    ) -> Vec<Range<BufferRow>>;
26783
26784    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26785
26786    fn inlay_hints(
26787        &self,
26788        invalidate: InvalidationStrategy,
26789        buffer: Entity<Buffer>,
26790        ranges: Vec<Range<text::Anchor>>,
26791        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26792        cx: &mut App,
26793    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26794
26795    fn semantic_tokens(
26796        &self,
26797        buffer: Entity<Buffer>,
26798        refresh: Option<RefreshForServer>,
26799        cx: &mut App,
26800    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>;
26801
26802    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26803
26804    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26805
26806    fn document_highlights(
26807        &self,
26808        buffer: &Entity<Buffer>,
26809        position: text::Anchor,
26810        cx: &mut App,
26811    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26812
26813    fn definitions(
26814        &self,
26815        buffer: &Entity<Buffer>,
26816        position: text::Anchor,
26817        kind: GotoDefinitionKind,
26818        cx: &mut App,
26819    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26820
26821    fn range_for_rename(
26822        &self,
26823        buffer: &Entity<Buffer>,
26824        position: text::Anchor,
26825        cx: &mut App,
26826    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26827
26828    fn perform_rename(
26829        &self,
26830        buffer: &Entity<Buffer>,
26831        position: text::Anchor,
26832        new_name: String,
26833        cx: &mut App,
26834    ) -> Option<Task<Result<ProjectTransaction>>>;
26835}
26836
26837pub trait CompletionProvider {
26838    fn completions(
26839        &self,
26840        excerpt_id: ExcerptId,
26841        buffer: &Entity<Buffer>,
26842        buffer_position: text::Anchor,
26843        trigger: CompletionContext,
26844        window: &mut Window,
26845        cx: &mut Context<Editor>,
26846    ) -> Task<Result<Vec<CompletionResponse>>>;
26847
26848    fn resolve_completions(
26849        &self,
26850        _buffer: Entity<Buffer>,
26851        _completion_indices: Vec<usize>,
26852        _completions: Rc<RefCell<Box<[Completion]>>>,
26853        _cx: &mut Context<Editor>,
26854    ) -> Task<Result<bool>> {
26855        Task::ready(Ok(false))
26856    }
26857
26858    fn apply_additional_edits_for_completion(
26859        &self,
26860        _buffer: Entity<Buffer>,
26861        _completions: Rc<RefCell<Box<[Completion]>>>,
26862        _completion_index: usize,
26863        _push_to_history: bool,
26864        _cx: &mut Context<Editor>,
26865    ) -> Task<Result<Option<language::Transaction>>> {
26866        Task::ready(Ok(None))
26867    }
26868
26869    fn is_completion_trigger(
26870        &self,
26871        buffer: &Entity<Buffer>,
26872        position: language::Anchor,
26873        text: &str,
26874        trigger_in_words: bool,
26875        cx: &mut Context<Editor>,
26876    ) -> bool;
26877
26878    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26879
26880    fn sort_completions(&self) -> bool {
26881        true
26882    }
26883
26884    fn filter_completions(&self) -> bool {
26885        true
26886    }
26887
26888    fn show_snippets(&self) -> bool {
26889        false
26890    }
26891}
26892
26893pub trait CodeActionProvider {
26894    fn id(&self) -> Arc<str>;
26895
26896    fn code_actions(
26897        &self,
26898        buffer: &Entity<Buffer>,
26899        range: Range<text::Anchor>,
26900        window: &mut Window,
26901        cx: &mut App,
26902    ) -> Task<Result<Vec<CodeAction>>>;
26903
26904    fn apply_code_action(
26905        &self,
26906        buffer_handle: Entity<Buffer>,
26907        action: CodeAction,
26908        excerpt_id: ExcerptId,
26909        push_to_history: bool,
26910        window: &mut Window,
26911        cx: &mut App,
26912    ) -> Task<Result<ProjectTransaction>>;
26913}
26914
26915impl CodeActionProvider for Entity<Project> {
26916    fn id(&self) -> Arc<str> {
26917        "project".into()
26918    }
26919
26920    fn code_actions(
26921        &self,
26922        buffer: &Entity<Buffer>,
26923        range: Range<text::Anchor>,
26924        _window: &mut Window,
26925        cx: &mut App,
26926    ) -> Task<Result<Vec<CodeAction>>> {
26927        self.update(cx, |project, cx| {
26928            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26929            let code_actions = project.code_actions(buffer, range, None, cx);
26930            cx.background_spawn(async move {
26931                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26932                Ok(code_lens_actions
26933                    .context("code lens fetch")?
26934                    .into_iter()
26935                    .flatten()
26936                    .chain(
26937                        code_actions
26938                            .context("code action fetch")?
26939                            .into_iter()
26940                            .flatten(),
26941                    )
26942                    .collect())
26943            })
26944        })
26945    }
26946
26947    fn apply_code_action(
26948        &self,
26949        buffer_handle: Entity<Buffer>,
26950        action: CodeAction,
26951        _excerpt_id: ExcerptId,
26952        push_to_history: bool,
26953        _window: &mut Window,
26954        cx: &mut App,
26955    ) -> Task<Result<ProjectTransaction>> {
26956        self.update(cx, |project, cx| {
26957            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26958        })
26959    }
26960}
26961
26962fn snippet_completions(
26963    project: &Project,
26964    buffer: &Entity<Buffer>,
26965    buffer_anchor: text::Anchor,
26966    classifier: CharClassifier,
26967    cx: &mut App,
26968) -> Task<Result<CompletionResponse>> {
26969    let languages = buffer.read(cx).languages_at(buffer_anchor);
26970    let snippet_store = project.snippets().read(cx);
26971
26972    let scopes: Vec<_> = languages
26973        .iter()
26974        .filter_map(|language| {
26975            let language_name = language.lsp_id();
26976            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26977
26978            if snippets.is_empty() {
26979                None
26980            } else {
26981                Some((language.default_scope(), snippets))
26982            }
26983        })
26984        .collect();
26985
26986    if scopes.is_empty() {
26987        return Task::ready(Ok(CompletionResponse {
26988            completions: vec![],
26989            display_options: CompletionDisplayOptions::default(),
26990            is_incomplete: false,
26991        }));
26992    }
26993
26994    let snapshot = buffer.read(cx).text_snapshot();
26995    let executor = cx.background_executor().clone();
26996
26997    cx.background_spawn(async move {
26998        let is_word_char = |c| classifier.is_word(c);
26999
27000        let mut is_incomplete = false;
27001        let mut completions: Vec<Completion> = Vec::new();
27002
27003        const MAX_PREFIX_LEN: usize = 128;
27004        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27005        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27006        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27007
27008        let max_buffer_window: String = snapshot
27009            .text_for_range(window_start..buffer_offset)
27010            .collect();
27011
27012        if max_buffer_window.is_empty() {
27013            return Ok(CompletionResponse {
27014                completions: vec![],
27015                display_options: CompletionDisplayOptions::default(),
27016                is_incomplete: true,
27017            });
27018        }
27019
27020        for (_scope, snippets) in scopes.into_iter() {
27021            // Sort snippets by word count to match longer snippet prefixes first.
27022            let mut sorted_snippet_candidates = snippets
27023                .iter()
27024                .enumerate()
27025                .flat_map(|(snippet_ix, snippet)| {
27026                    snippet
27027                        .prefix
27028                        .iter()
27029                        .enumerate()
27030                        .map(move |(prefix_ix, prefix)| {
27031                            let word_count =
27032                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27033                            ((snippet_ix, prefix_ix), prefix, word_count)
27034                        })
27035                })
27036                .collect_vec();
27037            sorted_snippet_candidates
27038                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27039
27040            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27041
27042            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27043                .take(
27044                    sorted_snippet_candidates
27045                        .first()
27046                        .map(|(_, _, word_count)| *word_count)
27047                        .unwrap_or_default(),
27048                )
27049                .collect_vec();
27050
27051            const MAX_RESULTS: usize = 100;
27052            // Each match also remembers how many characters from the buffer it consumed
27053            let mut matches: Vec<(StringMatch, usize)> = vec![];
27054
27055            let mut snippet_list_cutoff_index = 0;
27056            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27057                let word_count = buffer_index + 1;
27058                // Increase `snippet_list_cutoff_index` until we have all of the
27059                // snippets with sufficiently many words.
27060                while sorted_snippet_candidates
27061                    .get(snippet_list_cutoff_index)
27062                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27063                        *snippet_word_count >= word_count
27064                    })
27065                {
27066                    snippet_list_cutoff_index += 1;
27067                }
27068
27069                // Take only the candidates with at least `word_count` many words
27070                let snippet_candidates_at_word_len =
27071                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27072
27073                let candidates = snippet_candidates_at_word_len
27074                    .iter()
27075                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27076                    .enumerate() // index in `sorted_snippet_candidates`
27077                    // First char must match
27078                    .filter(|(_ix, prefix)| {
27079                        itertools::equal(
27080                            prefix
27081                                .chars()
27082                                .next()
27083                                .into_iter()
27084                                .flat_map(|c| c.to_lowercase()),
27085                            buffer_window
27086                                .chars()
27087                                .next()
27088                                .into_iter()
27089                                .flat_map(|c| c.to_lowercase()),
27090                        )
27091                    })
27092                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27093                    .collect::<Vec<StringMatchCandidate>>();
27094
27095                matches.extend(
27096                    fuzzy::match_strings(
27097                        &candidates,
27098                        &buffer_window,
27099                        buffer_window.chars().any(|c| c.is_uppercase()),
27100                        true,
27101                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27102                        &Default::default(),
27103                        executor.clone(),
27104                    )
27105                    .await
27106                    .into_iter()
27107                    .map(|string_match| (string_match, buffer_window.len())),
27108                );
27109
27110                if matches.len() >= MAX_RESULTS {
27111                    break;
27112                }
27113            }
27114
27115            let to_lsp = |point: &text::Anchor| {
27116                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27117                point_to_lsp(end)
27118            };
27119            let lsp_end = to_lsp(&buffer_anchor);
27120
27121            if matches.len() >= MAX_RESULTS {
27122                is_incomplete = true;
27123            }
27124
27125            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27126                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27127                    sorted_snippet_candidates[string_match.candidate_id];
27128                let snippet = &snippets[snippet_index];
27129                let start = buffer_offset - buffer_window_len;
27130                let start = snapshot.anchor_before(start);
27131                let range = start..buffer_anchor;
27132                let lsp_start = to_lsp(&start);
27133                let lsp_range = lsp::Range {
27134                    start: lsp_start,
27135                    end: lsp_end,
27136                };
27137                Completion {
27138                    replace_range: range,
27139                    new_text: snippet.body.clone(),
27140                    source: CompletionSource::Lsp {
27141                        insert_range: None,
27142                        server_id: LanguageServerId(usize::MAX),
27143                        resolved: true,
27144                        lsp_completion: Box::new(lsp::CompletionItem {
27145                            label: snippet.prefix.first().unwrap().clone(),
27146                            kind: Some(CompletionItemKind::SNIPPET),
27147                            label_details: snippet.description.as_ref().map(|description| {
27148                                lsp::CompletionItemLabelDetails {
27149                                    detail: Some(description.clone()),
27150                                    description: None,
27151                                }
27152                            }),
27153                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27154                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27155                                lsp::InsertReplaceEdit {
27156                                    new_text: snippet.body.clone(),
27157                                    insert: lsp_range,
27158                                    replace: lsp_range,
27159                                },
27160                            )),
27161                            filter_text: Some(snippet.body.clone()),
27162                            sort_text: Some(char::MAX.to_string()),
27163                            ..lsp::CompletionItem::default()
27164                        }),
27165                        lsp_defaults: None,
27166                    },
27167                    label: CodeLabel {
27168                        text: matching_prefix.clone(),
27169                        runs: Vec::new(),
27170                        filter_range: 0..matching_prefix.len(),
27171                    },
27172                    icon_path: None,
27173                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27174                        single_line: snippet.name.clone().into(),
27175                        plain_text: snippet
27176                            .description
27177                            .clone()
27178                            .map(|description| description.into()),
27179                    }),
27180                    insert_text_mode: None,
27181                    confirm: None,
27182                    match_start: Some(start),
27183                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27184                }
27185            }));
27186        }
27187
27188        Ok(CompletionResponse {
27189            completions,
27190            display_options: CompletionDisplayOptions::default(),
27191            is_incomplete,
27192        })
27193    })
27194}
27195
27196impl CompletionProvider for Entity<Project> {
27197    fn completions(
27198        &self,
27199        _excerpt_id: ExcerptId,
27200        buffer: &Entity<Buffer>,
27201        buffer_position: text::Anchor,
27202        options: CompletionContext,
27203        _window: &mut Window,
27204        cx: &mut Context<Editor>,
27205    ) -> Task<Result<Vec<CompletionResponse>>> {
27206        self.update(cx, |project, cx| {
27207            let task = project.completions(buffer, buffer_position, options, cx);
27208            cx.background_spawn(task)
27209        })
27210    }
27211
27212    fn resolve_completions(
27213        &self,
27214        buffer: Entity<Buffer>,
27215        completion_indices: Vec<usize>,
27216        completions: Rc<RefCell<Box<[Completion]>>>,
27217        cx: &mut Context<Editor>,
27218    ) -> Task<Result<bool>> {
27219        self.update(cx, |project, cx| {
27220            project.lsp_store().update(cx, |lsp_store, cx| {
27221                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27222            })
27223        })
27224    }
27225
27226    fn apply_additional_edits_for_completion(
27227        &self,
27228        buffer: Entity<Buffer>,
27229        completions: Rc<RefCell<Box<[Completion]>>>,
27230        completion_index: usize,
27231        push_to_history: bool,
27232        cx: &mut Context<Editor>,
27233    ) -> Task<Result<Option<language::Transaction>>> {
27234        self.update(cx, |project, cx| {
27235            project.lsp_store().update(cx, |lsp_store, cx| {
27236                lsp_store.apply_additional_edits_for_completion(
27237                    buffer,
27238                    completions,
27239                    completion_index,
27240                    push_to_history,
27241                    cx,
27242                )
27243            })
27244        })
27245    }
27246
27247    fn is_completion_trigger(
27248        &self,
27249        buffer: &Entity<Buffer>,
27250        position: language::Anchor,
27251        text: &str,
27252        trigger_in_words: bool,
27253        cx: &mut Context<Editor>,
27254    ) -> bool {
27255        let mut chars = text.chars();
27256        let char = if let Some(char) = chars.next() {
27257            char
27258        } else {
27259            return false;
27260        };
27261        if chars.next().is_some() {
27262            return false;
27263        }
27264
27265        let buffer = buffer.read(cx);
27266        let snapshot = buffer.snapshot();
27267        let classifier = snapshot
27268            .char_classifier_at(position)
27269            .scope_context(Some(CharScopeContext::Completion));
27270        if trigger_in_words && classifier.is_word(char) {
27271            return true;
27272        }
27273
27274        buffer.completion_triggers().contains(text)
27275    }
27276
27277    fn show_snippets(&self) -> bool {
27278        true
27279    }
27280}
27281
27282impl SemanticsProvider for Entity<Project> {
27283    fn hover(
27284        &self,
27285        buffer: &Entity<Buffer>,
27286        position: text::Anchor,
27287        cx: &mut App,
27288    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27289        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
27290    }
27291
27292    fn document_highlights(
27293        &self,
27294        buffer: &Entity<Buffer>,
27295        position: text::Anchor,
27296        cx: &mut App,
27297    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27298        Some(self.update(cx, |project, cx| {
27299            project.document_highlights(buffer, position, cx)
27300        }))
27301    }
27302
27303    fn definitions(
27304        &self,
27305        buffer: &Entity<Buffer>,
27306        position: text::Anchor,
27307        kind: GotoDefinitionKind,
27308        cx: &mut App,
27309    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27310        Some(self.update(cx, |project, cx| match kind {
27311            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27312            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27313            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27314            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27315        }))
27316    }
27317
27318    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27319        self.update(cx, |project, cx| {
27320            if project
27321                .active_debug_session(cx)
27322                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27323            {
27324                return true;
27325            }
27326
27327            buffer.update(cx, |buffer, cx| {
27328                project.any_language_server_supports_inlay_hints(buffer, cx)
27329            })
27330        })
27331    }
27332
27333    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27334        self.update(cx, |project, cx| {
27335            buffer.update(cx, |buffer, cx| {
27336                project.any_language_server_supports_semantic_tokens(buffer, cx)
27337            })
27338        })
27339    }
27340
27341    fn inline_values(
27342        &self,
27343        buffer_handle: Entity<Buffer>,
27344        range: Range<text::Anchor>,
27345        cx: &mut App,
27346    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27347        self.update(cx, |project, cx| {
27348            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27349
27350            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27351        })
27352    }
27353
27354    fn applicable_inlay_chunks(
27355        &self,
27356        buffer: &Entity<Buffer>,
27357        ranges: &[Range<text::Anchor>],
27358        cx: &mut App,
27359    ) -> Vec<Range<BufferRow>> {
27360        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27361            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27362        })
27363    }
27364
27365    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27366        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
27367            lsp_store.invalidate_inlay_hints(for_buffers)
27368        });
27369    }
27370
27371    fn inlay_hints(
27372        &self,
27373        invalidate: InvalidationStrategy,
27374        buffer: Entity<Buffer>,
27375        ranges: Vec<Range<text::Anchor>>,
27376        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27377        cx: &mut App,
27378    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27379        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27380            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27381        }))
27382    }
27383
27384    fn semantic_tokens(
27385        &self,
27386        buffer: Entity<Buffer>,
27387        refresh: Option<RefreshForServer>,
27388        cx: &mut App,
27389    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>> {
27390        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27391            lsp_store.semantic_tokens(buffer, refresh, cx)
27392        })
27393    }
27394
27395    fn range_for_rename(
27396        &self,
27397        buffer: &Entity<Buffer>,
27398        position: text::Anchor,
27399        cx: &mut App,
27400    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27401        Some(self.update(cx, |project, cx| {
27402            let buffer = buffer.clone();
27403            let task = project.prepare_rename(buffer.clone(), position, cx);
27404            cx.spawn(async move |_, cx| {
27405                Ok(match task.await? {
27406                    PrepareRenameResponse::Success(range) => Some(range),
27407                    PrepareRenameResponse::InvalidPosition => None,
27408                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27409                        // Fallback on using TreeSitter info to determine identifier range
27410                        buffer.read_with(cx, |buffer, _| {
27411                            let snapshot = buffer.snapshot();
27412                            let (range, kind) = snapshot.surrounding_word(position, None);
27413                            if kind != Some(CharKind::Word) {
27414                                return None;
27415                            }
27416                            Some(
27417                                snapshot.anchor_before(range.start)
27418                                    ..snapshot.anchor_after(range.end),
27419                            )
27420                        })
27421                    }
27422                })
27423            })
27424        }))
27425    }
27426
27427    fn perform_rename(
27428        &self,
27429        buffer: &Entity<Buffer>,
27430        position: text::Anchor,
27431        new_name: String,
27432        cx: &mut App,
27433    ) -> Option<Task<Result<ProjectTransaction>>> {
27434        Some(self.update(cx, |project, cx| {
27435            project.perform_rename(buffer.clone(), position, new_name, cx)
27436        }))
27437    }
27438}
27439
27440fn consume_contiguous_rows(
27441    contiguous_row_selections: &mut Vec<Selection<Point>>,
27442    selection: &Selection<Point>,
27443    display_map: &DisplaySnapshot,
27444    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27445) -> (MultiBufferRow, MultiBufferRow) {
27446    contiguous_row_selections.push(selection.clone());
27447    let start_row = starting_row(selection, display_map);
27448    let mut end_row = ending_row(selection, display_map);
27449
27450    while let Some(next_selection) = selections.peek() {
27451        if next_selection.start.row <= end_row.0 {
27452            end_row = ending_row(next_selection, display_map);
27453            contiguous_row_selections.push(selections.next().unwrap().clone());
27454        } else {
27455            break;
27456        }
27457    }
27458    (start_row, end_row)
27459}
27460
27461fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27462    if selection.start.column > 0 {
27463        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27464    } else {
27465        MultiBufferRow(selection.start.row)
27466    }
27467}
27468
27469fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27470    if next_selection.end.column > 0 || next_selection.is_empty() {
27471        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27472    } else {
27473        MultiBufferRow(next_selection.end.row)
27474    }
27475}
27476
27477impl EditorSnapshot {
27478    pub fn remote_selections_in_range<'a>(
27479        &'a self,
27480        range: &'a Range<Anchor>,
27481        collaboration_hub: &dyn CollaborationHub,
27482        cx: &'a App,
27483    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27484        let participant_names = collaboration_hub.user_names(cx);
27485        let participant_indices = collaboration_hub.user_participant_indices(cx);
27486        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27487        let collaborators_by_replica_id = collaborators_by_peer_id
27488            .values()
27489            .map(|collaborator| (collaborator.replica_id, collaborator))
27490            .collect::<HashMap<_, _>>();
27491        self.buffer_snapshot()
27492            .selections_in_range(range, false)
27493            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27494                if replica_id == ReplicaId::AGENT {
27495                    Some(RemoteSelection {
27496                        replica_id,
27497                        selection,
27498                        cursor_shape,
27499                        line_mode,
27500                        collaborator_id: CollaboratorId::Agent,
27501                        user_name: Some("Agent".into()),
27502                        color: cx.theme().players().agent(),
27503                    })
27504                } else {
27505                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27506                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27507                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27508                    Some(RemoteSelection {
27509                        replica_id,
27510                        selection,
27511                        cursor_shape,
27512                        line_mode,
27513                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27514                        user_name,
27515                        color: if let Some(index) = participant_index {
27516                            cx.theme().players().color_for_participant(index.0)
27517                        } else {
27518                            cx.theme().players().absent()
27519                        },
27520                    })
27521                }
27522            })
27523    }
27524
27525    pub fn hunks_for_ranges(
27526        &self,
27527        ranges: impl IntoIterator<Item = Range<Point>>,
27528    ) -> Vec<MultiBufferDiffHunk> {
27529        let mut hunks = Vec::new();
27530        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27531            HashMap::default();
27532        for query_range in ranges {
27533            let query_rows =
27534                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27535            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27536                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27537            ) {
27538                // Include deleted hunks that are adjacent to the query range, because
27539                // otherwise they would be missed.
27540                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27541                if hunk.status().is_deleted() {
27542                    intersects_range |= hunk.row_range.start == query_rows.end;
27543                    intersects_range |= hunk.row_range.end == query_rows.start;
27544                }
27545                if intersects_range {
27546                    if !processed_buffer_rows
27547                        .entry(hunk.buffer_id)
27548                        .or_default()
27549                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27550                    {
27551                        continue;
27552                    }
27553                    hunks.push(hunk);
27554                }
27555            }
27556        }
27557
27558        hunks
27559    }
27560
27561    fn display_diff_hunks_for_rows<'a>(
27562        &'a self,
27563        display_rows: Range<DisplayRow>,
27564        folded_buffers: &'a HashSet<BufferId>,
27565    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27566        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27567        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27568
27569        self.buffer_snapshot()
27570            .diff_hunks_in_range(buffer_start..buffer_end)
27571            .filter_map(|hunk| {
27572                if folded_buffers.contains(&hunk.buffer_id)
27573                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27574                {
27575                    return None;
27576                }
27577
27578                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27579                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27580                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27581                    let line_len = self.buffer_snapshot().line_len(last_row);
27582                    Point::new(last_row.0, line_len)
27583                } else {
27584                    Point::new(hunk.row_range.end.0, 0)
27585                };
27586
27587                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27588                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27589
27590                let display_hunk = if hunk_display_start.column() != 0 {
27591                    DisplayDiffHunk::Folded {
27592                        display_row: hunk_display_start.row(),
27593                    }
27594                } else {
27595                    let mut end_row = hunk_display_end.row();
27596                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27597                        end_row.0 += 1;
27598                    }
27599                    let is_created_file = hunk.is_created_file();
27600
27601                    DisplayDiffHunk::Unfolded {
27602                        status: hunk.status(),
27603                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27604                            ..hunk.diff_base_byte_range.end.0,
27605                        word_diffs: hunk.word_diffs,
27606                        display_row_range: hunk_display_start.row()..end_row,
27607                        multi_buffer_range: Anchor::range_in_buffer(
27608                            hunk.excerpt_id,
27609                            hunk.buffer_range,
27610                        ),
27611                        is_created_file,
27612                    }
27613                };
27614
27615                Some(display_hunk)
27616            })
27617    }
27618
27619    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27620        self.display_snapshot
27621            .buffer_snapshot()
27622            .language_at(position)
27623    }
27624
27625    pub fn is_focused(&self) -> bool {
27626        self.is_focused
27627    }
27628
27629    pub fn placeholder_text(&self) -> Option<String> {
27630        self.placeholder_display_snapshot
27631            .as_ref()
27632            .map(|display_map| display_map.text())
27633    }
27634
27635    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27636        self.scroll_anchor.scroll_position(&self.display_snapshot)
27637    }
27638
27639    pub fn gutter_dimensions(
27640        &self,
27641        font_id: FontId,
27642        font_size: Pixels,
27643        style: &EditorStyle,
27644        window: &mut Window,
27645        cx: &App,
27646    ) -> GutterDimensions {
27647        if self.show_gutter
27648            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27649            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27650        {
27651            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27652                matches!(
27653                    ProjectSettings::get_global(cx).git.git_gutter,
27654                    GitGutterSetting::TrackedFiles
27655                )
27656            });
27657            let gutter_settings = EditorSettings::get_global(cx).gutter;
27658            let show_line_numbers = self
27659                .show_line_numbers
27660                .unwrap_or(gutter_settings.line_numbers);
27661            let line_gutter_width = if show_line_numbers {
27662                // Avoid flicker-like gutter resizes when the line number gains another digit by
27663                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27664                let min_width_for_number_on_gutter =
27665                    ch_advance * gutter_settings.min_line_number_digits as f32;
27666                self.max_line_number_width(style, window)
27667                    .max(min_width_for_number_on_gutter)
27668            } else {
27669                0.0.into()
27670            };
27671
27672            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27673            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27674
27675            let git_blame_entries_width =
27676                self.git_blame_gutter_max_author_length
27677                    .map(|max_author_length| {
27678                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27679                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27680
27681                        /// The number of characters to dedicate to gaps and margins.
27682                        const SPACING_WIDTH: usize = 4;
27683
27684                        let max_char_count = max_author_length.min(renderer.max_author_length())
27685                            + ::git::SHORT_SHA_LENGTH
27686                            + MAX_RELATIVE_TIMESTAMP.len()
27687                            + SPACING_WIDTH;
27688
27689                        ch_advance * max_char_count
27690                    });
27691
27692            let is_singleton = self.buffer_snapshot().is_singleton();
27693
27694            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27695            left_padding += if !is_singleton {
27696                ch_width * 4.0
27697            } else if show_runnables || show_breakpoints {
27698                ch_width * 3.0
27699            } else if show_git_gutter && show_line_numbers {
27700                ch_width * 2.0
27701            } else if show_git_gutter || show_line_numbers {
27702                ch_width
27703            } else {
27704                px(0.)
27705            };
27706
27707            let shows_folds = is_singleton && gutter_settings.folds;
27708
27709            let right_padding = if shows_folds && show_line_numbers {
27710                ch_width * 4.0
27711            } else if shows_folds || (!is_singleton && show_line_numbers) {
27712                ch_width * 3.0
27713            } else if show_line_numbers {
27714                ch_width
27715            } else {
27716                px(0.)
27717            };
27718
27719            GutterDimensions {
27720                left_padding,
27721                right_padding,
27722                width: line_gutter_width + left_padding + right_padding,
27723                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27724                git_blame_entries_width,
27725            }
27726        } else if self.offset_content {
27727            GutterDimensions::default_with_margin(font_id, font_size, cx)
27728        } else {
27729            GutterDimensions::default()
27730        }
27731    }
27732
27733    pub fn render_crease_toggle(
27734        &self,
27735        buffer_row: MultiBufferRow,
27736        row_contains_cursor: bool,
27737        editor: Entity<Editor>,
27738        window: &mut Window,
27739        cx: &mut App,
27740    ) -> Option<AnyElement> {
27741        let folded = self.is_line_folded(buffer_row);
27742        let mut is_foldable = false;
27743
27744        if let Some(crease) = self
27745            .crease_snapshot
27746            .query_row(buffer_row, self.buffer_snapshot())
27747        {
27748            is_foldable = true;
27749            match crease {
27750                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27751                    if let Some(render_toggle) = render_toggle {
27752                        let toggle_callback =
27753                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27754                                if folded {
27755                                    editor.update(cx, |editor, cx| {
27756                                        editor.fold_at(buffer_row, window, cx)
27757                                    });
27758                                } else {
27759                                    editor.update(cx, |editor, cx| {
27760                                        editor.unfold_at(buffer_row, window, cx)
27761                                    });
27762                                }
27763                            });
27764                        return Some((render_toggle)(
27765                            buffer_row,
27766                            folded,
27767                            toggle_callback,
27768                            window,
27769                            cx,
27770                        ));
27771                    }
27772                }
27773            }
27774        }
27775
27776        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27777
27778        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27779            Some(
27780                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27781                    .toggle_state(folded)
27782                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27783                        if folded {
27784                            this.unfold_at(buffer_row, window, cx);
27785                        } else {
27786                            this.fold_at(buffer_row, window, cx);
27787                        }
27788                    }))
27789                    .into_any_element(),
27790            )
27791        } else {
27792            None
27793        }
27794    }
27795
27796    pub fn render_crease_trailer(
27797        &self,
27798        buffer_row: MultiBufferRow,
27799        window: &mut Window,
27800        cx: &mut App,
27801    ) -> Option<AnyElement> {
27802        let folded = self.is_line_folded(buffer_row);
27803        if let Crease::Inline { render_trailer, .. } = self
27804            .crease_snapshot
27805            .query_row(buffer_row, self.buffer_snapshot())?
27806        {
27807            let render_trailer = render_trailer.as_ref()?;
27808            Some(render_trailer(buffer_row, folded, window, cx))
27809        } else {
27810            None
27811        }
27812    }
27813
27814    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27815        let digit_count = self.widest_line_number().ilog10() + 1;
27816        column_pixels(style, digit_count as usize, window)
27817    }
27818
27819    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27820    ///
27821    /// This is positive if `base` is before `line`.
27822    fn relative_line_delta(
27823        &self,
27824        current_selection_head: DisplayRow,
27825        first_visible_row: DisplayRow,
27826        consider_wrapped_lines: bool,
27827    ) -> i64 {
27828        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27829        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27830
27831        if consider_wrapped_lines {
27832            let wrap_snapshot = self.wrap_snapshot();
27833            let base_wrap_row = wrap_snapshot
27834                .make_wrap_point(current_selection_head, Bias::Left)
27835                .row();
27836            let wrap_row = wrap_snapshot
27837                .make_wrap_point(first_visible_row, Bias::Left)
27838                .row();
27839
27840            wrap_row.0 as i64 - base_wrap_row.0 as i64
27841        } else {
27842            let fold_snapshot = self.fold_snapshot();
27843            let base_fold_row = fold_snapshot
27844                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27845                .row();
27846            let fold_row = fold_snapshot
27847                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27848                .row();
27849
27850            fold_row as i64 - base_fold_row as i64
27851        }
27852    }
27853
27854    /// Returns the unsigned relative line number to display for each row in `rows`.
27855    ///
27856    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27857    pub fn calculate_relative_line_numbers(
27858        &self,
27859        rows: &Range<DisplayRow>,
27860        current_selection_head: DisplayRow,
27861        count_wrapped_lines: bool,
27862    ) -> HashMap<DisplayRow, u32> {
27863        let initial_offset =
27864            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27865
27866        self.row_infos(rows.start)
27867            .take(rows.len())
27868            .enumerate()
27869            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27870            .filter(|(_row, row_info)| {
27871                row_info.buffer_row.is_some()
27872                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27873            })
27874            .enumerate()
27875            .filter_map(|(i, (row, row_info))| {
27876                // We want to ensure here that the current line has absolute
27877                // numbering, even if we are in a soft-wrapped line. With the
27878                // exception that if we are in a deleted line, we should number this
27879                // relative with 0, as otherwise it would have no line number at all
27880                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27881
27882                (relative_line_number != 0
27883                    || row_info
27884                        .diff_status
27885                        .is_some_and(|status| status.is_deleted()))
27886                .then_some((row, relative_line_number))
27887            })
27888            .collect()
27889    }
27890}
27891
27892pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27893    let font_size = style.text.font_size.to_pixels(window.rem_size());
27894    let layout = window.text_system().shape_line(
27895        SharedString::from(" ".repeat(column)),
27896        font_size,
27897        &[TextRun {
27898            len: column,
27899            font: style.text.font(),
27900            color: Hsla::default(),
27901            ..Default::default()
27902        }],
27903        None,
27904    );
27905
27906    layout.width
27907}
27908
27909impl Deref for EditorSnapshot {
27910    type Target = DisplaySnapshot;
27911
27912    fn deref(&self) -> &Self::Target {
27913        &self.display_snapshot
27914    }
27915}
27916
27917#[derive(Clone, Debug, PartialEq, Eq)]
27918pub enum EditorEvent {
27919    /// Emitted when the stored review comments change (added, removed, or updated).
27920    ReviewCommentsChanged {
27921        /// The new total count of review comments.
27922        total_count: usize,
27923    },
27924    InputIgnored {
27925        text: Arc<str>,
27926    },
27927    InputHandled {
27928        utf16_range_to_replace: Option<Range<isize>>,
27929        text: Arc<str>,
27930    },
27931    ExcerptsAdded {
27932        buffer: Entity<Buffer>,
27933        predecessor: ExcerptId,
27934        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27935    },
27936    ExcerptsRemoved {
27937        ids: Vec<ExcerptId>,
27938        removed_buffer_ids: Vec<BufferId>,
27939    },
27940    BufferFoldToggled {
27941        ids: Vec<ExcerptId>,
27942        folded: bool,
27943    },
27944    ExcerptsEdited {
27945        ids: Vec<ExcerptId>,
27946    },
27947    ExcerptsExpanded {
27948        ids: Vec<ExcerptId>,
27949    },
27950    ExpandExcerptsRequested {
27951        excerpt_ids: Vec<ExcerptId>,
27952        lines: u32,
27953        direction: ExpandExcerptDirection,
27954    },
27955    StageOrUnstageRequested {
27956        stage: bool,
27957        hunks: Vec<MultiBufferDiffHunk>,
27958    },
27959    OpenExcerptsRequested {
27960        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27961        split: bool,
27962    },
27963    RestoreRequested {
27964        hunks: Vec<MultiBufferDiffHunk>,
27965    },
27966    BufferEdited,
27967    Edited {
27968        transaction_id: clock::Lamport,
27969    },
27970    Reparsed(BufferId),
27971    Focused,
27972    FocusedIn,
27973    Blurred,
27974    DirtyChanged,
27975    Saved,
27976    TitleChanged,
27977    SelectionsChanged {
27978        local: bool,
27979    },
27980    ScrollPositionChanged {
27981        local: bool,
27982        autoscroll: bool,
27983    },
27984    TransactionUndone {
27985        transaction_id: clock::Lamport,
27986    },
27987    TransactionBegun {
27988        transaction_id: clock::Lamport,
27989    },
27990    CursorShapeChanged,
27991    BreadcrumbsChanged,
27992    OutlineSymbolsChanged,
27993    PushedToNavHistory {
27994        anchor: Anchor,
27995        is_deactivate: bool,
27996    },
27997}
27998
27999impl EventEmitter<EditorEvent> for Editor {}
28000
28001impl Focusable for Editor {
28002    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28003        self.focus_handle.clone()
28004    }
28005}
28006
28007impl Render for Editor {
28008    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28009        EditorElement::new(&cx.entity(), self.create_style(cx))
28010    }
28011}
28012
28013impl EntityInputHandler for Editor {
28014    fn text_for_range(
28015        &mut self,
28016        range_utf16: Range<usize>,
28017        adjusted_range: &mut Option<Range<usize>>,
28018        _: &mut Window,
28019        cx: &mut Context<Self>,
28020    ) -> Option<String> {
28021        let snapshot = self.buffer.read(cx).read(cx);
28022        let start = snapshot.clip_offset_utf16(
28023            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28024            Bias::Left,
28025        );
28026        let end = snapshot.clip_offset_utf16(
28027            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28028            Bias::Right,
28029        );
28030        if (start.0.0..end.0.0) != range_utf16 {
28031            adjusted_range.replace(start.0.0..end.0.0);
28032        }
28033        Some(snapshot.text_for_range(start..end).collect())
28034    }
28035
28036    fn selected_text_range(
28037        &mut self,
28038        ignore_disabled_input: bool,
28039        _: &mut Window,
28040        cx: &mut Context<Self>,
28041    ) -> Option<UTF16Selection> {
28042        // Prevent the IME menu from appearing when holding down an alphabetic key
28043        // while input is disabled.
28044        if !ignore_disabled_input && !self.input_enabled {
28045            return None;
28046        }
28047
28048        let selection = self
28049            .selections
28050            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28051        let range = selection.range();
28052
28053        Some(UTF16Selection {
28054            range: range.start.0.0..range.end.0.0,
28055            reversed: selection.reversed,
28056        })
28057    }
28058
28059    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28060        let snapshot = self.buffer.read(cx).read(cx);
28061        let range = self
28062            .text_highlights(HighlightKey::InputComposition, cx)?
28063            .1
28064            .first()?;
28065        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28066    }
28067
28068    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28069        self.clear_highlights(HighlightKey::InputComposition, cx);
28070        self.ime_transaction.take();
28071    }
28072
28073    fn replace_text_in_range(
28074        &mut self,
28075        range_utf16: Option<Range<usize>>,
28076        text: &str,
28077        window: &mut Window,
28078        cx: &mut Context<Self>,
28079    ) {
28080        if !self.input_enabled {
28081            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28082            return;
28083        }
28084
28085        self.transact(window, cx, |this, window, cx| {
28086            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28087                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28088                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28089                Some(this.selection_replacement_ranges(range_utf16, cx))
28090            } else {
28091                this.marked_text_ranges(cx)
28092            };
28093
28094            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28095                let newest_selection_id = this.selections.newest_anchor().id;
28096                this.selections
28097                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28098                    .iter()
28099                    .zip(ranges_to_replace.iter())
28100                    .find_map(|(selection, range)| {
28101                        if selection.id == newest_selection_id {
28102                            Some(
28103                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28104                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28105                            )
28106                        } else {
28107                            None
28108                        }
28109                    })
28110            });
28111
28112            cx.emit(EditorEvent::InputHandled {
28113                utf16_range_to_replace: range_to_replace,
28114                text: text.into(),
28115            });
28116
28117            if let Some(new_selected_ranges) = new_selected_ranges {
28118                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28119                    selections.select_ranges(new_selected_ranges)
28120                });
28121                this.backspace(&Default::default(), window, cx);
28122            }
28123
28124            this.handle_input(text, window, cx);
28125        });
28126
28127        if let Some(transaction) = self.ime_transaction {
28128            self.buffer.update(cx, |buffer, cx| {
28129                buffer.group_until_transaction(transaction, cx);
28130            });
28131        }
28132
28133        self.unmark_text(window, cx);
28134    }
28135
28136    fn replace_and_mark_text_in_range(
28137        &mut self,
28138        range_utf16: Option<Range<usize>>,
28139        text: &str,
28140        new_selected_range_utf16: Option<Range<usize>>,
28141        window: &mut Window,
28142        cx: &mut Context<Self>,
28143    ) {
28144        if !self.input_enabled {
28145            return;
28146        }
28147
28148        let transaction = self.transact(window, cx, |this, window, cx| {
28149            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28150                let snapshot = this.buffer.read(cx).read(cx);
28151                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28152                    for marked_range in &mut marked_ranges {
28153                        marked_range.end = marked_range.start + relative_range_utf16.end;
28154                        marked_range.start += relative_range_utf16.start;
28155                        marked_range.start =
28156                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28157                        marked_range.end =
28158                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28159                    }
28160                }
28161                Some(marked_ranges)
28162            } else if let Some(range_utf16) = range_utf16 {
28163                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28164                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28165                Some(this.selection_replacement_ranges(range_utf16, cx))
28166            } else {
28167                None
28168            };
28169
28170            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28171                let newest_selection_id = this.selections.newest_anchor().id;
28172                this.selections
28173                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28174                    .iter()
28175                    .zip(ranges_to_replace.iter())
28176                    .find_map(|(selection, range)| {
28177                        if selection.id == newest_selection_id {
28178                            Some(
28179                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28180                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28181                            )
28182                        } else {
28183                            None
28184                        }
28185                    })
28186            });
28187
28188            cx.emit(EditorEvent::InputHandled {
28189                utf16_range_to_replace: range_to_replace,
28190                text: text.into(),
28191            });
28192
28193            if let Some(ranges) = ranges_to_replace {
28194                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28195                    s.select_ranges(ranges)
28196                });
28197            }
28198
28199            let marked_ranges = {
28200                let snapshot = this.buffer.read(cx).read(cx);
28201                this.selections
28202                    .disjoint_anchors_arc()
28203                    .iter()
28204                    .map(|selection| {
28205                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28206                    })
28207                    .collect::<Vec<_>>()
28208            };
28209
28210            if text.is_empty() {
28211                this.unmark_text(window, cx);
28212            } else {
28213                this.highlight_text(
28214                    HighlightKey::InputComposition,
28215                    marked_ranges.clone(),
28216                    HighlightStyle {
28217                        underline: Some(UnderlineStyle {
28218                            thickness: px(1.),
28219                            color: None,
28220                            wavy: false,
28221                        }),
28222                        ..Default::default()
28223                    },
28224                    cx,
28225                );
28226            }
28227
28228            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28229            let use_autoclose = this.use_autoclose;
28230            let use_auto_surround = this.use_auto_surround;
28231            this.set_use_autoclose(false);
28232            this.set_use_auto_surround(false);
28233            this.handle_input(text, window, cx);
28234            this.set_use_autoclose(use_autoclose);
28235            this.set_use_auto_surround(use_auto_surround);
28236
28237            if let Some(new_selected_range) = new_selected_range_utf16 {
28238                let snapshot = this.buffer.read(cx).read(cx);
28239                let new_selected_ranges = marked_ranges
28240                    .into_iter()
28241                    .map(|marked_range| {
28242                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28243                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28244                            insertion_start.0 + new_selected_range.start,
28245                        ));
28246                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28247                            insertion_start.0 + new_selected_range.end,
28248                        ));
28249                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28250                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28251                    })
28252                    .collect::<Vec<_>>();
28253
28254                drop(snapshot);
28255                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28256                    selections.select_ranges(new_selected_ranges)
28257                });
28258            }
28259        });
28260
28261        self.ime_transaction = self.ime_transaction.or(transaction);
28262        if let Some(transaction) = self.ime_transaction {
28263            self.buffer.update(cx, |buffer, cx| {
28264                buffer.group_until_transaction(transaction, cx);
28265            });
28266        }
28267
28268        if self
28269            .text_highlights(HighlightKey::InputComposition, cx)
28270            .is_none()
28271        {
28272            self.ime_transaction.take();
28273        }
28274    }
28275
28276    fn bounds_for_range(
28277        &mut self,
28278        range_utf16: Range<usize>,
28279        element_bounds: gpui::Bounds<Pixels>,
28280        window: &mut Window,
28281        cx: &mut Context<Self>,
28282    ) -> Option<gpui::Bounds<Pixels>> {
28283        let text_layout_details = self.text_layout_details(window, cx);
28284        let CharacterDimensions {
28285            em_width,
28286            em_advance,
28287            line_height,
28288        } = self.character_dimensions(window, cx);
28289
28290        let snapshot = self.snapshot(window, cx);
28291        let scroll_position = snapshot.scroll_position();
28292        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28293
28294        let start =
28295            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28296        let x = Pixels::from(
28297            ScrollOffset::from(
28298                snapshot.x_for_display_point(start, &text_layout_details)
28299                    + self.gutter_dimensions.full_width(),
28300            ) - scroll_left,
28301        );
28302        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28303
28304        Some(Bounds {
28305            origin: element_bounds.origin + point(x, y),
28306            size: size(em_width, line_height),
28307        })
28308    }
28309
28310    fn character_index_for_point(
28311        &mut self,
28312        point: gpui::Point<Pixels>,
28313        _window: &mut Window,
28314        _cx: &mut Context<Self>,
28315    ) -> Option<usize> {
28316        let position_map = self.last_position_map.as_ref()?;
28317        if !position_map.text_hitbox.contains(&point) {
28318            return None;
28319        }
28320        let display_point = position_map.point_for_position(point).previous_valid;
28321        let anchor = position_map
28322            .snapshot
28323            .display_point_to_anchor(display_point, Bias::Left);
28324        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28325        Some(utf16_offset.0.0)
28326    }
28327
28328    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28329        self.input_enabled
28330    }
28331}
28332
28333trait SelectionExt {
28334    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28335    fn spanned_rows(
28336        &self,
28337        include_end_if_at_line_start: bool,
28338        map: &DisplaySnapshot,
28339    ) -> Range<MultiBufferRow>;
28340}
28341
28342impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28343    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28344        let start = self
28345            .start
28346            .to_point(map.buffer_snapshot())
28347            .to_display_point(map);
28348        let end = self
28349            .end
28350            .to_point(map.buffer_snapshot())
28351            .to_display_point(map);
28352        if self.reversed {
28353            end..start
28354        } else {
28355            start..end
28356        }
28357    }
28358
28359    fn spanned_rows(
28360        &self,
28361        include_end_if_at_line_start: bool,
28362        map: &DisplaySnapshot,
28363    ) -> Range<MultiBufferRow> {
28364        let start = self.start.to_point(map.buffer_snapshot());
28365        let mut end = self.end.to_point(map.buffer_snapshot());
28366        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28367            end.row -= 1;
28368        }
28369
28370        let buffer_start = map.prev_line_boundary(start).0;
28371        let buffer_end = map.next_line_boundary(end).0;
28372        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28373    }
28374}
28375
28376impl<T: InvalidationRegion> InvalidationStack<T> {
28377    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28378    where
28379        S: Clone + ToOffset,
28380    {
28381        while let Some(region) = self.last() {
28382            let all_selections_inside_invalidation_ranges =
28383                if selections.len() == region.ranges().len() {
28384                    selections
28385                        .iter()
28386                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28387                        .all(|(selection, invalidation_range)| {
28388                            let head = selection.head().to_offset(buffer);
28389                            invalidation_range.start <= head && invalidation_range.end >= head
28390                        })
28391                } else {
28392                    false
28393                };
28394
28395            if all_selections_inside_invalidation_ranges {
28396                break;
28397            } else {
28398                self.pop();
28399            }
28400        }
28401    }
28402}
28403
28404#[derive(Clone)]
28405struct ErasedEditorImpl(Entity<Editor>);
28406
28407impl ui_input::ErasedEditor for ErasedEditorImpl {
28408    fn text(&self, cx: &App) -> String {
28409        self.0.read(cx).text(cx)
28410    }
28411
28412    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28413        self.0.update(cx, |this, cx| {
28414            this.set_text(text, window, cx);
28415        })
28416    }
28417
28418    fn clear(&self, window: &mut Window, cx: &mut App) {
28419        self.0.update(cx, |this, cx| this.clear(window, cx));
28420    }
28421
28422    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28423        self.0.update(cx, |this, cx| {
28424            this.set_placeholder_text(text, window, cx);
28425        });
28426    }
28427
28428    fn focus_handle(&self, cx: &App) -> FocusHandle {
28429        self.0.read(cx).focus_handle(cx)
28430    }
28431
28432    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28433        let settings = ThemeSettings::get_global(cx);
28434        let theme_color = cx.theme().colors();
28435
28436        let text_style = TextStyle {
28437            font_family: settings.ui_font.family.clone(),
28438            font_features: settings.ui_font.features.clone(),
28439            font_size: rems(0.875).into(),
28440            font_weight: settings.ui_font.weight,
28441            font_style: FontStyle::Normal,
28442            line_height: relative(1.2),
28443            color: theme_color.text,
28444            ..Default::default()
28445        };
28446        let editor_style = EditorStyle {
28447            background: theme_color.ghost_element_background,
28448            local_player: cx.theme().players().local(),
28449            syntax: cx.theme().syntax().clone(),
28450            text: text_style,
28451            ..Default::default()
28452        };
28453        EditorElement::new(&self.0, editor_style).into_any()
28454    }
28455
28456    fn as_any(&self) -> &dyn Any {
28457        &self.0
28458    }
28459
28460    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28461        self.0.update(cx, |editor, cx| {
28462            let editor_offset = editor.buffer().read(cx).len(cx);
28463            editor.change_selections(
28464                SelectionEffects::scroll(Autoscroll::Next),
28465                window,
28466                cx,
28467                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28468            );
28469        });
28470    }
28471
28472    fn subscribe(
28473        &self,
28474        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28475        window: &mut Window,
28476        cx: &mut App,
28477    ) -> Subscription {
28478        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28479            let event = match event {
28480                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28481                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28482                _ => return,
28483            };
28484            (callback)(event, window, cx);
28485        })
28486    }
28487
28488    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28489        self.0.update(cx, |editor, cx| {
28490            editor.set_masked(masked, cx);
28491        });
28492    }
28493}
28494impl<T> Default for InvalidationStack<T> {
28495    fn default() -> Self {
28496        Self(Default::default())
28497    }
28498}
28499
28500impl<T> Deref for InvalidationStack<T> {
28501    type Target = Vec<T>;
28502
28503    fn deref(&self) -> &Self::Target {
28504        &self.0
28505    }
28506}
28507
28508impl<T> DerefMut for InvalidationStack<T> {
28509    fn deref_mut(&mut self) -> &mut Self::Target {
28510        &mut self.0
28511    }
28512}
28513
28514impl InvalidationRegion for SnippetState {
28515    fn ranges(&self) -> &[Range<Anchor>] {
28516        &self.ranges[self.active_index]
28517    }
28518}
28519
28520fn edit_prediction_edit_text(
28521    current_snapshot: &BufferSnapshot,
28522    edits: &[(Range<Anchor>, impl AsRef<str>)],
28523    edit_preview: &EditPreview,
28524    include_deletions: bool,
28525    cx: &App,
28526) -> HighlightedText {
28527    let edits = edits
28528        .iter()
28529        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28530        .collect::<Vec<_>>();
28531
28532    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28533}
28534
28535fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28536    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
28537    // Just show the raw edit text with basic styling
28538    let mut text = String::new();
28539    let mut highlights = Vec::new();
28540
28541    let insertion_highlight_style = HighlightStyle {
28542        color: Some(cx.theme().colors().text),
28543        ..Default::default()
28544    };
28545
28546    for (_, edit_text) in edits {
28547        let start_offset = text.len();
28548        text.push_str(edit_text);
28549        let end_offset = text.len();
28550
28551        if start_offset < end_offset {
28552            highlights.push((start_offset..end_offset, insertion_highlight_style));
28553        }
28554    }
28555
28556    HighlightedText {
28557        text: text.into(),
28558        highlights,
28559    }
28560}
28561
28562pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28563    match severity {
28564        lsp::DiagnosticSeverity::ERROR => colors.error,
28565        lsp::DiagnosticSeverity::WARNING => colors.warning,
28566        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28567        lsp::DiagnosticSeverity::HINT => colors.info,
28568        _ => colors.ignored,
28569    }
28570}
28571
28572pub fn styled_runs_for_code_label<'a>(
28573    label: &'a CodeLabel,
28574    syntax_theme: &'a theme::SyntaxTheme,
28575    local_player: &'a theme::PlayerColor,
28576) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28577    let fade_out = HighlightStyle {
28578        fade_out: Some(0.35),
28579        ..Default::default()
28580    };
28581
28582    let mut prev_end = label.filter_range.end;
28583    label
28584        .runs
28585        .iter()
28586        .enumerate()
28587        .flat_map(move |(ix, (range, highlight_id))| {
28588            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28589                HighlightStyle {
28590                    color: Some(local_player.cursor),
28591                    ..Default::default()
28592                }
28593            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28594                HighlightStyle {
28595                    background_color: Some(local_player.selection),
28596                    ..Default::default()
28597                }
28598            } else if let Some(style) = highlight_id.style(syntax_theme) {
28599                style
28600            } else {
28601                return Default::default();
28602            };
28603            let muted_style = style.highlight(fade_out);
28604
28605            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28606            if range.start >= label.filter_range.end {
28607                if range.start > prev_end {
28608                    runs.push((prev_end..range.start, fade_out));
28609                }
28610                runs.push((range.clone(), muted_style));
28611            } else if range.end <= label.filter_range.end {
28612                runs.push((range.clone(), style));
28613            } else {
28614                runs.push((range.start..label.filter_range.end, style));
28615                runs.push((label.filter_range.end..range.end, muted_style));
28616            }
28617            prev_end = cmp::max(prev_end, range.end);
28618
28619            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28620                runs.push((prev_end..label.text.len(), fade_out));
28621            }
28622
28623            runs
28624        })
28625}
28626
28627pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28628    let mut prev_index = 0;
28629    let mut prev_codepoint: Option<char> = None;
28630    text.char_indices()
28631        .chain([(text.len(), '\0')])
28632        .filter_map(move |(index, codepoint)| {
28633            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28634            let is_boundary = index == text.len()
28635                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28636                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28637            if is_boundary {
28638                let chunk = &text[prev_index..index];
28639                prev_index = index;
28640                Some(chunk)
28641            } else {
28642                None
28643            }
28644        })
28645}
28646
28647/// Given a string of text immediately before the cursor, iterates over possible
28648/// strings a snippet could match to. More precisely: returns an iterator over
28649/// suffixes of `text` created by splitting at word boundaries (before & after
28650/// every non-word character).
28651///
28652/// Shorter suffixes are returned first.
28653pub(crate) fn snippet_candidate_suffixes<'a>(
28654    text: &'a str,
28655    is_word_char: &'a dyn Fn(char) -> bool,
28656) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28657    let mut prev_index = text.len();
28658    let mut prev_codepoint = None;
28659    text.char_indices()
28660        .rev()
28661        .chain([(0, '\0')])
28662        .filter_map(move |(index, codepoint)| {
28663            let prev_index = std::mem::replace(&mut prev_index, index);
28664            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28665            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28666                None
28667            } else {
28668                let chunk = &text[prev_index..]; // go to end of string
28669                Some(chunk)
28670            }
28671        })
28672}
28673
28674pub trait RangeToAnchorExt: Sized {
28675    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28676
28677    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28678        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28679        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28680    }
28681}
28682
28683impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28684    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28685        let start_offset = self.start.to_offset(snapshot);
28686        let end_offset = self.end.to_offset(snapshot);
28687        if start_offset == end_offset {
28688            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28689        } else {
28690            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28691        }
28692    }
28693}
28694
28695pub trait RowExt {
28696    fn as_f64(&self) -> f64;
28697
28698    fn next_row(&self) -> Self;
28699
28700    fn previous_row(&self) -> Self;
28701
28702    fn minus(&self, other: Self) -> u32;
28703}
28704
28705impl RowExt for DisplayRow {
28706    fn as_f64(&self) -> f64 {
28707        self.0 as _
28708    }
28709
28710    fn next_row(&self) -> Self {
28711        Self(self.0 + 1)
28712    }
28713
28714    fn previous_row(&self) -> Self {
28715        Self(self.0.saturating_sub(1))
28716    }
28717
28718    fn minus(&self, other: Self) -> u32 {
28719        self.0 - other.0
28720    }
28721}
28722
28723impl RowExt for MultiBufferRow {
28724    fn as_f64(&self) -> f64 {
28725        self.0 as _
28726    }
28727
28728    fn next_row(&self) -> Self {
28729        Self(self.0 + 1)
28730    }
28731
28732    fn previous_row(&self) -> Self {
28733        Self(self.0.saturating_sub(1))
28734    }
28735
28736    fn minus(&self, other: Self) -> u32 {
28737        self.0 - other.0
28738    }
28739}
28740
28741trait RowRangeExt {
28742    type Row;
28743
28744    fn len(&self) -> usize;
28745
28746    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28747}
28748
28749impl RowRangeExt for Range<MultiBufferRow> {
28750    type Row = MultiBufferRow;
28751
28752    fn len(&self) -> usize {
28753        (self.end.0 - self.start.0) as usize
28754    }
28755
28756    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28757        (self.start.0..self.end.0).map(MultiBufferRow)
28758    }
28759}
28760
28761impl RowRangeExt for Range<DisplayRow> {
28762    type Row = DisplayRow;
28763
28764    fn len(&self) -> usize {
28765        (self.end.0 - self.start.0) as usize
28766    }
28767
28768    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28769        (self.start.0..self.end.0).map(DisplayRow)
28770    }
28771}
28772
28773/// If select range has more than one line, we
28774/// just point the cursor to range.start.
28775fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28776    if range.start.row == range.end.row {
28777        range
28778    } else {
28779        range.start..range.start
28780    }
28781}
28782pub struct KillRing(ClipboardItem);
28783impl Global for KillRing {}
28784
28785const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28786
28787enum BreakpointPromptEditAction {
28788    Log,
28789    Condition,
28790    HitCondition,
28791}
28792
28793struct BreakpointPromptEditor {
28794    pub(crate) prompt: Entity<Editor>,
28795    editor: WeakEntity<Editor>,
28796    breakpoint_anchor: Anchor,
28797    breakpoint: Breakpoint,
28798    edit_action: BreakpointPromptEditAction,
28799    block_ids: HashSet<CustomBlockId>,
28800    editor_margins: Arc<Mutex<EditorMargins>>,
28801    _subscriptions: Vec<Subscription>,
28802}
28803
28804impl BreakpointPromptEditor {
28805    const MAX_LINES: u8 = 4;
28806
28807    fn new(
28808        editor: WeakEntity<Editor>,
28809        breakpoint_anchor: Anchor,
28810        breakpoint: Breakpoint,
28811        edit_action: BreakpointPromptEditAction,
28812        window: &mut Window,
28813        cx: &mut Context<Self>,
28814    ) -> Self {
28815        let base_text = match edit_action {
28816            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28817            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28818            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28819        }
28820        .map(|msg| msg.to_string())
28821        .unwrap_or_default();
28822
28823        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28824        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28825
28826        let prompt = cx.new(|cx| {
28827            let mut prompt = Editor::new(
28828                EditorMode::AutoHeight {
28829                    min_lines: 1,
28830                    max_lines: Some(Self::MAX_LINES as usize),
28831                },
28832                buffer,
28833                None,
28834                window,
28835                cx,
28836            );
28837            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28838            prompt.set_show_cursor_when_unfocused(false, cx);
28839            prompt.set_placeholder_text(
28840                match edit_action {
28841                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28842                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28843                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28844                },
28845                window,
28846                cx,
28847            );
28848
28849            prompt
28850        });
28851
28852        Self {
28853            prompt,
28854            editor,
28855            breakpoint_anchor,
28856            breakpoint,
28857            edit_action,
28858            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28859            block_ids: Default::default(),
28860            _subscriptions: vec![],
28861        }
28862    }
28863
28864    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28865        self.block_ids.extend(block_ids)
28866    }
28867
28868    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28869        if let Some(editor) = self.editor.upgrade() {
28870            let message = self
28871                .prompt
28872                .read(cx)
28873                .buffer
28874                .read(cx)
28875                .as_singleton()
28876                .expect("A multi buffer in breakpoint prompt isn't possible")
28877                .read(cx)
28878                .as_rope()
28879                .to_string();
28880
28881            editor.update(cx, |editor, cx| {
28882                editor.edit_breakpoint_at_anchor(
28883                    self.breakpoint_anchor,
28884                    self.breakpoint.clone(),
28885                    match self.edit_action {
28886                        BreakpointPromptEditAction::Log => {
28887                            BreakpointEditAction::EditLogMessage(message.into())
28888                        }
28889                        BreakpointPromptEditAction::Condition => {
28890                            BreakpointEditAction::EditCondition(message.into())
28891                        }
28892                        BreakpointPromptEditAction::HitCondition => {
28893                            BreakpointEditAction::EditHitCondition(message.into())
28894                        }
28895                    },
28896                    cx,
28897                );
28898
28899                editor.remove_blocks(self.block_ids.clone(), None, cx);
28900                cx.focus_self(window);
28901            });
28902        }
28903    }
28904
28905    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28906        self.editor
28907            .update(cx, |editor, cx| {
28908                editor.remove_blocks(self.block_ids.clone(), None, cx);
28909                window.focus(&editor.focus_handle, cx);
28910            })
28911            .log_err();
28912    }
28913
28914    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28915        let settings = ThemeSettings::get_global(cx);
28916        let text_style = TextStyle {
28917            color: if self.prompt.read(cx).read_only(cx) {
28918                cx.theme().colors().text_disabled
28919            } else {
28920                cx.theme().colors().text
28921            },
28922            font_family: settings.buffer_font.family.clone(),
28923            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28924            font_size: settings.buffer_font_size(cx).into(),
28925            font_weight: settings.buffer_font.weight,
28926            line_height: relative(settings.buffer_line_height.value()),
28927            ..Default::default()
28928        };
28929        EditorElement::new(
28930            &self.prompt,
28931            EditorStyle {
28932                background: cx.theme().colors().editor_background,
28933                local_player: cx.theme().players().local(),
28934                text: text_style,
28935                ..Default::default()
28936            },
28937        )
28938    }
28939}
28940
28941impl Render for BreakpointPromptEditor {
28942    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28943        let editor_margins = *self.editor_margins.lock();
28944        let gutter_dimensions = editor_margins.gutter;
28945        h_flex()
28946            .key_context("Editor")
28947            .bg(cx.theme().colors().editor_background)
28948            .border_y_1()
28949            .border_color(cx.theme().status().info_border)
28950            .size_full()
28951            .py(window.line_height() / 2.5)
28952            .on_action(cx.listener(Self::confirm))
28953            .on_action(cx.listener(Self::cancel))
28954            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28955            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28956    }
28957}
28958
28959impl Focusable for BreakpointPromptEditor {
28960    fn focus_handle(&self, cx: &App) -> FocusHandle {
28961        self.prompt.focus_handle(cx)
28962    }
28963}
28964
28965fn all_edits_insertions_or_deletions(
28966    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28967    snapshot: &MultiBufferSnapshot,
28968) -> bool {
28969    let mut all_insertions = true;
28970    let mut all_deletions = true;
28971
28972    for (range, new_text) in edits.iter() {
28973        let range_is_empty = range.to_offset(snapshot).is_empty();
28974        let text_is_empty = new_text.is_empty();
28975
28976        if range_is_empty != text_is_empty {
28977            if range_is_empty {
28978                all_deletions = false;
28979            } else {
28980                all_insertions = false;
28981            }
28982        } else {
28983            return false;
28984        }
28985
28986        if !all_insertions && !all_deletions {
28987            return false;
28988        }
28989    }
28990    all_insertions || all_deletions
28991}
28992
28993struct MissingEditPredictionKeybindingTooltip;
28994
28995impl Render for MissingEditPredictionKeybindingTooltip {
28996    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28997        ui::tooltip_container(cx, |container, cx| {
28998            container
28999                .flex_shrink_0()
29000                .max_w_80()
29001                .min_h(rems_from_px(124.))
29002                .justify_between()
29003                .child(
29004                    v_flex()
29005                        .flex_1()
29006                        .text_ui_sm(cx)
29007                        .child(Label::new("Conflict with Accept Keybinding"))
29008                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29009                )
29010                .child(
29011                    h_flex()
29012                        .pb_1()
29013                        .gap_1()
29014                        .items_end()
29015                        .w_full()
29016                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29017                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29018                        }))
29019                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29020                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29021                        })),
29022                )
29023        })
29024    }
29025}
29026
29027#[derive(Debug, Clone, Copy, PartialEq)]
29028pub struct LineHighlight {
29029    pub background: Background,
29030    pub border: Option<gpui::Hsla>,
29031    pub include_gutter: bool,
29032    pub type_id: Option<TypeId>,
29033}
29034
29035struct LineManipulationResult {
29036    pub new_text: String,
29037    pub line_count_before: usize,
29038    pub line_count_after: usize,
29039}
29040
29041fn render_diff_hunk_controls(
29042    row: u32,
29043    status: &DiffHunkStatus,
29044    hunk_range: Range<Anchor>,
29045    is_created_file: bool,
29046    line_height: Pixels,
29047    editor: &Entity<Editor>,
29048    _window: &mut Window,
29049    cx: &mut App,
29050) -> AnyElement {
29051    h_flex()
29052        .h(line_height)
29053        .mr_1()
29054        .gap_1()
29055        .px_0p5()
29056        .pb_1()
29057        .border_x_1()
29058        .border_b_1()
29059        .border_color(cx.theme().colors().border_variant)
29060        .rounded_b_lg()
29061        .bg(cx.theme().colors().editor_background)
29062        .gap_1()
29063        .block_mouse_except_scroll()
29064        .shadow_md()
29065        .child(if status.has_secondary_hunk() {
29066            Button::new(("stage", row as u64), "Stage")
29067                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29068                .tooltip({
29069                    let focus_handle = editor.focus_handle(cx);
29070                    move |_window, cx| {
29071                        Tooltip::for_action_in(
29072                            "Stage Hunk",
29073                            &::git::ToggleStaged,
29074                            &focus_handle,
29075                            cx,
29076                        )
29077                    }
29078                })
29079                .on_click({
29080                    let editor = editor.clone();
29081                    move |_event, _window, cx| {
29082                        editor.update(cx, |editor, cx| {
29083                            editor.stage_or_unstage_diff_hunks(
29084                                true,
29085                                vec![hunk_range.start..hunk_range.start],
29086                                cx,
29087                            );
29088                        });
29089                    }
29090                })
29091        } else {
29092            Button::new(("unstage", row as u64), "Unstage")
29093                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29094                .tooltip({
29095                    let focus_handle = editor.focus_handle(cx);
29096                    move |_window, cx| {
29097                        Tooltip::for_action_in(
29098                            "Unstage Hunk",
29099                            &::git::ToggleStaged,
29100                            &focus_handle,
29101                            cx,
29102                        )
29103                    }
29104                })
29105                .on_click({
29106                    let editor = editor.clone();
29107                    move |_event, _window, cx| {
29108                        editor.update(cx, |editor, cx| {
29109                            editor.stage_or_unstage_diff_hunks(
29110                                false,
29111                                vec![hunk_range.start..hunk_range.start],
29112                                cx,
29113                            );
29114                        });
29115                    }
29116                })
29117        })
29118        .child(
29119            Button::new(("restore", row as u64), "Restore")
29120                .tooltip({
29121                    let focus_handle = editor.focus_handle(cx);
29122                    move |_window, cx| {
29123                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29124                    }
29125                })
29126                .on_click({
29127                    let editor = editor.clone();
29128                    move |_event, window, cx| {
29129                        editor.update(cx, |editor, cx| {
29130                            let snapshot = editor.snapshot(window, cx);
29131                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29132                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29133                        });
29134                    }
29135                })
29136                .disabled(is_created_file),
29137        )
29138        .when(
29139            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29140            |el| {
29141                el.child(
29142                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29143                        .shape(IconButtonShape::Square)
29144                        .icon_size(IconSize::Small)
29145                        // .disabled(!has_multiple_hunks)
29146                        .tooltip({
29147                            let focus_handle = editor.focus_handle(cx);
29148                            move |_window, cx| {
29149                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29150                            }
29151                        })
29152                        .on_click({
29153                            let editor = editor.clone();
29154                            move |_event, window, cx| {
29155                                editor.update(cx, |editor, cx| {
29156                                    let snapshot = editor.snapshot(window, cx);
29157                                    let position =
29158                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29159                                    editor.go_to_hunk_before_or_after_position(
29160                                        &snapshot,
29161                                        position,
29162                                        Direction::Next,
29163                                        window,
29164                                        cx,
29165                                    );
29166                                    editor.expand_selected_diff_hunks(cx);
29167                                });
29168                            }
29169                        }),
29170                )
29171                .child(
29172                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29173                        .shape(IconButtonShape::Square)
29174                        .icon_size(IconSize::Small)
29175                        // .disabled(!has_multiple_hunks)
29176                        .tooltip({
29177                            let focus_handle = editor.focus_handle(cx);
29178                            move |_window, cx| {
29179                                Tooltip::for_action_in(
29180                                    "Previous Hunk",
29181                                    &GoToPreviousHunk,
29182                                    &focus_handle,
29183                                    cx,
29184                                )
29185                            }
29186                        })
29187                        .on_click({
29188                            let editor = editor.clone();
29189                            move |_event, window, cx| {
29190                                editor.update(cx, |editor, cx| {
29191                                    let snapshot = editor.snapshot(window, cx);
29192                                    let point =
29193                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29194                                    editor.go_to_hunk_before_or_after_position(
29195                                        &snapshot,
29196                                        point,
29197                                        Direction::Prev,
29198                                        window,
29199                                        cx,
29200                                    );
29201                                    editor.expand_selected_diff_hunks(cx);
29202                                });
29203                            }
29204                        }),
29205                )
29206            },
29207        )
29208        .into_any_element()
29209}
29210
29211pub fn multibuffer_context_lines(cx: &App) -> u32 {
29212    EditorSettings::try_get(cx)
29213        .map(|settings| settings.excerpt_context_lines)
29214        .unwrap_or(2)
29215        .min(32)
29216}