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 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  241const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  242const MAX_LINE_LEN: usize = 1024;
  243const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  244const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  245pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  246#[doc(hidden)]
  247pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  248pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  249
  250pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  251pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  252pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  253pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  254
  255pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  256pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  257pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  258
  259pub type RenderDiffHunkControlsFn = Arc<
  260    dyn Fn(
  261        u32,
  262        &DiffHunkStatus,
  263        Range<Anchor>,
  264        bool,
  265        Pixels,
  266        &Entity<Editor>,
  267        &mut Window,
  268        &mut App,
  269    ) -> AnyElement,
  270>;
  271
  272enum ReportEditorEvent {
  273    Saved { auto_saved: bool },
  274    EditorOpened,
  275    Closed,
  276}
  277
  278impl ReportEditorEvent {
  279    pub fn event_type(&self) -> &'static str {
  280        match self {
  281            Self::Saved { .. } => "Editor Saved",
  282            Self::EditorOpened => "Editor Opened",
  283            Self::Closed => "Editor Closed",
  284        }
  285    }
  286}
  287
  288pub enum ActiveDebugLine {}
  289pub enum DebugStackFrameLine {}
  290
  291pub enum ConflictsOuter {}
  292pub enum ConflictsOurs {}
  293pub enum ConflictsTheirs {}
  294pub enum ConflictsOursMarker {}
  295pub enum ConflictsTheirsMarker {}
  296
  297pub struct HunkAddedColor;
  298pub struct HunkRemovedColor;
  299
  300#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  301pub enum Navigated {
  302    Yes,
  303    No,
  304}
  305
  306impl Navigated {
  307    pub fn from_bool(yes: bool) -> Navigated {
  308        if yes { Navigated::Yes } else { Navigated::No }
  309    }
  310}
  311
  312#[derive(Debug, Clone, PartialEq, Eq)]
  313enum DisplayDiffHunk {
  314    Folded {
  315        display_row: DisplayRow,
  316    },
  317    Unfolded {
  318        is_created_file: bool,
  319        diff_base_byte_range: Range<usize>,
  320        display_row_range: Range<DisplayRow>,
  321        multi_buffer_range: Range<Anchor>,
  322        status: DiffHunkStatus,
  323        word_diffs: Vec<Range<MultiBufferOffset>>,
  324    },
  325}
  326
  327pub enum HideMouseCursorOrigin {
  328    TypingAction,
  329    MovementAction,
  330}
  331
  332pub fn init(cx: &mut App) {
  333    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  334    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  335
  336    workspace::register_project_item::<Editor>(cx);
  337    workspace::FollowableViewRegistry::register::<Editor>(cx);
  338    workspace::register_serializable_item::<Editor>(cx);
  339
  340    cx.observe_new(
  341        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  342            workspace.register_action(Editor::new_file);
  343            workspace.register_action(Editor::new_file_split);
  344            workspace.register_action(Editor::new_file_vertical);
  345            workspace.register_action(Editor::new_file_horizontal);
  346            workspace.register_action(Editor::cancel_language_server_work);
  347            workspace.register_action(Editor::toggle_focus);
  348        },
  349    )
  350    .detach();
  351
  352    cx.on_action(move |_: &workspace::NewFile, cx| {
  353        let app_state = workspace::AppState::global(cx);
  354        if let Some(app_state) = app_state.upgrade() {
  355            workspace::open_new(
  356                Default::default(),
  357                app_state,
  358                cx,
  359                |workspace, window, cx| {
  360                    Editor::new_file(workspace, &Default::default(), window, cx)
  361                },
  362            )
  363            .detach_and_log_err(cx);
  364        }
  365    })
  366    .on_action(move |_: &workspace::NewWindow, cx| {
  367        let app_state = workspace::AppState::global(cx);
  368        if let Some(app_state) = app_state.upgrade() {
  369            workspace::open_new(
  370                Default::default(),
  371                app_state,
  372                cx,
  373                |workspace, window, cx| {
  374                    cx.activate(true);
  375                    Editor::new_file(workspace, &Default::default(), window, cx)
  376                },
  377            )
  378            .detach_and_log_err(cx);
  379        }
  380    });
  381    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  382        Arc::new(ErasedEditorImpl(
  383            cx.new(|cx| Editor::single_line(window, cx)),
  384        )) as Arc<dyn ErasedEditor>
  385    });
  386    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  387}
  388
  389pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  390    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  391}
  392
  393pub trait DiagnosticRenderer {
  394    fn render_group(
  395        &self,
  396        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  397        buffer_id: BufferId,
  398        snapshot: EditorSnapshot,
  399        editor: WeakEntity<Editor>,
  400        language_registry: Option<Arc<LanguageRegistry>>,
  401        cx: &mut App,
  402    ) -> Vec<BlockProperties<Anchor>>;
  403
  404    fn render_hover(
  405        &self,
  406        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  407        range: Range<Point>,
  408        buffer_id: BufferId,
  409        language_registry: Option<Arc<LanguageRegistry>>,
  410        cx: &mut App,
  411    ) -> Option<Entity<markdown::Markdown>>;
  412
  413    fn open_link(
  414        &self,
  415        editor: &mut Editor,
  416        link: SharedString,
  417        window: &mut Window,
  418        cx: &mut Context<Editor>,
  419    );
  420}
  421
  422pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  423
  424impl GlobalDiagnosticRenderer {
  425    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  426        cx.try_global::<Self>().map(|g| g.0.clone())
  427    }
  428}
  429
  430impl gpui::Global for GlobalDiagnosticRenderer {}
  431pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  432    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  433}
  434
  435pub struct SearchWithinRange;
  436
  437trait InvalidationRegion {
  438    fn ranges(&self) -> &[Range<Anchor>];
  439}
  440
  441#[derive(Clone, Debug, PartialEq)]
  442pub enum SelectPhase {
  443    Begin {
  444        position: DisplayPoint,
  445        add: bool,
  446        click_count: usize,
  447    },
  448    BeginColumnar {
  449        position: DisplayPoint,
  450        reset: bool,
  451        mode: ColumnarMode,
  452        goal_column: u32,
  453    },
  454    Extend {
  455        position: DisplayPoint,
  456        click_count: usize,
  457    },
  458    Update {
  459        position: DisplayPoint,
  460        goal_column: u32,
  461        scroll_delta: gpui::Point<f32>,
  462    },
  463    End,
  464}
  465
  466#[derive(Clone, Debug, PartialEq)]
  467pub enum ColumnarMode {
  468    FromMouse,
  469    FromSelection,
  470}
  471
  472#[derive(Clone, Debug)]
  473pub enum SelectMode {
  474    Character,
  475    Word(Range<Anchor>),
  476    Line(Range<Anchor>),
  477    All,
  478}
  479
  480#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  481pub enum SizingBehavior {
  482    /// The editor will layout itself using `size_full` and will include the vertical
  483    /// scroll margin as requested by user settings.
  484    #[default]
  485    Default,
  486    /// The editor will layout itself using `size_full`, but will not have any
  487    /// vertical overscroll.
  488    ExcludeOverscrollMargin,
  489    /// The editor will request a vertical size according to its content and will be
  490    /// layouted without a vertical scroll margin.
  491    SizeByContent,
  492}
  493
  494#[derive(Clone, PartialEq, Eq, Debug)]
  495pub enum EditorMode {
  496    SingleLine,
  497    AutoHeight {
  498        min_lines: usize,
  499        max_lines: Option<usize>,
  500    },
  501    Full {
  502        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  503        scale_ui_elements_with_buffer_font_size: bool,
  504        /// When set to `true`, the editor will render a background for the active line.
  505        show_active_line_background: bool,
  506        /// Determines the sizing behavior for this editor
  507        sizing_behavior: SizingBehavior,
  508    },
  509    Minimap {
  510        parent: WeakEntity<Editor>,
  511    },
  512}
  513
  514impl EditorMode {
  515    pub fn full() -> Self {
  516        Self::Full {
  517            scale_ui_elements_with_buffer_font_size: true,
  518            show_active_line_background: true,
  519            sizing_behavior: SizingBehavior::Default,
  520        }
  521    }
  522
  523    #[inline]
  524    pub fn is_full(&self) -> bool {
  525        matches!(self, Self::Full { .. })
  526    }
  527
  528    #[inline]
  529    pub fn is_single_line(&self) -> bool {
  530        matches!(self, Self::SingleLine { .. })
  531    }
  532
  533    #[inline]
  534    fn is_minimap(&self) -> bool {
  535        matches!(self, Self::Minimap { .. })
  536    }
  537}
  538
  539#[derive(Copy, Clone, Debug)]
  540pub enum SoftWrap {
  541    /// Prefer not to wrap at all.
  542    ///
  543    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  544    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  545    GitDiff,
  546    /// Prefer a single line generally, unless an overly long line is encountered.
  547    None,
  548    /// Soft wrap lines that exceed the editor width.
  549    EditorWidth,
  550    /// Soft wrap lines at the preferred line length.
  551    Column(u32),
  552    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  553    Bounded(u32),
  554}
  555
  556#[derive(Clone)]
  557pub struct EditorStyle {
  558    pub background: Hsla,
  559    pub border: Hsla,
  560    pub local_player: PlayerColor,
  561    pub text: TextStyle,
  562    pub scrollbar_width: Pixels,
  563    pub syntax: Arc<SyntaxTheme>,
  564    pub status: StatusColors,
  565    pub inlay_hints_style: HighlightStyle,
  566    pub edit_prediction_styles: EditPredictionStyles,
  567    pub unnecessary_code_fade: f32,
  568    pub show_underlines: bool,
  569}
  570
  571impl Default for EditorStyle {
  572    fn default() -> Self {
  573        Self {
  574            background: Hsla::default(),
  575            border: Hsla::default(),
  576            local_player: PlayerColor::default(),
  577            text: TextStyle::default(),
  578            scrollbar_width: Pixels::default(),
  579            syntax: Default::default(),
  580            // HACK: Status colors don't have a real default.
  581            // We should look into removing the status colors from the editor
  582            // style and retrieve them directly from the theme.
  583            status: StatusColors::dark(),
  584            inlay_hints_style: HighlightStyle::default(),
  585            edit_prediction_styles: EditPredictionStyles {
  586                insertion: HighlightStyle::default(),
  587                whitespace: HighlightStyle::default(),
  588            },
  589            unnecessary_code_fade: Default::default(),
  590            show_underlines: true,
  591        }
  592    }
  593}
  594
  595pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  596    let show_background = language_settings::language_settings(None, None, cx)
  597        .inlay_hints
  598        .show_background;
  599
  600    let mut style = cx.theme().syntax().get("hint");
  601
  602    if style.color.is_none() {
  603        style.color = Some(cx.theme().status().hint);
  604    }
  605
  606    if !show_background {
  607        style.background_color = None;
  608        return style;
  609    }
  610
  611    if style.background_color.is_none() {
  612        style.background_color = Some(cx.theme().status().hint_background);
  613    }
  614
  615    style
  616}
  617
  618pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  619    EditPredictionStyles {
  620        insertion: HighlightStyle {
  621            color: Some(cx.theme().status().predictive),
  622            ..HighlightStyle::default()
  623        },
  624        whitespace: HighlightStyle {
  625            background_color: Some(cx.theme().status().created_background),
  626            ..HighlightStyle::default()
  627        },
  628    }
  629}
  630
  631type CompletionId = usize;
  632
  633pub(crate) enum EditDisplayMode {
  634    TabAccept,
  635    DiffPopover,
  636    Inline,
  637}
  638
  639enum EditPrediction {
  640    Edit {
  641        edits: Vec<(Range<Anchor>, Arc<str>)>,
  642        /// Predicted cursor position as (anchor, offset_from_anchor).
  643        /// The anchor is in multibuffer coordinates; after applying edits,
  644        /// resolve the anchor and add the offset to get the final cursor position.
  645        cursor_position: Option<(Anchor, usize)>,
  646        edit_preview: Option<EditPreview>,
  647        display_mode: EditDisplayMode,
  648        snapshot: BufferSnapshot,
  649    },
  650    /// Move to a specific location in the active editor
  651    MoveWithin {
  652        target: Anchor,
  653        snapshot: BufferSnapshot,
  654    },
  655    /// Move to a specific location in a different editor (not the active one)
  656    MoveOutside {
  657        target: language::Anchor,
  658        snapshot: BufferSnapshot,
  659    },
  660}
  661
  662struct EditPredictionState {
  663    inlay_ids: Vec<InlayId>,
  664    completion: EditPrediction,
  665    completion_id: Option<SharedString>,
  666    invalidation_range: Option<Range<Anchor>>,
  667}
  668
  669enum EditPredictionSettings {
  670    Disabled,
  671    Enabled {
  672        show_in_menu: bool,
  673        preview_requires_modifier: bool,
  674    },
  675}
  676
  677#[derive(Debug, Clone)]
  678struct InlineDiagnostic {
  679    message: SharedString,
  680    group_id: usize,
  681    is_primary: bool,
  682    start: Point,
  683    severity: lsp::DiagnosticSeverity,
  684}
  685
  686pub enum MenuEditPredictionsPolicy {
  687    Never,
  688    ByProvider,
  689}
  690
  691pub enum EditPredictionPreview {
  692    /// Modifier is not pressed
  693    Inactive { released_too_fast: bool },
  694    /// Modifier pressed
  695    Active {
  696        since: Instant,
  697        previous_scroll_position: Option<SharedScrollAnchor>,
  698    },
  699}
  700
  701impl EditPredictionPreview {
  702    pub fn released_too_fast(&self) -> bool {
  703        match self {
  704            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  705            EditPredictionPreview::Active { .. } => false,
  706        }
  707    }
  708
  709    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  710        if let EditPredictionPreview::Active {
  711            previous_scroll_position,
  712            ..
  713        } = self
  714        {
  715            *previous_scroll_position = scroll_position;
  716        }
  717    }
  718}
  719
  720pub struct ContextMenuOptions {
  721    pub min_entries_visible: usize,
  722    pub max_entries_visible: usize,
  723    pub placement: Option<ContextMenuPlacement>,
  724}
  725
  726#[derive(Debug, Clone, PartialEq, Eq)]
  727pub enum ContextMenuPlacement {
  728    Above,
  729    Below,
  730}
  731
  732#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  733struct EditorActionId(usize);
  734
  735impl EditorActionId {
  736    pub fn post_inc(&mut self) -> Self {
  737        let answer = self.0;
  738
  739        *self = Self(answer + 1);
  740
  741        Self(answer)
  742    }
  743}
  744
  745// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  746// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  747
  748type BackgroundHighlight = (
  749    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  750    Arc<[Range<Anchor>]>,
  751);
  752type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  753
  754#[derive(Default)]
  755struct ScrollbarMarkerState {
  756    scrollbar_size: Size<Pixels>,
  757    dirty: bool,
  758    markers: Arc<[PaintQuad]>,
  759    pending_refresh: Option<Task<Result<()>>>,
  760}
  761
  762impl ScrollbarMarkerState {
  763    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  764        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  765    }
  766}
  767
  768#[derive(Clone, Copy, PartialEq, Eq)]
  769pub enum MinimapVisibility {
  770    Disabled,
  771    Enabled {
  772        /// The configuration currently present in the users settings.
  773        setting_configuration: bool,
  774        /// Whether to override the currently set visibility from the users setting.
  775        toggle_override: bool,
  776    },
  777}
  778
  779impl MinimapVisibility {
  780    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  781        if mode.is_full() {
  782            Self::Enabled {
  783                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  784                toggle_override: false,
  785            }
  786        } else {
  787            Self::Disabled
  788        }
  789    }
  790
  791    fn hidden(&self) -> Self {
  792        match *self {
  793            Self::Enabled {
  794                setting_configuration,
  795                ..
  796            } => Self::Enabled {
  797                setting_configuration,
  798                toggle_override: setting_configuration,
  799            },
  800            Self::Disabled => Self::Disabled,
  801        }
  802    }
  803
  804    fn disabled(&self) -> bool {
  805        matches!(*self, Self::Disabled)
  806    }
  807
  808    fn settings_visibility(&self) -> bool {
  809        match *self {
  810            Self::Enabled {
  811                setting_configuration,
  812                ..
  813            } => setting_configuration,
  814            _ => false,
  815        }
  816    }
  817
  818    fn visible(&self) -> bool {
  819        match *self {
  820            Self::Enabled {
  821                setting_configuration,
  822                toggle_override,
  823            } => setting_configuration ^ toggle_override,
  824            _ => false,
  825        }
  826    }
  827
  828    fn toggle_visibility(&self) -> Self {
  829        match *self {
  830            Self::Enabled {
  831                toggle_override,
  832                setting_configuration,
  833            } => Self::Enabled {
  834                setting_configuration,
  835                toggle_override: !toggle_override,
  836            },
  837            Self::Disabled => Self::Disabled,
  838        }
  839    }
  840}
  841
  842#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  843pub enum BufferSerialization {
  844    All,
  845    NonDirtyBuffers,
  846}
  847
  848impl BufferSerialization {
  849    fn new(restore_unsaved_buffers: bool) -> Self {
  850        if restore_unsaved_buffers {
  851            Self::All
  852        } else {
  853            Self::NonDirtyBuffers
  854        }
  855    }
  856}
  857
  858#[derive(Clone, Debug)]
  859struct RunnableTasks {
  860    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  861    offset: multi_buffer::Anchor,
  862    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  863    column: u32,
  864    // Values of all named captures, including those starting with '_'
  865    extra_variables: HashMap<String, String>,
  866    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  867    context_range: Range<BufferOffset>,
  868}
  869
  870impl RunnableTasks {
  871    fn resolve<'a>(
  872        &'a self,
  873        cx: &'a task::TaskContext,
  874    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  875        self.templates.iter().filter_map(|(kind, template)| {
  876            template
  877                .resolve_task(&kind.to_id_base(), cx)
  878                .map(|task| (kind.clone(), task))
  879        })
  880    }
  881}
  882
  883#[derive(Clone)]
  884pub struct ResolvedTasks {
  885    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  886    position: Anchor,
  887}
  888
  889/// Addons allow storing per-editor state in other crates (e.g. Vim)
  890pub trait Addon: 'static {
  891    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  892
  893    fn render_buffer_header_controls(
  894        &self,
  895        _: &ExcerptInfo,
  896        _: &Window,
  897        _: &App,
  898    ) -> Option<AnyElement> {
  899        None
  900    }
  901
  902    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  903        None
  904    }
  905
  906    fn to_any(&self) -> &dyn std::any::Any;
  907
  908    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  909        None
  910    }
  911}
  912
  913struct ChangeLocation {
  914    current: Option<Vec<Anchor>>,
  915    original: Vec<Anchor>,
  916}
  917impl ChangeLocation {
  918    fn locations(&self) -> &[Anchor] {
  919        self.current.as_ref().unwrap_or(&self.original)
  920    }
  921}
  922
  923/// A set of caret positions, registered when the editor was edited.
  924pub struct ChangeList {
  925    changes: Vec<ChangeLocation>,
  926    /// Currently "selected" change.
  927    position: Option<usize>,
  928}
  929
  930impl ChangeList {
  931    pub fn new() -> Self {
  932        Self {
  933            changes: Vec::new(),
  934            position: None,
  935        }
  936    }
  937
  938    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  939    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  940    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  941        if self.changes.is_empty() {
  942            return None;
  943        }
  944
  945        let prev = self.position.unwrap_or(self.changes.len());
  946        let next = if direction == Direction::Prev {
  947            prev.saturating_sub(count)
  948        } else {
  949            (prev + count).min(self.changes.len() - 1)
  950        };
  951        self.position = Some(next);
  952        self.changes.get(next).map(|change| change.locations())
  953    }
  954
  955    /// Adds a new change to the list, resetting the change list position.
  956    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  957        self.position.take();
  958        if let Some(last) = self.changes.last_mut()
  959            && group
  960        {
  961            last.current = Some(new_positions)
  962        } else {
  963            self.changes.push(ChangeLocation {
  964                original: new_positions,
  965                current: None,
  966            });
  967        }
  968    }
  969
  970    pub fn last(&self) -> Option<&[Anchor]> {
  971        self.changes.last().map(|change| change.locations())
  972    }
  973
  974    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  975        self.changes.last().map(|change| change.original.as_slice())
  976    }
  977
  978    pub fn invert_last_group(&mut self) {
  979        if let Some(last) = self.changes.last_mut()
  980            && let Some(current) = last.current.as_mut()
  981        {
  982            mem::swap(&mut last.original, current);
  983        }
  984    }
  985}
  986
  987#[derive(Clone)]
  988struct InlineBlamePopoverState {
  989    scroll_handle: ScrollHandle,
  990    commit_message: Option<ParsedCommitMessage>,
  991    markdown: Entity<Markdown>,
  992}
  993
  994struct InlineBlamePopover {
  995    position: gpui::Point<Pixels>,
  996    hide_task: Option<Task<()>>,
  997    popover_bounds: Option<Bounds<Pixels>>,
  998    popover_state: InlineBlamePopoverState,
  999    keyboard_grace: bool,
 1000}
 1001
 1002enum SelectionDragState {
 1003    /// State when no drag related activity is detected.
 1004    None,
 1005    /// State when the mouse is down on a selection that is about to be dragged.
 1006    ReadyToDrag {
 1007        selection: Selection<Anchor>,
 1008        click_position: gpui::Point<Pixels>,
 1009        mouse_down_time: Instant,
 1010    },
 1011    /// State when the mouse is dragging the selection in the editor.
 1012    Dragging {
 1013        selection: Selection<Anchor>,
 1014        drop_cursor: Selection<Anchor>,
 1015        hide_drop_cursor: bool,
 1016    },
 1017}
 1018
 1019enum ColumnarSelectionState {
 1020    FromMouse {
 1021        selection_tail: Anchor,
 1022        display_point: Option<DisplayPoint>,
 1023    },
 1024    FromSelection {
 1025        selection_tail: Anchor,
 1026    },
 1027}
 1028
 1029/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1030/// a breakpoint on them.
 1031#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1032struct PhantomBreakpointIndicator {
 1033    display_row: DisplayRow,
 1034    /// There's a small debounce between hovering over the line and showing the indicator.
 1035    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1036    is_active: bool,
 1037    collides_with_existing_breakpoint: bool,
 1038}
 1039
 1040/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1041/// in diff view mode.
 1042#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1043pub(crate) struct PhantomDiffReviewIndicator {
 1044    /// The starting anchor of the selection (or the only row if not dragging).
 1045    pub start: Anchor,
 1046    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1047    pub end: Anchor,
 1048    /// There's a small debounce between hovering over the line and showing the indicator.
 1049    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1050    pub is_active: bool,
 1051}
 1052
 1053#[derive(Clone, Debug)]
 1054pub(crate) struct DiffReviewDragState {
 1055    pub start_anchor: Anchor,
 1056    pub current_anchor: Anchor,
 1057}
 1058
 1059impl DiffReviewDragState {
 1060    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1061        let start = self.start_anchor.to_display_point(snapshot).row();
 1062        let current = self.current_anchor.to_display_point(snapshot).row();
 1063
 1064        (start..=current).sorted()
 1065    }
 1066}
 1067
 1068/// Identifies a specific hunk in the diff buffer.
 1069/// Used as a key to group comments by their location.
 1070#[derive(Clone, Debug)]
 1071pub struct DiffHunkKey {
 1072    /// The file path (relative to worktree) this hunk belongs to.
 1073    pub file_path: Arc<util::rel_path::RelPath>,
 1074    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1075    pub hunk_start_anchor: Anchor,
 1076}
 1077
 1078/// A review comment stored locally before being sent to the Agent panel.
 1079#[derive(Clone)]
 1080pub struct StoredReviewComment {
 1081    /// Unique identifier for this comment (for edit/delete operations).
 1082    pub id: usize,
 1083    /// The comment text entered by the user.
 1084    pub comment: String,
 1085    /// Anchors for the code range being reviewed.
 1086    pub range: Range<Anchor>,
 1087    /// Timestamp when the comment was created (for chronological ordering).
 1088    pub created_at: Instant,
 1089    /// Whether this comment is currently being edited inline.
 1090    pub is_editing: bool,
 1091}
 1092
 1093impl StoredReviewComment {
 1094    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1095        Self {
 1096            id,
 1097            comment,
 1098            range: anchor_range,
 1099            created_at: Instant::now(),
 1100            is_editing: false,
 1101        }
 1102    }
 1103}
 1104
 1105/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1106pub(crate) struct DiffReviewOverlay {
 1107    pub anchor_range: Range<Anchor>,
 1108    /// The block ID for the overlay.
 1109    pub block_id: CustomBlockId,
 1110    /// The editor entity for the review input.
 1111    pub prompt_editor: Entity<Editor>,
 1112    /// The hunk key this overlay belongs to.
 1113    pub hunk_key: DiffHunkKey,
 1114    /// Whether the comments section is expanded.
 1115    pub comments_expanded: bool,
 1116    /// Editors for comments currently being edited inline.
 1117    /// Key: comment ID, Value: Editor entity for inline editing.
 1118    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1119    /// Subscriptions for inline edit editors' action handlers.
 1120    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1121    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1122    /// The current user's avatar URI for display in comment rows.
 1123    pub user_avatar_uri: Option<SharedUri>,
 1124    /// Subscription to keep the action handler alive.
 1125    _subscription: Subscription,
 1126}
 1127
 1128/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1129///
 1130/// See the [module level documentation](self) for more information.
 1131pub struct Editor {
 1132    focus_handle: FocusHandle,
 1133    last_focused_descendant: Option<WeakFocusHandle>,
 1134    /// The text buffer being edited
 1135    buffer: Entity<MultiBuffer>,
 1136    /// Map of how text in the buffer should be displayed.
 1137    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1138    pub display_map: Entity<DisplayMap>,
 1139    placeholder_display_map: Option<Entity<DisplayMap>>,
 1140    pub selections: SelectionsCollection,
 1141    pub scroll_manager: ScrollManager,
 1142    /// When inline assist editors are linked, they all render cursors because
 1143    /// typing enters text into each of them, even the ones that aren't focused.
 1144    pub(crate) show_cursor_when_unfocused: bool,
 1145    columnar_selection_state: Option<ColumnarSelectionState>,
 1146    add_selections_state: Option<AddSelectionsState>,
 1147    select_next_state: Option<SelectNextState>,
 1148    select_prev_state: Option<SelectNextState>,
 1149    selection_history: SelectionHistory,
 1150    defer_selection_effects: bool,
 1151    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1152    autoclose_regions: Vec<AutocloseRegion>,
 1153    snippet_stack: InvalidationStack<SnippetState>,
 1154    select_syntax_node_history: SelectSyntaxNodeHistory,
 1155    ime_transaction: Option<TransactionId>,
 1156    pub diagnostics_max_severity: DiagnosticSeverity,
 1157    active_diagnostics: ActiveDiagnostic,
 1158    show_inline_diagnostics: bool,
 1159    inline_diagnostics_update: Task<()>,
 1160    inline_diagnostics_enabled: bool,
 1161    diagnostics_enabled: bool,
 1162    word_completions_enabled: bool,
 1163    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1164    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1165    hard_wrap: Option<usize>,
 1166    project: Option<Entity<Project>>,
 1167    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1168    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1169    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1170    blink_manager: Entity<BlinkManager>,
 1171    show_cursor_names: bool,
 1172    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1173    pub show_local_selections: bool,
 1174    mode: EditorMode,
 1175    show_breadcrumbs: bool,
 1176    show_gutter: bool,
 1177    show_scrollbars: ScrollbarAxes,
 1178    minimap_visibility: MinimapVisibility,
 1179    offset_content: bool,
 1180    disable_expand_excerpt_buttons: bool,
 1181    delegate_expand_excerpts: bool,
 1182    delegate_stage_and_restore: bool,
 1183    delegate_open_excerpts: bool,
 1184    enable_lsp_data: bool,
 1185    enable_runnables: bool,
 1186    show_line_numbers: Option<bool>,
 1187    use_relative_line_numbers: Option<bool>,
 1188    show_git_diff_gutter: Option<bool>,
 1189    show_code_actions: Option<bool>,
 1190    show_runnables: Option<bool>,
 1191    show_breakpoints: Option<bool>,
 1192    show_diff_review_button: bool,
 1193    show_wrap_guides: Option<bool>,
 1194    show_indent_guides: Option<bool>,
 1195    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1196    highlight_order: usize,
 1197    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1198    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1199    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1200    scrollbar_marker_state: ScrollbarMarkerState,
 1201    active_indent_guides_state: ActiveIndentGuidesState,
 1202    nav_history: Option<ItemNavHistory>,
 1203    context_menu: RefCell<Option<CodeContextMenu>>,
 1204    context_menu_options: Option<ContextMenuOptions>,
 1205    mouse_context_menu: Option<MouseContextMenu>,
 1206    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1207    inline_blame_popover: Option<InlineBlamePopover>,
 1208    inline_blame_popover_show_task: Option<Task<()>>,
 1209    signature_help_state: SignatureHelpState,
 1210    auto_signature_help: Option<bool>,
 1211    find_all_references_task_sources: Vec<Anchor>,
 1212    next_completion_id: CompletionId,
 1213    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1214    code_actions_task: Option<Task<Result<()>>>,
 1215    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1216    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1217    debounced_selection_highlight_complete: bool,
 1218    document_highlights_task: Option<Task<()>>,
 1219    linked_editing_range_task: Option<Task<Option<()>>>,
 1220    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1221    pending_rename: Option<RenameState>,
 1222    searchable: bool,
 1223    cursor_shape: CursorShape,
 1224    /// Whether the cursor is offset one character to the left when something is
 1225    /// selected (needed for vim visual mode)
 1226    cursor_offset_on_selection: bool,
 1227    current_line_highlight: Option<CurrentLineHighlight>,
 1228    /// Whether to collapse search match ranges to just their start position.
 1229    /// When true, navigating to a match positions the cursor at the match
 1230    /// without selecting the matched text.
 1231    collapse_matches: bool,
 1232    autoindent_mode: Option<AutoindentMode>,
 1233    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1234    input_enabled: bool,
 1235    use_modal_editing: bool,
 1236    read_only: bool,
 1237    leader_id: Option<CollaboratorId>,
 1238    remote_id: Option<ViewId>,
 1239    pub hover_state: HoverState,
 1240    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1241    prev_pressure_stage: Option<PressureStage>,
 1242    gutter_hovered: bool,
 1243    hovered_link_state: Option<HoveredLinkState>,
 1244    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1245    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1246    active_edit_prediction: Option<EditPredictionState>,
 1247    /// Used to prevent flickering as the user types while the menu is open
 1248    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1249    edit_prediction_settings: EditPredictionSettings,
 1250    edit_predictions_hidden_for_vim_mode: bool,
 1251    show_edit_predictions_override: Option<bool>,
 1252    show_completions_on_input_override: Option<bool>,
 1253    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1254    edit_prediction_preview: EditPredictionPreview,
 1255    edit_prediction_indent_conflict: bool,
 1256    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1257    next_inlay_id: usize,
 1258    next_color_inlay_id: usize,
 1259    _subscriptions: Vec<Subscription>,
 1260    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1261    gutter_dimensions: GutterDimensions,
 1262    style: Option<EditorStyle>,
 1263    text_style_refinement: Option<TextStyleRefinement>,
 1264    next_editor_action_id: EditorActionId,
 1265    editor_actions: Rc<
 1266        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1267    >,
 1268    use_autoclose: bool,
 1269    use_auto_surround: bool,
 1270    auto_replace_emoji_shortcode: bool,
 1271    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1272    show_git_blame_gutter: bool,
 1273    show_git_blame_inline: bool,
 1274    show_git_blame_inline_delay_task: Option<Task<()>>,
 1275    git_blame_inline_enabled: bool,
 1276    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1277    buffer_serialization: Option<BufferSerialization>,
 1278    show_selection_menu: Option<bool>,
 1279    blame: Option<Entity<GitBlame>>,
 1280    blame_subscription: Option<Subscription>,
 1281    custom_context_menu: Option<
 1282        Box<
 1283            dyn 'static
 1284                + Fn(
 1285                    &mut Self,
 1286                    DisplayPoint,
 1287                    &mut Window,
 1288                    &mut Context<Self>,
 1289                ) -> Option<Entity<ui::ContextMenu>>,
 1290        >,
 1291    >,
 1292    last_bounds: Option<Bounds<Pixels>>,
 1293    last_position_map: Option<Rc<PositionMap>>,
 1294    expect_bounds_change: Option<Bounds<Pixels>>,
 1295    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1296    tasks_update_task: Option<Task<()>>,
 1297    breakpoint_store: Option<Entity<BreakpointStore>>,
 1298    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1299    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1300    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1301    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1302    /// when hunks have comments stored.
 1303    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1304    /// Stored review comments grouped by hunk.
 1305    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1306    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1307    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1308    /// Counter for generating unique comment IDs.
 1309    next_review_comment_id: usize,
 1310    hovered_diff_hunk_row: Option<DisplayRow>,
 1311    pull_diagnostics_task: Task<()>,
 1312    in_project_search: bool,
 1313    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1314    breadcrumb_header: Option<String>,
 1315    focused_block: Option<FocusedBlock>,
 1316    next_scroll_position: NextScrollCursorCenterTopBottom,
 1317    addons: HashMap<TypeId, Box<dyn Addon>>,
 1318    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1319    load_diff_task: Option<Shared<Task<()>>>,
 1320    /// Whether we are temporarily displaying a diff other than git's
 1321    temporary_diff_override: bool,
 1322    selection_mark_mode: bool,
 1323    toggle_fold_multiple_buffers: Task<()>,
 1324    _scroll_cursor_center_top_bottom_task: Task<()>,
 1325    serialize_selections: Task<()>,
 1326    serialize_folds: Task<()>,
 1327    mouse_cursor_hidden: bool,
 1328    minimap: Option<Entity<Self>>,
 1329    hide_mouse_mode: HideMouseMode,
 1330    pub change_list: ChangeList,
 1331    inline_value_cache: InlineValueCache,
 1332    number_deleted_lines: bool,
 1333
 1334    selection_drag_state: SelectionDragState,
 1335    colors: Option<LspColorData>,
 1336    post_scroll_update: Task<()>,
 1337    refresh_colors_task: Task<()>,
 1338    use_document_folding_ranges: bool,
 1339    refresh_folding_ranges_task: Task<()>,
 1340    inlay_hints: Option<LspInlayHintData>,
 1341    folding_newlines: Task<()>,
 1342    select_next_is_case_sensitive: Option<bool>,
 1343    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1344    on_local_selections_changed:
 1345        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1346    suppress_selection_callback: bool,
 1347    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1348    accent_data: Option<AccentData>,
 1349    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1350    semantic_token_state: SemanticTokenState,
 1351    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1352    refresh_document_symbols_task: Shared<Task<()>>,
 1353    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1354    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1355    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1356    sticky_headers_task: Task<()>,
 1357    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1358}
 1359
 1360#[derive(Debug, PartialEq)]
 1361struct AccentData {
 1362    colors: AccentColors,
 1363    overrides: Vec<SharedString>,
 1364}
 1365
 1366fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1367    if debounce_ms > 0 {
 1368        Some(Duration::from_millis(debounce_ms))
 1369    } else {
 1370        None
 1371    }
 1372}
 1373
 1374#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1375enum NextScrollCursorCenterTopBottom {
 1376    #[default]
 1377    Center,
 1378    Top,
 1379    Bottom,
 1380}
 1381
 1382impl NextScrollCursorCenterTopBottom {
 1383    fn next(&self) -> Self {
 1384        match self {
 1385            Self::Center => Self::Top,
 1386            Self::Top => Self::Bottom,
 1387            Self::Bottom => Self::Center,
 1388        }
 1389    }
 1390}
 1391
 1392#[derive(Clone)]
 1393pub struct EditorSnapshot {
 1394    pub mode: EditorMode,
 1395    show_gutter: bool,
 1396    offset_content: bool,
 1397    show_line_numbers: Option<bool>,
 1398    number_deleted_lines: bool,
 1399    show_git_diff_gutter: Option<bool>,
 1400    show_code_actions: Option<bool>,
 1401    show_runnables: Option<bool>,
 1402    show_breakpoints: Option<bool>,
 1403    git_blame_gutter_max_author_length: Option<usize>,
 1404    pub display_snapshot: DisplaySnapshot,
 1405    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1406    is_focused: bool,
 1407    scroll_anchor: SharedScrollAnchor,
 1408    ongoing_scroll: OngoingScroll,
 1409    current_line_highlight: CurrentLineHighlight,
 1410    gutter_hovered: bool,
 1411    semantic_tokens_enabled: bool,
 1412}
 1413
 1414#[derive(Default, Debug, Clone, Copy)]
 1415pub struct GutterDimensions {
 1416    pub left_padding: Pixels,
 1417    pub right_padding: Pixels,
 1418    pub width: Pixels,
 1419    pub margin: Pixels,
 1420    pub git_blame_entries_width: Option<Pixels>,
 1421}
 1422
 1423impl GutterDimensions {
 1424    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1425        Self {
 1426            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1427            ..Default::default()
 1428        }
 1429    }
 1430
 1431    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1432        -cx.text_system().descent(font_id, font_size)
 1433    }
 1434    /// The full width of the space taken up by the gutter.
 1435    pub fn full_width(&self) -> Pixels {
 1436        self.margin + self.width
 1437    }
 1438
 1439    /// The width of the space reserved for the fold indicators,
 1440    /// use alongside 'justify_end' and `gutter_width` to
 1441    /// right align content with the line numbers
 1442    pub fn fold_area_width(&self) -> Pixels {
 1443        self.margin + self.right_padding
 1444    }
 1445}
 1446
 1447struct CharacterDimensions {
 1448    em_width: Pixels,
 1449    em_advance: Pixels,
 1450    line_height: Pixels,
 1451}
 1452
 1453#[derive(Debug)]
 1454pub struct RemoteSelection {
 1455    pub replica_id: ReplicaId,
 1456    pub selection: Selection<Anchor>,
 1457    pub cursor_shape: CursorShape,
 1458    pub collaborator_id: CollaboratorId,
 1459    pub line_mode: bool,
 1460    pub user_name: Option<SharedString>,
 1461    pub color: PlayerColor,
 1462}
 1463
 1464#[derive(Clone, Debug)]
 1465struct SelectionHistoryEntry {
 1466    selections: Arc<[Selection<Anchor>]>,
 1467    select_next_state: Option<SelectNextState>,
 1468    select_prev_state: Option<SelectNextState>,
 1469    add_selections_state: Option<AddSelectionsState>,
 1470}
 1471
 1472#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1473enum SelectionHistoryMode {
 1474    #[default]
 1475    Normal,
 1476    Undoing,
 1477    Redoing,
 1478    Skipping,
 1479}
 1480
 1481#[derive(Clone, PartialEq, Eq, Hash)]
 1482struct HoveredCursor {
 1483    replica_id: ReplicaId,
 1484    selection_id: usize,
 1485}
 1486
 1487#[derive(Debug)]
 1488/// SelectionEffects controls the side-effects of updating the selection.
 1489///
 1490/// The default behaviour does "what you mostly want":
 1491/// - it pushes to the nav history if the cursor moved by >10 lines
 1492/// - it re-triggers completion requests
 1493/// - it scrolls to fit
 1494///
 1495/// You might want to modify these behaviours. For example when doing a "jump"
 1496/// like go to definition, we always want to add to nav history; but when scrolling
 1497/// in vim mode we never do.
 1498///
 1499/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1500/// move.
 1501#[derive(Clone)]
 1502pub struct SelectionEffects {
 1503    nav_history: Option<bool>,
 1504    completions: bool,
 1505    scroll: Option<Autoscroll>,
 1506}
 1507
 1508impl Default for SelectionEffects {
 1509    fn default() -> Self {
 1510        Self {
 1511            nav_history: None,
 1512            completions: true,
 1513            scroll: Some(Autoscroll::fit()),
 1514        }
 1515    }
 1516}
 1517impl SelectionEffects {
 1518    pub fn scroll(scroll: Autoscroll) -> Self {
 1519        Self {
 1520            scroll: Some(scroll),
 1521            ..Default::default()
 1522        }
 1523    }
 1524
 1525    pub fn no_scroll() -> Self {
 1526        Self {
 1527            scroll: None,
 1528            ..Default::default()
 1529        }
 1530    }
 1531
 1532    pub fn completions(self, completions: bool) -> Self {
 1533        Self {
 1534            completions,
 1535            ..self
 1536        }
 1537    }
 1538
 1539    pub fn nav_history(self, nav_history: bool) -> Self {
 1540        Self {
 1541            nav_history: Some(nav_history),
 1542            ..self
 1543        }
 1544    }
 1545}
 1546
 1547struct DeferredSelectionEffectsState {
 1548    changed: bool,
 1549    effects: SelectionEffects,
 1550    old_cursor_position: Anchor,
 1551    history_entry: SelectionHistoryEntry,
 1552}
 1553
 1554#[derive(Default)]
 1555struct SelectionHistory {
 1556    #[allow(clippy::type_complexity)]
 1557    selections_by_transaction:
 1558        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1559    mode: SelectionHistoryMode,
 1560    undo_stack: VecDeque<SelectionHistoryEntry>,
 1561    redo_stack: VecDeque<SelectionHistoryEntry>,
 1562}
 1563
 1564impl SelectionHistory {
 1565    #[track_caller]
 1566    fn insert_transaction(
 1567        &mut self,
 1568        transaction_id: TransactionId,
 1569        selections: Arc<[Selection<Anchor>]>,
 1570    ) {
 1571        if selections.is_empty() {
 1572            log::error!(
 1573                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1574                std::panic::Location::caller()
 1575            );
 1576            return;
 1577        }
 1578        self.selections_by_transaction
 1579            .insert(transaction_id, (selections, None));
 1580    }
 1581
 1582    #[allow(clippy::type_complexity)]
 1583    fn transaction(
 1584        &self,
 1585        transaction_id: TransactionId,
 1586    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1587        self.selections_by_transaction.get(&transaction_id)
 1588    }
 1589
 1590    #[allow(clippy::type_complexity)]
 1591    fn transaction_mut(
 1592        &mut self,
 1593        transaction_id: TransactionId,
 1594    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1595        self.selections_by_transaction.get_mut(&transaction_id)
 1596    }
 1597
 1598    fn push(&mut self, entry: SelectionHistoryEntry) {
 1599        if !entry.selections.is_empty() {
 1600            match self.mode {
 1601                SelectionHistoryMode::Normal => {
 1602                    self.push_undo(entry);
 1603                    self.redo_stack.clear();
 1604                }
 1605                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1606                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1607                SelectionHistoryMode::Skipping => {}
 1608            }
 1609        }
 1610    }
 1611
 1612    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1613        if self
 1614            .undo_stack
 1615            .back()
 1616            .is_none_or(|e| e.selections != entry.selections)
 1617        {
 1618            self.undo_stack.push_back(entry);
 1619            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1620                self.undo_stack.pop_front();
 1621            }
 1622        }
 1623    }
 1624
 1625    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1626        if self
 1627            .redo_stack
 1628            .back()
 1629            .is_none_or(|e| e.selections != entry.selections)
 1630        {
 1631            self.redo_stack.push_back(entry);
 1632            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1633                self.redo_stack.pop_front();
 1634            }
 1635        }
 1636    }
 1637}
 1638
 1639#[derive(Clone, Copy)]
 1640pub struct RowHighlightOptions {
 1641    pub autoscroll: bool,
 1642    pub include_gutter: bool,
 1643}
 1644
 1645impl Default for RowHighlightOptions {
 1646    fn default() -> Self {
 1647        Self {
 1648            autoscroll: Default::default(),
 1649            include_gutter: true,
 1650        }
 1651    }
 1652}
 1653
 1654struct RowHighlight {
 1655    index: usize,
 1656    range: Range<Anchor>,
 1657    color: Hsla,
 1658    options: RowHighlightOptions,
 1659    type_id: TypeId,
 1660}
 1661
 1662#[derive(Clone, Debug)]
 1663struct AddSelectionsState {
 1664    groups: Vec<AddSelectionsGroup>,
 1665}
 1666
 1667#[derive(Clone, Debug)]
 1668struct AddSelectionsGroup {
 1669    above: bool,
 1670    stack: Vec<usize>,
 1671}
 1672
 1673#[derive(Clone)]
 1674struct SelectNextState {
 1675    query: AhoCorasick,
 1676    wordwise: bool,
 1677    done: bool,
 1678}
 1679
 1680impl std::fmt::Debug for SelectNextState {
 1681    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1682        f.debug_struct(std::any::type_name::<Self>())
 1683            .field("wordwise", &self.wordwise)
 1684            .field("done", &self.done)
 1685            .finish()
 1686    }
 1687}
 1688
 1689#[derive(Debug)]
 1690struct AutocloseRegion {
 1691    selection_id: usize,
 1692    range: Range<Anchor>,
 1693    pair: BracketPair,
 1694}
 1695
 1696#[derive(Debug)]
 1697struct SnippetState {
 1698    ranges: Vec<Vec<Range<Anchor>>>,
 1699    active_index: usize,
 1700    choices: Vec<Option<Vec<String>>>,
 1701}
 1702
 1703#[doc(hidden)]
 1704pub struct RenameState {
 1705    pub range: Range<Anchor>,
 1706    pub old_name: Arc<str>,
 1707    pub editor: Entity<Editor>,
 1708    block_id: CustomBlockId,
 1709}
 1710
 1711struct InvalidationStack<T>(Vec<T>);
 1712
 1713struct RegisteredEditPredictionDelegate {
 1714    provider: Arc<dyn EditPredictionDelegateHandle>,
 1715    _subscription: Subscription,
 1716}
 1717
 1718#[derive(Debug, PartialEq, Eq)]
 1719pub struct ActiveDiagnosticGroup {
 1720    pub active_range: Range<Anchor>,
 1721    pub active_message: String,
 1722    pub group_id: usize,
 1723    pub blocks: HashSet<CustomBlockId>,
 1724}
 1725
 1726#[derive(Debug, PartialEq, Eq)]
 1727
 1728pub(crate) enum ActiveDiagnostic {
 1729    None,
 1730    All,
 1731    Group(ActiveDiagnosticGroup),
 1732}
 1733
 1734#[derive(Serialize, Deserialize, Clone, Debug)]
 1735pub struct ClipboardSelection {
 1736    /// The number of bytes in this selection.
 1737    pub len: usize,
 1738    /// Whether this was a full-line selection.
 1739    pub is_entire_line: bool,
 1740    /// The indentation of the first line when this content was originally copied.
 1741    pub first_line_indent: u32,
 1742    #[serde(default)]
 1743    pub file_path: Option<PathBuf>,
 1744    #[serde(default)]
 1745    pub line_range: Option<RangeInclusive<u32>>,
 1746}
 1747
 1748impl ClipboardSelection {
 1749    pub fn for_buffer(
 1750        len: usize,
 1751        is_entire_line: bool,
 1752        range: Range<Point>,
 1753        buffer: &MultiBufferSnapshot,
 1754        project: Option<&Entity<Project>>,
 1755        cx: &App,
 1756    ) -> Self {
 1757        let first_line_indent = buffer
 1758            .indent_size_for_line(MultiBufferRow(range.start.row))
 1759            .len;
 1760
 1761        let file_path = util::maybe!({
 1762            let project = project?.read(cx);
 1763            let file = buffer.file_at(range.start)?;
 1764            let project_path = ProjectPath {
 1765                worktree_id: file.worktree_id(cx),
 1766                path: file.path().clone(),
 1767            };
 1768            project.absolute_path(&project_path, cx)
 1769        });
 1770
 1771        let line_range = file_path.as_ref().and_then(|_| {
 1772            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1773            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1774            if start_excerpt_id == end_excerpt_id {
 1775                Some(start_point.row..=end_point.row)
 1776            } else {
 1777                None
 1778            }
 1779        });
 1780
 1781        Self {
 1782            len,
 1783            is_entire_line,
 1784            first_line_indent,
 1785            file_path,
 1786            line_range,
 1787        }
 1788    }
 1789}
 1790
 1791// selections, scroll behavior, was newest selection reversed
 1792type SelectSyntaxNodeHistoryState = (
 1793    Box<[Selection<Anchor>]>,
 1794    SelectSyntaxNodeScrollBehavior,
 1795    bool,
 1796);
 1797
 1798#[derive(Default)]
 1799struct SelectSyntaxNodeHistory {
 1800    stack: Vec<SelectSyntaxNodeHistoryState>,
 1801    // disable temporarily to allow changing selections without losing the stack
 1802    pub disable_clearing: bool,
 1803}
 1804
 1805impl SelectSyntaxNodeHistory {
 1806    pub fn try_clear(&mut self) {
 1807        if !self.disable_clearing {
 1808            self.stack.clear();
 1809        }
 1810    }
 1811
 1812    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1813        self.stack.push(selection);
 1814    }
 1815
 1816    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1817        self.stack.pop()
 1818    }
 1819}
 1820
 1821enum SelectSyntaxNodeScrollBehavior {
 1822    CursorTop,
 1823    FitSelection,
 1824    CursorBottom,
 1825}
 1826
 1827#[derive(Debug, Clone, Copy)]
 1828pub(crate) struct NavigationData {
 1829    cursor_anchor: Anchor,
 1830    cursor_position: Point,
 1831    scroll_anchor: ScrollAnchor,
 1832    scroll_top_row: u32,
 1833}
 1834
 1835#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1836pub enum GotoDefinitionKind {
 1837    Symbol,
 1838    Declaration,
 1839    Type,
 1840    Implementation,
 1841}
 1842
 1843pub enum FormatTarget {
 1844    Buffers(HashSet<Entity<Buffer>>),
 1845    Ranges(Vec<Range<MultiBufferPoint>>),
 1846}
 1847
 1848pub(crate) struct FocusedBlock {
 1849    id: BlockId,
 1850    focus_handle: WeakFocusHandle,
 1851}
 1852
 1853#[derive(Clone, Debug)]
 1854pub enum JumpData {
 1855    MultiBufferRow {
 1856        row: MultiBufferRow,
 1857        line_offset_from_top: u32,
 1858    },
 1859    MultiBufferPoint {
 1860        excerpt_id: ExcerptId,
 1861        position: Point,
 1862        anchor: text::Anchor,
 1863        line_offset_from_top: u32,
 1864    },
 1865}
 1866
 1867pub enum MultibufferSelectionMode {
 1868    First,
 1869    All,
 1870}
 1871
 1872#[derive(Clone, Copy, Debug, Default)]
 1873pub struct RewrapOptions {
 1874    pub override_language_settings: bool,
 1875    pub preserve_existing_whitespace: bool,
 1876}
 1877
 1878impl Editor {
 1879    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1880        let buffer = cx.new(|cx| Buffer::local("", cx));
 1881        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1882        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1883    }
 1884
 1885    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1886        let buffer = cx.new(|cx| Buffer::local("", cx));
 1887        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1888        Self::new(EditorMode::full(), buffer, None, window, cx)
 1889    }
 1890
 1891    pub fn auto_height(
 1892        min_lines: usize,
 1893        max_lines: usize,
 1894        window: &mut Window,
 1895        cx: &mut Context<Self>,
 1896    ) -> Self {
 1897        let buffer = cx.new(|cx| Buffer::local("", cx));
 1898        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1899        Self::new(
 1900            EditorMode::AutoHeight {
 1901                min_lines,
 1902                max_lines: Some(max_lines),
 1903            },
 1904            buffer,
 1905            None,
 1906            window,
 1907            cx,
 1908        )
 1909    }
 1910
 1911    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1912    /// The editor grows as tall as needed to fit its content.
 1913    pub fn auto_height_unbounded(
 1914        min_lines: usize,
 1915        window: &mut Window,
 1916        cx: &mut Context<Self>,
 1917    ) -> Self {
 1918        let buffer = cx.new(|cx| Buffer::local("", cx));
 1919        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1920        Self::new(
 1921            EditorMode::AutoHeight {
 1922                min_lines,
 1923                max_lines: None,
 1924            },
 1925            buffer,
 1926            None,
 1927            window,
 1928            cx,
 1929        )
 1930    }
 1931
 1932    pub fn for_buffer(
 1933        buffer: Entity<Buffer>,
 1934        project: Option<Entity<Project>>,
 1935        window: &mut Window,
 1936        cx: &mut Context<Self>,
 1937    ) -> Self {
 1938        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1939        Self::new(EditorMode::full(), buffer, project, window, cx)
 1940    }
 1941
 1942    pub fn for_multibuffer(
 1943        buffer: Entity<MultiBuffer>,
 1944        project: Option<Entity<Project>>,
 1945        window: &mut Window,
 1946        cx: &mut Context<Self>,
 1947    ) -> Self {
 1948        Self::new(EditorMode::full(), buffer, project, window, cx)
 1949    }
 1950
 1951    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1952        let mut clone = Self::new(
 1953            self.mode.clone(),
 1954            self.buffer.clone(),
 1955            self.project.clone(),
 1956            window,
 1957            cx,
 1958        );
 1959        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1960            let snapshot = display_map.snapshot(cx);
 1961            clone.display_map.update(cx, |display_map, cx| {
 1962                display_map.set_state(&snapshot, cx);
 1963            });
 1964            snapshot
 1965        });
 1966        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1967        clone.folds_did_change(cx);
 1968        clone.selections.clone_state(&self.selections);
 1969        clone
 1970            .scroll_manager
 1971            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1972        clone.searchable = self.searchable;
 1973        clone.read_only = self.read_only;
 1974        clone
 1975    }
 1976
 1977    pub fn new(
 1978        mode: EditorMode,
 1979        buffer: Entity<MultiBuffer>,
 1980        project: Option<Entity<Project>>,
 1981        window: &mut Window,
 1982        cx: &mut Context<Self>,
 1983    ) -> Self {
 1984        Editor::new_internal(mode, buffer, project, None, window, cx)
 1985    }
 1986
 1987    pub fn refresh_sticky_headers(
 1988        &mut self,
 1989        display_snapshot: &DisplaySnapshot,
 1990        cx: &mut Context<Editor>,
 1991    ) {
 1992        if !self.mode.is_full() {
 1993            return;
 1994        }
 1995        let multi_buffer = display_snapshot.buffer_snapshot();
 1996        let scroll_anchor = self
 1997            .scroll_manager
 1998            .native_anchor(display_snapshot, cx)
 1999            .anchor;
 2000        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2001            return;
 2002        };
 2003        let buffer = buffer.clone();
 2004
 2005        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2006        let max_row = buffer.max_point().row;
 2007        let start_row = buffer_visible_start.row.min(max_row);
 2008        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2009
 2010        let syntax = self.style(cx).syntax.clone();
 2011        let background_task = cx.background_spawn(async move {
 2012            buffer
 2013                .outline_items_containing(
 2014                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2015                    true,
 2016                    Some(syntax.as_ref()),
 2017                )
 2018                .into_iter()
 2019                .map(|outline_item| OutlineItem {
 2020                    depth: outline_item.depth,
 2021                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2022                    source_range_for_text: Anchor::range_in_buffer(
 2023                        excerpt_id,
 2024                        outline_item.source_range_for_text,
 2025                    ),
 2026                    text: outline_item.text,
 2027                    highlight_ranges: outline_item.highlight_ranges,
 2028                    name_ranges: outline_item.name_ranges,
 2029                    body_range: outline_item
 2030                        .body_range
 2031                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2032                    annotation_range: outline_item
 2033                        .annotation_range
 2034                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2035                })
 2036                .collect()
 2037        });
 2038        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2039            let sticky_headers = background_task.await;
 2040            this.update(cx, |this, cx| {
 2041                this.sticky_headers = Some(sticky_headers);
 2042                cx.notify();
 2043            })
 2044            .ok();
 2045        });
 2046    }
 2047
 2048    fn new_internal(
 2049        mode: EditorMode,
 2050        multi_buffer: Entity<MultiBuffer>,
 2051        project: Option<Entity<Project>>,
 2052        display_map: Option<Entity<DisplayMap>>,
 2053        window: &mut Window,
 2054        cx: &mut Context<Self>,
 2055    ) -> Self {
 2056        debug_assert!(
 2057            display_map.is_none() || mode.is_minimap(),
 2058            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2059        );
 2060
 2061        let full_mode = mode.is_full();
 2062        let is_minimap = mode.is_minimap();
 2063        let diagnostics_max_severity = if full_mode {
 2064            EditorSettings::get_global(cx)
 2065                .diagnostics_max_severity
 2066                .unwrap_or(DiagnosticSeverity::Hint)
 2067        } else {
 2068            DiagnosticSeverity::Off
 2069        };
 2070        let style = window.text_style();
 2071        let font_size = style.font_size.to_pixels(window.rem_size());
 2072        let editor = cx.entity().downgrade();
 2073        let fold_placeholder = FoldPlaceholder {
 2074            constrain_width: false,
 2075            render: Arc::new(move |fold_id, fold_range, cx| {
 2076                let editor = editor.clone();
 2077                FoldPlaceholder::fold_element(fold_id, cx)
 2078                    .cursor_pointer()
 2079                    .child("")
 2080                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2081                    .on_click(move |_, _window, cx| {
 2082                        editor
 2083                            .update(cx, |editor, cx| {
 2084                                editor.unfold_ranges(
 2085                                    &[fold_range.start..fold_range.end],
 2086                                    true,
 2087                                    false,
 2088                                    cx,
 2089                                );
 2090                                cx.stop_propagation();
 2091                            })
 2092                            .ok();
 2093                    })
 2094                    .into_any()
 2095            }),
 2096            merge_adjacent: true,
 2097            ..FoldPlaceholder::default()
 2098        };
 2099        let display_map = display_map.unwrap_or_else(|| {
 2100            cx.new(|cx| {
 2101                DisplayMap::new(
 2102                    multi_buffer.clone(),
 2103                    style.font(),
 2104                    font_size,
 2105                    None,
 2106                    FILE_HEADER_HEIGHT,
 2107                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2108                    fold_placeholder,
 2109                    diagnostics_max_severity,
 2110                    cx,
 2111                )
 2112            })
 2113        });
 2114
 2115        let selections = SelectionsCollection::new();
 2116
 2117        let blink_manager = cx.new(|cx| {
 2118            let mut blink_manager = BlinkManager::new(
 2119                CURSOR_BLINK_INTERVAL,
 2120                |cx| EditorSettings::get_global(cx).cursor_blink,
 2121                cx,
 2122            );
 2123            if is_minimap {
 2124                blink_manager.disable(cx);
 2125            }
 2126            blink_manager
 2127        });
 2128
 2129        let soft_wrap_mode_override =
 2130            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2131
 2132        let mut project_subscriptions = Vec::new();
 2133        if full_mode && let Some(project) = project.as_ref() {
 2134            project_subscriptions.push(cx.subscribe_in(
 2135                project,
 2136                window,
 2137                |editor, _, event, window, cx| match event {
 2138                    project::Event::RefreshCodeLens => {
 2139                        // we always query lens with actions, without storing them, always refreshing them
 2140                    }
 2141                    project::Event::RefreshInlayHints {
 2142                        server_id,
 2143                        request_id,
 2144                    } => {
 2145                        editor.refresh_inlay_hints(
 2146                            InlayHintRefreshReason::RefreshRequested {
 2147                                server_id: *server_id,
 2148                                request_id: *request_id,
 2149                            },
 2150                            cx,
 2151                        );
 2152                    }
 2153                    project::Event::RefreshSemanticTokens {
 2154                        server_id,
 2155                        request_id,
 2156                    } => {
 2157                        editor.refresh_semantic_tokens(
 2158                            None,
 2159                            Some(RefreshForServer {
 2160                                server_id: *server_id,
 2161                                request_id: *request_id,
 2162                            }),
 2163                            cx,
 2164                        );
 2165                    }
 2166                    project::Event::LanguageServerRemoved(_) => {
 2167                        editor.registered_buffers.clear();
 2168                        editor.register_visible_buffers(cx);
 2169                        editor.invalidate_semantic_tokens(None);
 2170                        editor.update_lsp_data(None, window, cx);
 2171                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2172                        if editor.tasks_update_task.is_none() {
 2173                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2174                        }
 2175                    }
 2176                    project::Event::LanguageServerAdded(..) => {
 2177                        if editor.tasks_update_task.is_none() {
 2178                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2179                        }
 2180                    }
 2181                    project::Event::SnippetEdit(id, snippet_edits) => {
 2182                        // todo(lw): Non singletons
 2183                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2184                            let snapshot = buffer.read(cx).snapshot();
 2185                            let focus_handle = editor.focus_handle(cx);
 2186                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2187                                for (range, snippet) in snippet_edits {
 2188                                    let buffer_range =
 2189                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2190                                    editor
 2191                                        .insert_snippet(
 2192                                            &[MultiBufferOffset(buffer_range.start)
 2193                                                ..MultiBufferOffset(buffer_range.end)],
 2194                                            snippet.clone(),
 2195                                            window,
 2196                                            cx,
 2197                                        )
 2198                                        .ok();
 2199                                }
 2200                            }
 2201                        }
 2202                    }
 2203                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2204                        let buffer_id = *buffer_id;
 2205                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2206                            editor.register_buffer(buffer_id, cx);
 2207                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2208                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2209                            refresh_linked_ranges(editor, window, cx);
 2210                            editor.refresh_code_actions(window, cx);
 2211                            editor.refresh_document_highlights(cx);
 2212                        }
 2213                    }
 2214
 2215                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2216                        let Some(workspace) = editor.workspace() else {
 2217                            return;
 2218                        };
 2219                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2220                        else {
 2221                            return;
 2222                        };
 2223
 2224                        if active_editor.entity_id() == cx.entity_id() {
 2225                            let entity_id = cx.entity_id();
 2226                            workspace.update(cx, |this, cx| {
 2227                                this.panes_mut()
 2228                                    .iter_mut()
 2229                                    .filter(|pane| pane.entity_id() != entity_id)
 2230                                    .for_each(|p| {
 2231                                        p.update(cx, |pane, _| {
 2232                                            pane.nav_history_mut().rename_item(
 2233                                                entity_id,
 2234                                                project_path.clone(),
 2235                                                abs_path.clone().into(),
 2236                                            );
 2237                                        })
 2238                                    });
 2239                            });
 2240
 2241                            Self::open_transaction_for_hidden_buffers(
 2242                                workspace,
 2243                                transaction.clone(),
 2244                                "Rename".to_string(),
 2245                                window,
 2246                                cx,
 2247                            );
 2248                        }
 2249                    }
 2250
 2251                    project::Event::WorkspaceEditApplied(transaction) => {
 2252                        let Some(workspace) = editor.workspace() else {
 2253                            return;
 2254                        };
 2255                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2256                        else {
 2257                            return;
 2258                        };
 2259
 2260                        if active_editor.entity_id() == cx.entity_id() {
 2261                            Self::open_transaction_for_hidden_buffers(
 2262                                workspace,
 2263                                transaction.clone(),
 2264                                "LSP Edit".to_string(),
 2265                                window,
 2266                                cx,
 2267                            );
 2268                        }
 2269                    }
 2270
 2271                    _ => {}
 2272                },
 2273            ));
 2274            if let Some(task_inventory) = project
 2275                .read(cx)
 2276                .task_store()
 2277                .read(cx)
 2278                .task_inventory()
 2279                .cloned()
 2280            {
 2281                project_subscriptions.push(cx.observe_in(
 2282                    &task_inventory,
 2283                    window,
 2284                    |editor, _, window, cx| {
 2285                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2286                    },
 2287                ));
 2288            };
 2289
 2290            project_subscriptions.push(cx.subscribe_in(
 2291                &project.read(cx).breakpoint_store(),
 2292                window,
 2293                |editor, _, event, window, cx| match event {
 2294                    BreakpointStoreEvent::ClearDebugLines => {
 2295                        editor.clear_row_highlights::<ActiveDebugLine>();
 2296                        editor.refresh_inline_values(cx);
 2297                    }
 2298                    BreakpointStoreEvent::SetDebugLine => {
 2299                        if editor.go_to_active_debug_line(window, cx) {
 2300                            cx.stop_propagation();
 2301                        }
 2302
 2303                        editor.refresh_inline_values(cx);
 2304                    }
 2305                    _ => {}
 2306                },
 2307            ));
 2308            let git_store = project.read(cx).git_store().clone();
 2309            let project = project.clone();
 2310            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2311                if let GitStoreEvent::RepositoryAdded = event {
 2312                    this.load_diff_task = Some(
 2313                        update_uncommitted_diff_for_buffer(
 2314                            cx.entity(),
 2315                            &project,
 2316                            this.buffer.read(cx).all_buffers(),
 2317                            this.buffer.clone(),
 2318                            cx,
 2319                        )
 2320                        .shared(),
 2321                    );
 2322                }
 2323            }));
 2324        }
 2325
 2326        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2327
 2328        let inlay_hint_settings =
 2329            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2330        let focus_handle = cx.focus_handle();
 2331        if !is_minimap {
 2332            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2333                .detach();
 2334            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2335                .detach();
 2336            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2337                .detach();
 2338            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2339                .detach();
 2340            cx.observe_pending_input(window, Self::observe_pending_input)
 2341                .detach();
 2342        }
 2343
 2344        let show_indent_guides =
 2345            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2346                Some(false)
 2347            } else {
 2348                None
 2349            };
 2350
 2351        let breakpoint_store = match (&mode, project.as_ref()) {
 2352            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2353            _ => None,
 2354        };
 2355
 2356        let mut code_action_providers = Vec::new();
 2357        let mut load_uncommitted_diff = None;
 2358        if let Some(project) = project.clone() {
 2359            load_uncommitted_diff = Some(
 2360                update_uncommitted_diff_for_buffer(
 2361                    cx.entity(),
 2362                    &project,
 2363                    multi_buffer.read(cx).all_buffers(),
 2364                    multi_buffer.clone(),
 2365                    cx,
 2366                )
 2367                .shared(),
 2368            );
 2369            code_action_providers.push(Rc::new(project) as Rc<_>);
 2370        }
 2371
 2372        let mut editor = Self {
 2373            focus_handle,
 2374            show_cursor_when_unfocused: false,
 2375            last_focused_descendant: None,
 2376            buffer: multi_buffer.clone(),
 2377            display_map: display_map.clone(),
 2378            placeholder_display_map: None,
 2379            selections,
 2380            scroll_manager: ScrollManager::new(cx),
 2381            columnar_selection_state: None,
 2382            add_selections_state: None,
 2383            select_next_state: None,
 2384            select_prev_state: None,
 2385            selection_history: SelectionHistory::default(),
 2386            defer_selection_effects: false,
 2387            deferred_selection_effects_state: None,
 2388            autoclose_regions: Vec::new(),
 2389            snippet_stack: InvalidationStack::default(),
 2390            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2391            ime_transaction: None,
 2392            active_diagnostics: ActiveDiagnostic::None,
 2393            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2394            inline_diagnostics_update: Task::ready(()),
 2395            inline_diagnostics: Vec::new(),
 2396            soft_wrap_mode_override,
 2397            diagnostics_max_severity,
 2398            hard_wrap: None,
 2399            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2400            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2401            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2402            project,
 2403            blink_manager: blink_manager.clone(),
 2404            show_local_selections: true,
 2405            show_scrollbars: ScrollbarAxes {
 2406                horizontal: full_mode,
 2407                vertical: full_mode,
 2408            },
 2409            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2410            offset_content: !matches!(mode, EditorMode::SingleLine),
 2411            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2412            show_gutter: full_mode,
 2413            show_line_numbers: (!full_mode).then_some(false),
 2414            use_relative_line_numbers: None,
 2415            disable_expand_excerpt_buttons: !full_mode,
 2416            delegate_expand_excerpts: false,
 2417            delegate_stage_and_restore: false,
 2418            delegate_open_excerpts: false,
 2419            enable_lsp_data: true,
 2420            enable_runnables: true,
 2421            show_git_diff_gutter: None,
 2422            show_code_actions: None,
 2423            show_runnables: None,
 2424            show_breakpoints: None,
 2425            show_diff_review_button: false,
 2426            show_wrap_guides: None,
 2427            show_indent_guides,
 2428            buffers_with_disabled_indent_guides: HashSet::default(),
 2429            highlight_order: 0,
 2430            highlighted_rows: HashMap::default(),
 2431            background_highlights: HashMap::default(),
 2432            gutter_highlights: HashMap::default(),
 2433            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2434            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2435            nav_history: None,
 2436            context_menu: RefCell::new(None),
 2437            context_menu_options: None,
 2438            mouse_context_menu: None,
 2439            completion_tasks: Vec::new(),
 2440            inline_blame_popover: None,
 2441            inline_blame_popover_show_task: None,
 2442            signature_help_state: SignatureHelpState::default(),
 2443            auto_signature_help: None,
 2444            find_all_references_task_sources: Vec::new(),
 2445            next_completion_id: 0,
 2446            next_inlay_id: 0,
 2447            code_action_providers,
 2448            available_code_actions: None,
 2449            code_actions_task: None,
 2450            quick_selection_highlight_task: None,
 2451            debounced_selection_highlight_task: None,
 2452            debounced_selection_highlight_complete: false,
 2453            document_highlights_task: None,
 2454            linked_editing_range_task: None,
 2455            pending_rename: None,
 2456            searchable: !is_minimap,
 2457            cursor_shape: EditorSettings::get_global(cx)
 2458                .cursor_shape
 2459                .unwrap_or_default(),
 2460            cursor_offset_on_selection: false,
 2461            current_line_highlight: None,
 2462            autoindent_mode: Some(AutoindentMode::EachLine),
 2463            collapse_matches: false,
 2464            workspace: None,
 2465            input_enabled: !is_minimap,
 2466            use_modal_editing: full_mode,
 2467            read_only: is_minimap,
 2468            use_autoclose: true,
 2469            use_auto_surround: true,
 2470            auto_replace_emoji_shortcode: false,
 2471            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2472            leader_id: None,
 2473            remote_id: None,
 2474            hover_state: HoverState::default(),
 2475            pending_mouse_down: None,
 2476            prev_pressure_stage: None,
 2477            hovered_link_state: None,
 2478            edit_prediction_provider: None,
 2479            active_edit_prediction: None,
 2480            stale_edit_prediction_in_menu: None,
 2481            edit_prediction_preview: EditPredictionPreview::Inactive {
 2482                released_too_fast: false,
 2483            },
 2484            inline_diagnostics_enabled: full_mode,
 2485            diagnostics_enabled: full_mode,
 2486            word_completions_enabled: full_mode,
 2487            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2488            gutter_hovered: false,
 2489            pixel_position_of_newest_cursor: None,
 2490            last_bounds: None,
 2491            last_position_map: None,
 2492            expect_bounds_change: None,
 2493            gutter_dimensions: GutterDimensions::default(),
 2494            style: None,
 2495            show_cursor_names: false,
 2496            hovered_cursors: HashMap::default(),
 2497            next_editor_action_id: EditorActionId::default(),
 2498            editor_actions: Rc::default(),
 2499            edit_predictions_hidden_for_vim_mode: false,
 2500            show_edit_predictions_override: None,
 2501            show_completions_on_input_override: None,
 2502            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2503            edit_prediction_settings: EditPredictionSettings::Disabled,
 2504            edit_prediction_indent_conflict: false,
 2505            edit_prediction_requires_modifier_in_indent_conflict: true,
 2506            custom_context_menu: None,
 2507            show_git_blame_gutter: false,
 2508            show_git_blame_inline: false,
 2509            show_selection_menu: None,
 2510            show_git_blame_inline_delay_task: None,
 2511            git_blame_inline_enabled: full_mode
 2512                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2513            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2514            buffer_serialization: is_minimap.not().then(|| {
 2515                BufferSerialization::new(
 2516                    ProjectSettings::get_global(cx)
 2517                        .session
 2518                        .restore_unsaved_buffers,
 2519                )
 2520            }),
 2521            blame: None,
 2522            blame_subscription: None,
 2523            tasks: BTreeMap::default(),
 2524
 2525            breakpoint_store,
 2526            gutter_breakpoint_indicator: (None, None),
 2527            gutter_diff_review_indicator: (None, None),
 2528            diff_review_drag_state: None,
 2529            diff_review_overlays: Vec::new(),
 2530            stored_review_comments: Vec::new(),
 2531            next_review_comment_id: 0,
 2532            hovered_diff_hunk_row: None,
 2533            _subscriptions: (!is_minimap)
 2534                .then(|| {
 2535                    vec![
 2536                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2537                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2538                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2539                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2540                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2541                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2542                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2543                        cx.observe_window_activation(window, |editor, window, cx| {
 2544                            let active = window.is_window_active();
 2545                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2546                                if active {
 2547                                    blink_manager.enable(cx);
 2548                                } else {
 2549                                    blink_manager.disable(cx);
 2550                                }
 2551                            });
 2552                            if active {
 2553                                editor.show_mouse_cursor(cx);
 2554                            }
 2555                        }),
 2556                    ]
 2557                })
 2558                .unwrap_or_default(),
 2559            tasks_update_task: None,
 2560            pull_diagnostics_task: Task::ready(()),
 2561            colors: None,
 2562            refresh_colors_task: Task::ready(()),
 2563            use_document_folding_ranges: false,
 2564            refresh_folding_ranges_task: Task::ready(()),
 2565            inlay_hints: None,
 2566            next_color_inlay_id: 0,
 2567            post_scroll_update: Task::ready(()),
 2568            linked_edit_ranges: Default::default(),
 2569            in_project_search: false,
 2570            previous_search_ranges: None,
 2571            breadcrumb_header: None,
 2572            focused_block: None,
 2573            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2574            addons: HashMap::default(),
 2575            registered_buffers: HashMap::default(),
 2576            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2577            selection_mark_mode: false,
 2578            toggle_fold_multiple_buffers: Task::ready(()),
 2579            serialize_selections: Task::ready(()),
 2580            serialize_folds: Task::ready(()),
 2581            text_style_refinement: None,
 2582            load_diff_task: load_uncommitted_diff,
 2583            temporary_diff_override: false,
 2584            mouse_cursor_hidden: false,
 2585            minimap: None,
 2586            hide_mouse_mode: EditorSettings::get_global(cx)
 2587                .hide_mouse
 2588                .unwrap_or_default(),
 2589            change_list: ChangeList::new(),
 2590            mode,
 2591            selection_drag_state: SelectionDragState::None,
 2592            folding_newlines: Task::ready(()),
 2593            lookup_key: None,
 2594            select_next_is_case_sensitive: None,
 2595            on_local_selections_changed: None,
 2596            suppress_selection_callback: false,
 2597            applicable_language_settings: HashMap::default(),
 2598            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2599            accent_data: None,
 2600            fetched_tree_sitter_chunks: HashMap::default(),
 2601            number_deleted_lines: false,
 2602            refresh_matching_bracket_highlights_task: Task::ready(()),
 2603            refresh_document_symbols_task: Task::ready(()).shared(),
 2604            lsp_document_symbols: HashMap::default(),
 2605            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2606            outline_symbols_at_cursor: None,
 2607            sticky_headers_task: Task::ready(()),
 2608            sticky_headers: None,
 2609        };
 2610
 2611        if is_minimap {
 2612            return editor;
 2613        }
 2614
 2615        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2616        editor.accent_data = editor.fetch_accent_data(cx);
 2617
 2618        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2619            editor
 2620                ._subscriptions
 2621                .push(cx.observe(breakpoints, |_, _, cx| {
 2622                    cx.notify();
 2623                }));
 2624        }
 2625        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2626        editor._subscriptions.extend(project_subscriptions);
 2627
 2628        editor._subscriptions.push(cx.subscribe_in(
 2629            &cx.entity(),
 2630            window,
 2631            |editor, _, e: &EditorEvent, window, cx| match e {
 2632                EditorEvent::ScrollPositionChanged { local, .. } => {
 2633                    if *local {
 2634                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2635                        editor.inline_blame_popover.take();
 2636                        let snapshot = editor.snapshot(window, cx);
 2637                        let new_anchor = editor
 2638                            .scroll_manager
 2639                            .native_anchor(&snapshot.display_snapshot, cx);
 2640                        editor.update_restoration_data(cx, move |data| {
 2641                            data.scroll_position = (
 2642                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2643                                new_anchor.offset,
 2644                            );
 2645                        });
 2646
 2647                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2648                            cx.background_executor()
 2649                                .timer(Duration::from_millis(50))
 2650                                .await;
 2651                            editor
 2652                                .update_in(cx, |editor, window, cx| {
 2653                                    editor.register_visible_buffers(cx);
 2654                                    editor.colorize_brackets(false, cx);
 2655                                    editor.refresh_inlay_hints(
 2656                                        InlayHintRefreshReason::NewLinesShown,
 2657                                        cx,
 2658                                    );
 2659                                    if !editor.buffer().read(cx).is_singleton() {
 2660                                        editor.update_lsp_data(None, window, cx);
 2661                                    }
 2662                                })
 2663                                .ok();
 2664                        });
 2665                    }
 2666                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2667                }
 2668                EditorEvent::Edited { .. } => {
 2669                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2670                        .map(|vim_mode| vim_mode.0)
 2671                        .unwrap_or(false);
 2672                    if !vim_mode {
 2673                        let display_map = editor.display_snapshot(cx);
 2674                        let selections = editor.selections.all_adjusted_display(&display_map);
 2675                        let pop_state = editor
 2676                            .change_list
 2677                            .last()
 2678                            .map(|previous| {
 2679                                previous.len() == selections.len()
 2680                                    && previous.iter().enumerate().all(|(ix, p)| {
 2681                                        p.to_display_point(&display_map).row()
 2682                                            == selections[ix].head().row()
 2683                                    })
 2684                            })
 2685                            .unwrap_or(false);
 2686                        let new_positions = selections
 2687                            .into_iter()
 2688                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2689                            .collect();
 2690                        editor
 2691                            .change_list
 2692                            .push_to_change_list(pop_state, new_positions);
 2693                    }
 2694                }
 2695                _ => (),
 2696            },
 2697        ));
 2698
 2699        if let Some(dap_store) = editor
 2700            .project
 2701            .as_ref()
 2702            .map(|project| project.read(cx).dap_store())
 2703        {
 2704            let weak_editor = cx.weak_entity();
 2705
 2706            editor
 2707                ._subscriptions
 2708                .push(
 2709                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2710                        let session_entity = cx.entity();
 2711                        weak_editor
 2712                            .update(cx, |editor, cx| {
 2713                                editor._subscriptions.push(
 2714                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2715                                );
 2716                            })
 2717                            .ok();
 2718                    }),
 2719                );
 2720
 2721            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2722                editor
 2723                    ._subscriptions
 2724                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2725            }
 2726        }
 2727
 2728        // skip adding the initial selection to selection history
 2729        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2730        editor.end_selection(window, cx);
 2731        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2732
 2733        editor.scroll_manager.show_scrollbars(window, cx);
 2734        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2735
 2736        if full_mode {
 2737            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2738            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2739
 2740            if editor.git_blame_inline_enabled {
 2741                editor.start_git_blame_inline(false, window, cx);
 2742            }
 2743
 2744            editor.go_to_active_debug_line(window, cx);
 2745
 2746            editor.minimap =
 2747                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2748            editor.colors = Some(LspColorData::new(cx));
 2749            editor.use_document_folding_ranges = true;
 2750            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2751
 2752            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2753                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2754            }
 2755            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2756        }
 2757
 2758        editor
 2759    }
 2760
 2761    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2762        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2763    }
 2764
 2765    pub fn deploy_mouse_context_menu(
 2766        &mut self,
 2767        position: gpui::Point<Pixels>,
 2768        context_menu: Entity<ContextMenu>,
 2769        window: &mut Window,
 2770        cx: &mut Context<Self>,
 2771    ) {
 2772        self.mouse_context_menu = Some(MouseContextMenu::new(
 2773            self,
 2774            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2775            context_menu,
 2776            window,
 2777            cx,
 2778        ));
 2779    }
 2780
 2781    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2782        self.mouse_context_menu
 2783            .as_ref()
 2784            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2785    }
 2786
 2787    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2788        if self
 2789            .selections
 2790            .pending_anchor()
 2791            .is_some_and(|pending_selection| {
 2792                let snapshot = self.buffer().read(cx).snapshot(cx);
 2793                pending_selection.range().includes(range, &snapshot)
 2794            })
 2795        {
 2796            return true;
 2797        }
 2798
 2799        self.selections
 2800            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2801            .into_iter()
 2802            .any(|selection| {
 2803                // This is needed to cover a corner case, if we just check for an existing
 2804                // selection in the fold range, having a cursor at the start of the fold
 2805                // marks it as selected. Non-empty selections don't cause this.
 2806                let length = selection.end - selection.start;
 2807                length > 0
 2808            })
 2809    }
 2810
 2811    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2812        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2813    }
 2814
 2815    fn key_context_internal(
 2816        &self,
 2817        has_active_edit_prediction: bool,
 2818        window: &mut Window,
 2819        cx: &mut App,
 2820    ) -> KeyContext {
 2821        let mut key_context = KeyContext::new_with_defaults();
 2822        key_context.add("Editor");
 2823        let mode = match self.mode {
 2824            EditorMode::SingleLine => "single_line",
 2825            EditorMode::AutoHeight { .. } => "auto_height",
 2826            EditorMode::Minimap { .. } => "minimap",
 2827            EditorMode::Full { .. } => "full",
 2828        };
 2829
 2830        if EditorSettings::jupyter_enabled(cx) {
 2831            key_context.add("jupyter");
 2832        }
 2833
 2834        key_context.set("mode", mode);
 2835        if self.pending_rename.is_some() {
 2836            key_context.add("renaming");
 2837        }
 2838
 2839        if let Some(snippet_stack) = self.snippet_stack.last() {
 2840            key_context.add("in_snippet");
 2841
 2842            if snippet_stack.active_index > 0 {
 2843                key_context.add("has_previous_tabstop");
 2844            }
 2845
 2846            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2847                key_context.add("has_next_tabstop");
 2848            }
 2849        }
 2850
 2851        match self.context_menu.borrow().as_ref() {
 2852            Some(CodeContextMenu::Completions(menu)) => {
 2853                if menu.visible() {
 2854                    key_context.add("menu");
 2855                    key_context.add("showing_completions");
 2856                }
 2857            }
 2858            Some(CodeContextMenu::CodeActions(menu)) => {
 2859                if menu.visible() {
 2860                    key_context.add("menu");
 2861                    key_context.add("showing_code_actions")
 2862                }
 2863            }
 2864            None => {}
 2865        }
 2866
 2867        if self.signature_help_state.has_multiple_signatures() {
 2868            key_context.add("showing_signature_help");
 2869        }
 2870
 2871        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2872        if !self.focus_handle(cx).contains_focused(window, cx)
 2873            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2874        {
 2875            for addon in self.addons.values() {
 2876                addon.extend_key_context(&mut key_context, cx)
 2877            }
 2878        }
 2879
 2880        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2881            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2882                Some(
 2883                    file.full_path(cx)
 2884                        .extension()?
 2885                        .to_string_lossy()
 2886                        .to_lowercase(),
 2887                )
 2888            }) {
 2889                key_context.set("extension", extension);
 2890            }
 2891        } else {
 2892            key_context.add("multibuffer");
 2893        }
 2894
 2895        if has_active_edit_prediction {
 2896            if self.edit_prediction_in_conflict() {
 2897                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2898            } else {
 2899                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2900                key_context.add("copilot_suggestion");
 2901            }
 2902        }
 2903
 2904        if self.selection_mark_mode {
 2905            key_context.add("selection_mode");
 2906        }
 2907
 2908        let disjoint = self.selections.disjoint_anchors();
 2909        let snapshot = self.snapshot(window, cx);
 2910        let snapshot = snapshot.buffer_snapshot();
 2911        if self.mode == EditorMode::SingleLine
 2912            && let [selection] = disjoint
 2913            && selection.start == selection.end
 2914            && selection.end.to_offset(snapshot) == snapshot.len()
 2915        {
 2916            key_context.add("end_of_input");
 2917        }
 2918
 2919        if self.has_any_expanded_diff_hunks(cx) {
 2920            key_context.add("diffs_expanded");
 2921        }
 2922
 2923        key_context
 2924    }
 2925
 2926    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2927        self.last_bounds.as_ref()
 2928    }
 2929
 2930    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2931        if self.mouse_cursor_hidden {
 2932            self.mouse_cursor_hidden = false;
 2933            cx.notify();
 2934        }
 2935    }
 2936
 2937    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2938        let hide_mouse_cursor = match origin {
 2939            HideMouseCursorOrigin::TypingAction => {
 2940                matches!(
 2941                    self.hide_mouse_mode,
 2942                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2943                )
 2944            }
 2945            HideMouseCursorOrigin::MovementAction => {
 2946                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2947            }
 2948        };
 2949        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2950            self.mouse_cursor_hidden = hide_mouse_cursor;
 2951            cx.notify();
 2952        }
 2953    }
 2954
 2955    pub fn edit_prediction_in_conflict(&self) -> bool {
 2956        if !self.show_edit_predictions_in_menu() {
 2957            return false;
 2958        }
 2959
 2960        let showing_completions = self
 2961            .context_menu
 2962            .borrow()
 2963            .as_ref()
 2964            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2965
 2966        showing_completions
 2967            || self.edit_prediction_requires_modifier()
 2968            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2969            // bindings to insert tab characters.
 2970            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2971    }
 2972
 2973    pub fn accept_edit_prediction_keybind(
 2974        &self,
 2975        granularity: EditPredictionGranularity,
 2976        window: &mut Window,
 2977        cx: &mut App,
 2978    ) -> AcceptEditPredictionBinding {
 2979        let key_context = self.key_context_internal(true, window, cx);
 2980        let in_conflict = self.edit_prediction_in_conflict();
 2981
 2982        let bindings =
 2983            match granularity {
 2984                EditPredictionGranularity::Word => window
 2985                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2986                EditPredictionGranularity::Line => window
 2987                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2988                EditPredictionGranularity::Full => {
 2989                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2990                }
 2991            };
 2992
 2993        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2994            !in_conflict
 2995                || binding
 2996                    .keystrokes()
 2997                    .first()
 2998                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2999        }))
 3000    }
 3001
 3002    pub fn new_file(
 3003        workspace: &mut Workspace,
 3004        _: &workspace::NewFile,
 3005        window: &mut Window,
 3006        cx: &mut Context<Workspace>,
 3007    ) {
 3008        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3009            "Failed to create buffer",
 3010            window,
 3011            cx,
 3012            |e, _, _| match e.error_code() {
 3013                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3014                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3015                e.error_tag("required").unwrap_or("the latest version")
 3016            )),
 3017                _ => None,
 3018            },
 3019        );
 3020    }
 3021
 3022    pub fn new_in_workspace(
 3023        workspace: &mut Workspace,
 3024        window: &mut Window,
 3025        cx: &mut Context<Workspace>,
 3026    ) -> Task<Result<Entity<Editor>>> {
 3027        let project = workspace.project().clone();
 3028        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3029
 3030        cx.spawn_in(window, async move |workspace, cx| {
 3031            let buffer = create.await?;
 3032            workspace.update_in(cx, |workspace, window, cx| {
 3033                let editor =
 3034                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3035                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3036                editor
 3037            })
 3038        })
 3039    }
 3040
 3041    fn new_file_vertical(
 3042        workspace: &mut Workspace,
 3043        _: &workspace::NewFileSplitVertical,
 3044        window: &mut Window,
 3045        cx: &mut Context<Workspace>,
 3046    ) {
 3047        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3048    }
 3049
 3050    fn new_file_horizontal(
 3051        workspace: &mut Workspace,
 3052        _: &workspace::NewFileSplitHorizontal,
 3053        window: &mut Window,
 3054        cx: &mut Context<Workspace>,
 3055    ) {
 3056        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3057    }
 3058
 3059    fn new_file_split(
 3060        workspace: &mut Workspace,
 3061        action: &workspace::NewFileSplit,
 3062        window: &mut Window,
 3063        cx: &mut Context<Workspace>,
 3064    ) {
 3065        Self::new_file_in_direction(workspace, action.0, window, cx)
 3066    }
 3067
 3068    fn new_file_in_direction(
 3069        workspace: &mut Workspace,
 3070        direction: SplitDirection,
 3071        window: &mut Window,
 3072        cx: &mut Context<Workspace>,
 3073    ) {
 3074        let project = workspace.project().clone();
 3075        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3076
 3077        cx.spawn_in(window, async move |workspace, cx| {
 3078            let buffer = create.await?;
 3079            workspace.update_in(cx, move |workspace, window, cx| {
 3080                workspace.split_item(
 3081                    direction,
 3082                    Box::new(
 3083                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3084                    ),
 3085                    window,
 3086                    cx,
 3087                )
 3088            })?;
 3089            anyhow::Ok(())
 3090        })
 3091        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3092            match e.error_code() {
 3093                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3094                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3095                e.error_tag("required").unwrap_or("the latest version")
 3096            )),
 3097                _ => None,
 3098            }
 3099        });
 3100    }
 3101
 3102    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3103        self.leader_id
 3104    }
 3105
 3106    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3107        &self.buffer
 3108    }
 3109
 3110    pub fn project(&self) -> Option<&Entity<Project>> {
 3111        self.project.as_ref()
 3112    }
 3113
 3114    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3115        self.workspace.as_ref()?.0.upgrade()
 3116    }
 3117
 3118    /// Detaches a task and shows an error notification in the workspace if available,
 3119    /// otherwise just logs the error.
 3120    pub fn detach_and_notify_err<R, E>(
 3121        &self,
 3122        task: Task<Result<R, E>>,
 3123        window: &mut Window,
 3124        cx: &mut App,
 3125    ) where
 3126        E: std::fmt::Debug + std::fmt::Display + 'static,
 3127        R: 'static,
 3128    {
 3129        if let Some(workspace) = self.workspace() {
 3130            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3131        } else {
 3132            task.detach_and_log_err(cx);
 3133        }
 3134    }
 3135
 3136    /// Returns the workspace serialization ID if this editor should be serialized.
 3137    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3138        self.workspace
 3139            .as_ref()
 3140            .filter(|_| self.should_serialize_buffer())
 3141            .and_then(|workspace| workspace.1)
 3142    }
 3143
 3144    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3145        self.buffer().read(cx).title(cx)
 3146    }
 3147
 3148    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3149        let git_blame_gutter_max_author_length = self
 3150            .render_git_blame_gutter(cx)
 3151            .then(|| {
 3152                if let Some(blame) = self.blame.as_ref() {
 3153                    let max_author_length =
 3154                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3155                    Some(max_author_length)
 3156                } else {
 3157                    None
 3158                }
 3159            })
 3160            .flatten();
 3161
 3162        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3163
 3164        EditorSnapshot {
 3165            mode: self.mode.clone(),
 3166            show_gutter: self.show_gutter,
 3167            offset_content: self.offset_content,
 3168            show_line_numbers: self.show_line_numbers,
 3169            number_deleted_lines: self.number_deleted_lines,
 3170            show_git_diff_gutter: self.show_git_diff_gutter,
 3171            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3172            show_code_actions: self.show_code_actions,
 3173            show_runnables: self.show_runnables,
 3174            show_breakpoints: self.show_breakpoints,
 3175            git_blame_gutter_max_author_length,
 3176            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3177            display_snapshot,
 3178            placeholder_display_snapshot: self
 3179                .placeholder_display_map
 3180                .as_ref()
 3181                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3182            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3183            is_focused: self.focus_handle.is_focused(window),
 3184            current_line_highlight: self
 3185                .current_line_highlight
 3186                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3187            gutter_hovered: self.gutter_hovered,
 3188        }
 3189    }
 3190
 3191    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3192        self.buffer.read(cx).language_at(point, cx)
 3193    }
 3194
 3195    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3196        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3197    }
 3198
 3199    pub fn active_excerpt(
 3200        &self,
 3201        cx: &App,
 3202    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3203        self.buffer
 3204            .read(cx)
 3205            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3206    }
 3207
 3208    pub fn mode(&self) -> &EditorMode {
 3209        &self.mode
 3210    }
 3211
 3212    pub fn set_mode(&mut self, mode: EditorMode) {
 3213        self.mode = mode;
 3214    }
 3215
 3216    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3217        self.collaboration_hub.as_deref()
 3218    }
 3219
 3220    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3221        self.collaboration_hub = Some(hub);
 3222    }
 3223
 3224    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3225        self.in_project_search = in_project_search;
 3226    }
 3227
 3228    pub fn set_custom_context_menu(
 3229        &mut self,
 3230        f: impl 'static
 3231        + Fn(
 3232            &mut Self,
 3233            DisplayPoint,
 3234            &mut Window,
 3235            &mut Context<Self>,
 3236        ) -> Option<Entity<ui::ContextMenu>>,
 3237    ) {
 3238        self.custom_context_menu = Some(Box::new(f))
 3239    }
 3240
 3241    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3242        self.completion_provider = provider;
 3243    }
 3244
 3245    #[cfg(any(test, feature = "test-support"))]
 3246    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3247        self.completion_provider.clone()
 3248    }
 3249
 3250    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3251        self.semantics_provider.clone()
 3252    }
 3253
 3254    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3255        self.semantics_provider = provider;
 3256    }
 3257
 3258    pub fn set_edit_prediction_provider<T>(
 3259        &mut self,
 3260        provider: Option<Entity<T>>,
 3261        window: &mut Window,
 3262        cx: &mut Context<Self>,
 3263    ) where
 3264        T: EditPredictionDelegate,
 3265    {
 3266        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3267            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3268                if this.focus_handle.is_focused(window) {
 3269                    this.update_visible_edit_prediction(window, cx);
 3270                }
 3271            }),
 3272            provider: Arc::new(provider),
 3273        });
 3274        self.update_edit_prediction_settings(cx);
 3275        self.refresh_edit_prediction(false, false, window, cx);
 3276    }
 3277
 3278    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3279        self.placeholder_display_map
 3280            .as_ref()
 3281            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3282    }
 3283
 3284    pub fn set_placeholder_text(
 3285        &mut self,
 3286        placeholder_text: &str,
 3287        window: &mut Window,
 3288        cx: &mut Context<Self>,
 3289    ) {
 3290        let multibuffer = cx
 3291            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3292
 3293        let style = window.text_style();
 3294
 3295        self.placeholder_display_map = Some(cx.new(|cx| {
 3296            DisplayMap::new(
 3297                multibuffer,
 3298                style.font(),
 3299                style.font_size.to_pixels(window.rem_size()),
 3300                None,
 3301                FILE_HEADER_HEIGHT,
 3302                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3303                Default::default(),
 3304                DiagnosticSeverity::Off,
 3305                cx,
 3306            )
 3307        }));
 3308        cx.notify();
 3309    }
 3310
 3311    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3312        self.cursor_shape = cursor_shape;
 3313
 3314        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3315        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3316
 3317        cx.notify();
 3318    }
 3319
 3320    pub fn cursor_shape(&self) -> CursorShape {
 3321        self.cursor_shape
 3322    }
 3323
 3324    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3325        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3326    }
 3327
 3328    pub fn set_current_line_highlight(
 3329        &mut self,
 3330        current_line_highlight: Option<CurrentLineHighlight>,
 3331    ) {
 3332        self.current_line_highlight = current_line_highlight;
 3333    }
 3334
 3335    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3336        self.collapse_matches = collapse_matches;
 3337    }
 3338
 3339    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3340        if self.collapse_matches {
 3341            return range.start..range.start;
 3342        }
 3343        range.clone()
 3344    }
 3345
 3346    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3347        self.display_map.read(cx).clip_at_line_ends
 3348    }
 3349
 3350    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3351        if self.display_map.read(cx).clip_at_line_ends != clip {
 3352            self.display_map
 3353                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3354        }
 3355    }
 3356
 3357    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3358        self.input_enabled = input_enabled;
 3359    }
 3360
 3361    pub fn set_edit_predictions_hidden_for_vim_mode(
 3362        &mut self,
 3363        hidden: bool,
 3364        window: &mut Window,
 3365        cx: &mut Context<Self>,
 3366    ) {
 3367        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3368            self.edit_predictions_hidden_for_vim_mode = hidden;
 3369            if hidden {
 3370                self.update_visible_edit_prediction(window, cx);
 3371            } else {
 3372                self.refresh_edit_prediction(true, false, window, cx);
 3373            }
 3374        }
 3375    }
 3376
 3377    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3378        self.menu_edit_predictions_policy = value;
 3379    }
 3380
 3381    pub fn set_autoindent(&mut self, autoindent: bool) {
 3382        if autoindent {
 3383            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3384        } else {
 3385            self.autoindent_mode = None;
 3386        }
 3387    }
 3388
 3389    pub fn capability(&self, cx: &App) -> Capability {
 3390        if self.read_only {
 3391            Capability::ReadOnly
 3392        } else {
 3393            self.buffer.read(cx).capability()
 3394        }
 3395    }
 3396
 3397    pub fn read_only(&self, cx: &App) -> bool {
 3398        self.read_only || self.buffer.read(cx).read_only()
 3399    }
 3400
 3401    pub fn set_read_only(&mut self, read_only: bool) {
 3402        self.read_only = read_only;
 3403    }
 3404
 3405    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3406        self.use_autoclose = autoclose;
 3407    }
 3408
 3409    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3410        self.use_auto_surround = auto_surround;
 3411    }
 3412
 3413    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3414        self.auto_replace_emoji_shortcode = auto_replace;
 3415    }
 3416
 3417    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3418        self.buffer_serialization = should_serialize.then(|| {
 3419            BufferSerialization::new(
 3420                ProjectSettings::get_global(cx)
 3421                    .session
 3422                    .restore_unsaved_buffers,
 3423            )
 3424        })
 3425    }
 3426
 3427    fn should_serialize_buffer(&self) -> bool {
 3428        self.buffer_serialization.is_some()
 3429    }
 3430
 3431    pub fn toggle_edit_predictions(
 3432        &mut self,
 3433        _: &ToggleEditPrediction,
 3434        window: &mut Window,
 3435        cx: &mut Context<Self>,
 3436    ) {
 3437        if self.show_edit_predictions_override.is_some() {
 3438            self.set_show_edit_predictions(None, window, cx);
 3439        } else {
 3440            let show_edit_predictions = !self.edit_predictions_enabled();
 3441            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3442        }
 3443    }
 3444
 3445    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3446        self.show_completions_on_input_override = show_completions_on_input;
 3447    }
 3448
 3449    pub fn set_show_edit_predictions(
 3450        &mut self,
 3451        show_edit_predictions: Option<bool>,
 3452        window: &mut Window,
 3453        cx: &mut Context<Self>,
 3454    ) {
 3455        self.show_edit_predictions_override = show_edit_predictions;
 3456        self.update_edit_prediction_settings(cx);
 3457
 3458        if let Some(false) = show_edit_predictions {
 3459            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3460        } else {
 3461            self.refresh_edit_prediction(false, true, window, cx);
 3462        }
 3463    }
 3464
 3465    fn edit_predictions_disabled_in_scope(
 3466        &self,
 3467        buffer: &Entity<Buffer>,
 3468        buffer_position: language::Anchor,
 3469        cx: &App,
 3470    ) -> bool {
 3471        let snapshot = buffer.read(cx).snapshot();
 3472        let settings = snapshot.settings_at(buffer_position, cx);
 3473
 3474        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3475            return false;
 3476        };
 3477
 3478        scope.override_name().is_some_and(|scope_name| {
 3479            settings
 3480                .edit_predictions_disabled_in
 3481                .iter()
 3482                .any(|s| s == scope_name)
 3483        })
 3484    }
 3485
 3486    pub fn set_use_modal_editing(&mut self, to: bool) {
 3487        self.use_modal_editing = to;
 3488    }
 3489
 3490    pub fn use_modal_editing(&self) -> bool {
 3491        self.use_modal_editing
 3492    }
 3493
 3494    fn selections_did_change(
 3495        &mut self,
 3496        local: bool,
 3497        old_cursor_position: &Anchor,
 3498        effects: SelectionEffects,
 3499        window: &mut Window,
 3500        cx: &mut Context<Self>,
 3501    ) {
 3502        window.invalidate_character_coordinates();
 3503
 3504        // Copy selections to primary selection buffer
 3505        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3506        if local {
 3507            let selections = self
 3508                .selections
 3509                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3510            let buffer_handle = self.buffer.read(cx).read(cx);
 3511
 3512            let mut text = String::new();
 3513            for (index, selection) in selections.iter().enumerate() {
 3514                let text_for_selection = buffer_handle
 3515                    .text_for_range(selection.start..selection.end)
 3516                    .collect::<String>();
 3517
 3518                text.push_str(&text_for_selection);
 3519                if index != selections.len() - 1 {
 3520                    text.push('\n');
 3521                }
 3522            }
 3523
 3524            if !text.is_empty() {
 3525                cx.write_to_primary(ClipboardItem::new_string(text));
 3526            }
 3527        }
 3528
 3529        let selection_anchors = self.selections.disjoint_anchors_arc();
 3530
 3531        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3532            self.buffer.update(cx, |buffer, cx| {
 3533                buffer.set_active_selections(
 3534                    &selection_anchors,
 3535                    self.selections.line_mode(),
 3536                    self.cursor_shape,
 3537                    cx,
 3538                )
 3539            });
 3540        }
 3541        let display_map = self
 3542            .display_map
 3543            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3544        let buffer = display_map.buffer_snapshot();
 3545        if self.selections.count() == 1 {
 3546            self.add_selections_state = None;
 3547        }
 3548        self.select_next_state = None;
 3549        self.select_prev_state = None;
 3550        self.select_syntax_node_history.try_clear();
 3551        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3552        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3553        self.take_rename(false, window, cx);
 3554
 3555        let newest_selection = self.selections.newest_anchor();
 3556        let new_cursor_position = newest_selection.head();
 3557        let selection_start = newest_selection.start;
 3558
 3559        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3560            self.push_to_nav_history(
 3561                *old_cursor_position,
 3562                Some(new_cursor_position.to_point(buffer)),
 3563                false,
 3564                effects.nav_history == Some(true),
 3565                cx,
 3566            );
 3567        }
 3568
 3569        if local {
 3570            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3571                self.register_buffer(buffer_id, cx);
 3572            }
 3573
 3574            let mut context_menu = self.context_menu.borrow_mut();
 3575            let completion_menu = match context_menu.as_ref() {
 3576                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3577                Some(CodeContextMenu::CodeActions(_)) => {
 3578                    *context_menu = None;
 3579                    None
 3580                }
 3581                None => None,
 3582            };
 3583            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3584            drop(context_menu);
 3585
 3586            if effects.completions
 3587                && let Some(completion_position) = completion_position
 3588            {
 3589                let start_offset = selection_start.to_offset(buffer);
 3590                let position_matches = start_offset == completion_position.to_offset(buffer);
 3591                let continue_showing = if let Some((snap, ..)) =
 3592                    buffer.point_to_buffer_offset(completion_position)
 3593                    && !snap.capability.editable()
 3594                {
 3595                    false
 3596                } else if position_matches {
 3597                    if self.snippet_stack.is_empty() {
 3598                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3599                            == Some(CharKind::Word)
 3600                    } else {
 3601                        // Snippet choices can be shown even when the cursor is in whitespace.
 3602                        // Dismissing the menu with actions like backspace is handled by
 3603                        // invalidation regions.
 3604                        true
 3605                    }
 3606                } else {
 3607                    false
 3608                };
 3609
 3610                if continue_showing {
 3611                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3612                } else {
 3613                    self.hide_context_menu(window, cx);
 3614                }
 3615            }
 3616
 3617            hide_hover(self, cx);
 3618
 3619            if old_cursor_position.to_display_point(&display_map).row()
 3620                != new_cursor_position.to_display_point(&display_map).row()
 3621            {
 3622                self.available_code_actions.take();
 3623            }
 3624            self.refresh_code_actions(window, cx);
 3625            self.refresh_document_highlights(cx);
 3626            refresh_linked_ranges(self, window, cx);
 3627
 3628            self.refresh_selected_text_highlights(false, window, cx);
 3629            self.refresh_matching_bracket_highlights(&display_map, cx);
 3630            self.refresh_outline_symbols_at_cursor(cx);
 3631            self.update_visible_edit_prediction(window, cx);
 3632            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3633            self.inline_blame_popover.take();
 3634            if self.git_blame_inline_enabled {
 3635                self.start_inline_blame_timer(window, cx);
 3636            }
 3637        }
 3638
 3639        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3640
 3641        if local && !self.suppress_selection_callback {
 3642            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3643                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3644                callback(cursor_position, window, cx);
 3645            }
 3646        }
 3647
 3648        cx.emit(EditorEvent::SelectionsChanged { local });
 3649
 3650        let selections = &self.selections.disjoint_anchors_arc();
 3651        if selections.len() == 1 {
 3652            cx.emit(SearchEvent::ActiveMatchChanged)
 3653        }
 3654        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3655            let inmemory_selections = selections
 3656                .iter()
 3657                .map(|s| {
 3658                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3659                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3660                })
 3661                .collect();
 3662            self.update_restoration_data(cx, |data| {
 3663                data.selections = inmemory_selections;
 3664            });
 3665
 3666            if WorkspaceSettings::get(None, cx).restore_on_startup
 3667                != RestoreOnStartupBehavior::EmptyTab
 3668                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3669            {
 3670                let snapshot = self.buffer().read(cx).snapshot(cx);
 3671                let selections = selections.clone();
 3672                let background_executor = cx.background_executor().clone();
 3673                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3674                self.serialize_selections = cx.background_spawn(async move {
 3675                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3676                    let db_selections = selections
 3677                        .iter()
 3678                        .map(|selection| {
 3679                            (
 3680                                selection.start.to_offset(&snapshot).0,
 3681                                selection.end.to_offset(&snapshot).0,
 3682                            )
 3683                        })
 3684                        .collect();
 3685
 3686                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3687                        .await
 3688                        .with_context(|| {
 3689                            format!(
 3690                                "persisting editor selections for editor {editor_id}, \
 3691                                workspace {workspace_id:?}"
 3692                            )
 3693                        })
 3694                        .log_err();
 3695                });
 3696            }
 3697        }
 3698
 3699        cx.notify();
 3700    }
 3701
 3702    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3703        use text::ToOffset as _;
 3704        use text::ToPoint as _;
 3705
 3706        if self.mode.is_minimap()
 3707            || WorkspaceSettings::get(None, cx).restore_on_startup
 3708                == RestoreOnStartupBehavior::EmptyTab
 3709        {
 3710            return;
 3711        }
 3712
 3713        if !self.buffer().read(cx).is_singleton() {
 3714            return;
 3715        }
 3716
 3717        let display_snapshot = self
 3718            .display_map
 3719            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3720        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3721            return;
 3722        };
 3723        let inmemory_folds = display_snapshot
 3724            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3725            .map(|fold| {
 3726                fold.range.start.text_anchor.to_point(&snapshot)
 3727                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3728            })
 3729            .collect();
 3730        self.update_restoration_data(cx, |data| {
 3731            data.folds = inmemory_folds;
 3732        });
 3733
 3734        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3735            return;
 3736        };
 3737
 3738        // Get file path for path-based fold storage (survives tab close)
 3739        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3740            project::File::from_dyn(buffer.read(cx).file())
 3741                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3742        }) else {
 3743            return;
 3744        };
 3745
 3746        let background_executor = cx.background_executor().clone();
 3747        const FINGERPRINT_LEN: usize = 32;
 3748        let db_folds = display_snapshot
 3749            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3750            .map(|fold| {
 3751                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3752                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3753
 3754                // Extract fingerprints - content at fold boundaries for validation on restore
 3755                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3756                // content that might change independently.
 3757                // start_fp: first min(32, fold_len) bytes of fold content
 3758                // end_fp: last min(32, fold_len) bytes of fold content
 3759                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3760                let fold_len = end - start;
 3761                let start_fp_end = snapshot
 3762                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3763                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3764                let end_fp_start = snapshot
 3765                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3766                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3767
 3768                (start, end, start_fp, end_fp)
 3769            })
 3770            .collect::<Vec<_>>();
 3771        self.serialize_folds = cx.background_spawn(async move {
 3772            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3773            if db_folds.is_empty() {
 3774                // No folds - delete any persisted folds for this file
 3775                DB.delete_file_folds(workspace_id, file_path)
 3776                    .await
 3777                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3778                    .log_err();
 3779            } else {
 3780                DB.save_file_folds(workspace_id, file_path, db_folds)
 3781                    .await
 3782                    .with_context(|| {
 3783                        format!("persisting file folds for workspace {workspace_id:?}")
 3784                    })
 3785                    .log_err();
 3786            }
 3787        });
 3788    }
 3789
 3790    pub fn sync_selections(
 3791        &mut self,
 3792        other: Entity<Editor>,
 3793        cx: &mut Context<Self>,
 3794    ) -> gpui::Subscription {
 3795        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3796        if !other_selections.is_empty() {
 3797            self.selections
 3798                .change_with(&self.display_snapshot(cx), |selections| {
 3799                    selections.select_anchors(other_selections);
 3800                });
 3801        }
 3802
 3803        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3804            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3805                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3806                if other_selections.is_empty() {
 3807                    return;
 3808                }
 3809                let snapshot = this.display_snapshot(cx);
 3810                this.selections.change_with(&snapshot, |selections| {
 3811                    selections.select_anchors(other_selections);
 3812                });
 3813            }
 3814        });
 3815
 3816        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3817            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3818                let these_selections = this.selections.disjoint_anchors().to_vec();
 3819                if these_selections.is_empty() {
 3820                    return;
 3821                }
 3822                other.update(cx, |other_editor, cx| {
 3823                    let snapshot = other_editor.display_snapshot(cx);
 3824                    other_editor
 3825                        .selections
 3826                        .change_with(&snapshot, |selections| {
 3827                            selections.select_anchors(these_selections);
 3828                        })
 3829                });
 3830            }
 3831        });
 3832
 3833        Subscription::join(other_subscription, this_subscription)
 3834    }
 3835
 3836    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3837        if self.buffer().read(cx).is_singleton() {
 3838            return;
 3839        }
 3840        let snapshot = self.buffer.read(cx).snapshot(cx);
 3841        let buffer_ids: HashSet<BufferId> = self
 3842            .selections
 3843            .disjoint_anchor_ranges()
 3844            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3845            .collect();
 3846        for buffer_id in buffer_ids {
 3847            self.unfold_buffer(buffer_id, cx);
 3848        }
 3849    }
 3850
 3851    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3852    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3853    /// effects of selection change occur at the end of the transaction.
 3854    pub fn change_selections<R>(
 3855        &mut self,
 3856        effects: SelectionEffects,
 3857        window: &mut Window,
 3858        cx: &mut Context<Self>,
 3859        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3860    ) -> R {
 3861        let snapshot = self.display_snapshot(cx);
 3862        if let Some(state) = &mut self.deferred_selection_effects_state {
 3863            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3864            state.effects.completions = effects.completions;
 3865            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3866            let (changed, result) = self.selections.change_with(&snapshot, change);
 3867            state.changed |= changed;
 3868            return result;
 3869        }
 3870        let mut state = DeferredSelectionEffectsState {
 3871            changed: false,
 3872            effects,
 3873            old_cursor_position: self.selections.newest_anchor().head(),
 3874            history_entry: SelectionHistoryEntry {
 3875                selections: self.selections.disjoint_anchors_arc(),
 3876                select_next_state: self.select_next_state.clone(),
 3877                select_prev_state: self.select_prev_state.clone(),
 3878                add_selections_state: self.add_selections_state.clone(),
 3879            },
 3880        };
 3881        let (changed, result) = self.selections.change_with(&snapshot, change);
 3882        state.changed = state.changed || changed;
 3883        if self.defer_selection_effects {
 3884            self.deferred_selection_effects_state = Some(state);
 3885        } else {
 3886            self.apply_selection_effects(state, window, cx);
 3887        }
 3888        result
 3889    }
 3890
 3891    /// Defers the effects of selection change, so that the effects of multiple calls to
 3892    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3893    /// to selection history and the state of popovers based on selection position aren't
 3894    /// erroneously updated.
 3895    pub fn with_selection_effects_deferred<R>(
 3896        &mut self,
 3897        window: &mut Window,
 3898        cx: &mut Context<Self>,
 3899        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3900    ) -> R {
 3901        let already_deferred = self.defer_selection_effects;
 3902        self.defer_selection_effects = true;
 3903        let result = update(self, window, cx);
 3904        if !already_deferred {
 3905            self.defer_selection_effects = false;
 3906            if let Some(state) = self.deferred_selection_effects_state.take() {
 3907                self.apply_selection_effects(state, window, cx);
 3908            }
 3909        }
 3910        result
 3911    }
 3912
 3913    fn apply_selection_effects(
 3914        &mut self,
 3915        state: DeferredSelectionEffectsState,
 3916        window: &mut Window,
 3917        cx: &mut Context<Self>,
 3918    ) {
 3919        if state.changed {
 3920            self.selection_history.push(state.history_entry);
 3921
 3922            if let Some(autoscroll) = state.effects.scroll {
 3923                self.request_autoscroll(autoscroll, cx);
 3924            }
 3925
 3926            let old_cursor_position = &state.old_cursor_position;
 3927
 3928            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3929
 3930            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3931                self.show_signature_help_auto(window, cx);
 3932            }
 3933        }
 3934    }
 3935
 3936    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3937    where
 3938        I: IntoIterator<Item = (Range<S>, T)>,
 3939        S: ToOffset,
 3940        T: Into<Arc<str>>,
 3941    {
 3942        if self.read_only(cx) {
 3943            return;
 3944        }
 3945
 3946        self.buffer
 3947            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3948    }
 3949
 3950    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3951    where
 3952        I: IntoIterator<Item = (Range<S>, T)>,
 3953        S: ToOffset,
 3954        T: Into<Arc<str>>,
 3955    {
 3956        if self.read_only(cx) {
 3957            return;
 3958        }
 3959
 3960        self.buffer.update(cx, |buffer, cx| {
 3961            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3962        });
 3963    }
 3964
 3965    pub fn edit_with_block_indent<I, S, T>(
 3966        &mut self,
 3967        edits: I,
 3968        original_indent_columns: Vec<Option<u32>>,
 3969        cx: &mut Context<Self>,
 3970    ) where
 3971        I: IntoIterator<Item = (Range<S>, T)>,
 3972        S: ToOffset,
 3973        T: Into<Arc<str>>,
 3974    {
 3975        if self.read_only(cx) {
 3976            return;
 3977        }
 3978
 3979        self.buffer.update(cx, |buffer, cx| {
 3980            buffer.edit(
 3981                edits,
 3982                Some(AutoindentMode::Block {
 3983                    original_indent_columns,
 3984                }),
 3985                cx,
 3986            )
 3987        });
 3988    }
 3989
 3990    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3991        self.hide_context_menu(window, cx);
 3992
 3993        match phase {
 3994            SelectPhase::Begin {
 3995                position,
 3996                add,
 3997                click_count,
 3998            } => self.begin_selection(position, add, click_count, window, cx),
 3999            SelectPhase::BeginColumnar {
 4000                position,
 4001                goal_column,
 4002                reset,
 4003                mode,
 4004            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4005            SelectPhase::Extend {
 4006                position,
 4007                click_count,
 4008            } => self.extend_selection(position, click_count, window, cx),
 4009            SelectPhase::Update {
 4010                position,
 4011                goal_column,
 4012                scroll_delta,
 4013            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4014            SelectPhase::End => self.end_selection(window, cx),
 4015        }
 4016    }
 4017
 4018    fn extend_selection(
 4019        &mut self,
 4020        position: DisplayPoint,
 4021        click_count: usize,
 4022        window: &mut Window,
 4023        cx: &mut Context<Self>,
 4024    ) {
 4025        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4026        let tail = self
 4027            .selections
 4028            .newest::<MultiBufferOffset>(&display_map)
 4029            .tail();
 4030        let click_count = click_count.max(match self.selections.select_mode() {
 4031            SelectMode::Character => 1,
 4032            SelectMode::Word(_) => 2,
 4033            SelectMode::Line(_) => 3,
 4034            SelectMode::All => 4,
 4035        });
 4036        self.begin_selection(position, false, click_count, window, cx);
 4037
 4038        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4039
 4040        let current_selection = match self.selections.select_mode() {
 4041            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4042            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4043        };
 4044
 4045        let mut pending_selection = self
 4046            .selections
 4047            .pending_anchor()
 4048            .cloned()
 4049            .expect("extend_selection not called with pending selection");
 4050
 4051        if pending_selection
 4052            .start
 4053            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4054            == Ordering::Greater
 4055        {
 4056            pending_selection.start = current_selection.start;
 4057        }
 4058        if pending_selection
 4059            .end
 4060            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4061            == Ordering::Less
 4062        {
 4063            pending_selection.end = current_selection.end;
 4064            pending_selection.reversed = true;
 4065        }
 4066
 4067        let mut pending_mode = self.selections.pending_mode().unwrap();
 4068        match &mut pending_mode {
 4069            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4070            _ => {}
 4071        }
 4072
 4073        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4074            SelectionEffects::scroll(Autoscroll::fit())
 4075        } else {
 4076            SelectionEffects::no_scroll()
 4077        };
 4078
 4079        self.change_selections(effects, window, cx, |s| {
 4080            s.set_pending(pending_selection.clone(), pending_mode);
 4081            s.set_is_extending(true);
 4082        });
 4083    }
 4084
 4085    fn begin_selection(
 4086        &mut self,
 4087        position: DisplayPoint,
 4088        add: bool,
 4089        click_count: usize,
 4090        window: &mut Window,
 4091        cx: &mut Context<Self>,
 4092    ) {
 4093        if !self.focus_handle.is_focused(window) {
 4094            self.last_focused_descendant = None;
 4095            window.focus(&self.focus_handle, cx);
 4096        }
 4097
 4098        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4099        let buffer = display_map.buffer_snapshot();
 4100        let position = display_map.clip_point(position, Bias::Left);
 4101
 4102        let start;
 4103        let end;
 4104        let mode;
 4105        let mut auto_scroll;
 4106        match click_count {
 4107            1 => {
 4108                start = buffer.anchor_before(position.to_point(&display_map));
 4109                end = start;
 4110                mode = SelectMode::Character;
 4111                auto_scroll = true;
 4112            }
 4113            2 => {
 4114                let position = display_map
 4115                    .clip_point(position, Bias::Left)
 4116                    .to_offset(&display_map, Bias::Left);
 4117                let (range, _) = buffer.surrounding_word(position, None);
 4118                start = buffer.anchor_before(range.start);
 4119                end = buffer.anchor_before(range.end);
 4120                mode = SelectMode::Word(start..end);
 4121                auto_scroll = true;
 4122            }
 4123            3 => {
 4124                let position = display_map
 4125                    .clip_point(position, Bias::Left)
 4126                    .to_point(&display_map);
 4127                let line_start = display_map.prev_line_boundary(position).0;
 4128                let next_line_start = buffer.clip_point(
 4129                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4130                    Bias::Left,
 4131                );
 4132                start = buffer.anchor_before(line_start);
 4133                end = buffer.anchor_before(next_line_start);
 4134                mode = SelectMode::Line(start..end);
 4135                auto_scroll = true;
 4136            }
 4137            _ => {
 4138                start = buffer.anchor_before(MultiBufferOffset(0));
 4139                end = buffer.anchor_before(buffer.len());
 4140                mode = SelectMode::All;
 4141                auto_scroll = false;
 4142            }
 4143        }
 4144        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4145
 4146        let point_to_delete: Option<usize> = {
 4147            let selected_points: Vec<Selection<Point>> =
 4148                self.selections.disjoint_in_range(start..end, &display_map);
 4149
 4150            if !add || click_count > 1 {
 4151                None
 4152            } else if !selected_points.is_empty() {
 4153                Some(selected_points[0].id)
 4154            } else {
 4155                let clicked_point_already_selected =
 4156                    self.selections.disjoint_anchors().iter().find(|selection| {
 4157                        selection.start.to_point(buffer) == start.to_point(buffer)
 4158                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4159                    });
 4160
 4161                clicked_point_already_selected.map(|selection| selection.id)
 4162            }
 4163        };
 4164
 4165        let selections_count = self.selections.count();
 4166        let effects = if auto_scroll {
 4167            SelectionEffects::default()
 4168        } else {
 4169            SelectionEffects::no_scroll()
 4170        };
 4171
 4172        self.change_selections(effects, window, cx, |s| {
 4173            if let Some(point_to_delete) = point_to_delete {
 4174                s.delete(point_to_delete);
 4175
 4176                if selections_count == 1 {
 4177                    s.set_pending_anchor_range(start..end, mode);
 4178                }
 4179            } else {
 4180                if !add {
 4181                    s.clear_disjoint();
 4182                }
 4183
 4184                s.set_pending_anchor_range(start..end, mode);
 4185            }
 4186        });
 4187    }
 4188
 4189    fn begin_columnar_selection(
 4190        &mut self,
 4191        position: DisplayPoint,
 4192        goal_column: u32,
 4193        reset: bool,
 4194        mode: ColumnarMode,
 4195        window: &mut Window,
 4196        cx: &mut Context<Self>,
 4197    ) {
 4198        if !self.focus_handle.is_focused(window) {
 4199            self.last_focused_descendant = None;
 4200            window.focus(&self.focus_handle, cx);
 4201        }
 4202
 4203        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4204
 4205        if reset {
 4206            let pointer_position = display_map
 4207                .buffer_snapshot()
 4208                .anchor_before(position.to_point(&display_map));
 4209
 4210            self.change_selections(
 4211                SelectionEffects::scroll(Autoscroll::newest()),
 4212                window,
 4213                cx,
 4214                |s| {
 4215                    s.clear_disjoint();
 4216                    s.set_pending_anchor_range(
 4217                        pointer_position..pointer_position,
 4218                        SelectMode::Character,
 4219                    );
 4220                },
 4221            );
 4222        };
 4223
 4224        let tail = self.selections.newest::<Point>(&display_map).tail();
 4225        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4226        self.columnar_selection_state = match mode {
 4227            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4228                selection_tail: selection_anchor,
 4229                display_point: if reset {
 4230                    if position.column() != goal_column {
 4231                        Some(DisplayPoint::new(position.row(), goal_column))
 4232                    } else {
 4233                        None
 4234                    }
 4235                } else {
 4236                    None
 4237                },
 4238            }),
 4239            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4240                selection_tail: selection_anchor,
 4241            }),
 4242        };
 4243
 4244        if !reset {
 4245            self.select_columns(position, goal_column, &display_map, window, cx);
 4246        }
 4247    }
 4248
 4249    fn update_selection(
 4250        &mut self,
 4251        position: DisplayPoint,
 4252        goal_column: u32,
 4253        scroll_delta: gpui::Point<f32>,
 4254        window: &mut Window,
 4255        cx: &mut Context<Self>,
 4256    ) {
 4257        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4258
 4259        if self.columnar_selection_state.is_some() {
 4260            self.select_columns(position, goal_column, &display_map, window, cx);
 4261        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4262            let buffer = display_map.buffer_snapshot();
 4263            let head;
 4264            let tail;
 4265            let mode = self.selections.pending_mode().unwrap();
 4266            match &mode {
 4267                SelectMode::Character => {
 4268                    head = position.to_point(&display_map);
 4269                    tail = pending.tail().to_point(buffer);
 4270                }
 4271                SelectMode::Word(original_range) => {
 4272                    let offset = display_map
 4273                        .clip_point(position, Bias::Left)
 4274                        .to_offset(&display_map, Bias::Left);
 4275                    let original_range = original_range.to_offset(buffer);
 4276
 4277                    let head_offset = if buffer.is_inside_word(offset, None)
 4278                        || original_range.contains(&offset)
 4279                    {
 4280                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4281                        if word_range.start < original_range.start {
 4282                            word_range.start
 4283                        } else {
 4284                            word_range.end
 4285                        }
 4286                    } else {
 4287                        offset
 4288                    };
 4289
 4290                    head = head_offset.to_point(buffer);
 4291                    if head_offset <= original_range.start {
 4292                        tail = original_range.end.to_point(buffer);
 4293                    } else {
 4294                        tail = original_range.start.to_point(buffer);
 4295                    }
 4296                }
 4297                SelectMode::Line(original_range) => {
 4298                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4299
 4300                    let position = display_map
 4301                        .clip_point(position, Bias::Left)
 4302                        .to_point(&display_map);
 4303                    let line_start = display_map.prev_line_boundary(position).0;
 4304                    let next_line_start = buffer.clip_point(
 4305                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4306                        Bias::Left,
 4307                    );
 4308
 4309                    if line_start < original_range.start {
 4310                        head = line_start
 4311                    } else {
 4312                        head = next_line_start
 4313                    }
 4314
 4315                    if head <= original_range.start {
 4316                        tail = original_range.end;
 4317                    } else {
 4318                        tail = original_range.start;
 4319                    }
 4320                }
 4321                SelectMode::All => {
 4322                    return;
 4323                }
 4324            };
 4325
 4326            if head < tail {
 4327                pending.start = buffer.anchor_before(head);
 4328                pending.end = buffer.anchor_before(tail);
 4329                pending.reversed = true;
 4330            } else {
 4331                pending.start = buffer.anchor_before(tail);
 4332                pending.end = buffer.anchor_before(head);
 4333                pending.reversed = false;
 4334            }
 4335
 4336            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4337                s.set_pending(pending.clone(), mode);
 4338            });
 4339        } else {
 4340            log::error!("update_selection dispatched with no pending selection");
 4341            return;
 4342        }
 4343
 4344        self.apply_scroll_delta(scroll_delta, window, cx);
 4345        cx.notify();
 4346    }
 4347
 4348    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4349        self.columnar_selection_state.take();
 4350        if let Some(pending_mode) = self.selections.pending_mode() {
 4351            let selections = self
 4352                .selections
 4353                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4354            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4355                s.select(selections);
 4356                s.clear_pending();
 4357                if s.is_extending() {
 4358                    s.set_is_extending(false);
 4359                } else {
 4360                    s.set_select_mode(pending_mode);
 4361                }
 4362            });
 4363        }
 4364    }
 4365
 4366    fn select_columns(
 4367        &mut self,
 4368        head: DisplayPoint,
 4369        goal_column: u32,
 4370        display_map: &DisplaySnapshot,
 4371        window: &mut Window,
 4372        cx: &mut Context<Self>,
 4373    ) {
 4374        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4375            return;
 4376        };
 4377
 4378        let tail = match columnar_state {
 4379            ColumnarSelectionState::FromMouse {
 4380                selection_tail,
 4381                display_point,
 4382            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4383            ColumnarSelectionState::FromSelection { selection_tail } => {
 4384                selection_tail.to_display_point(display_map)
 4385            }
 4386        };
 4387
 4388        let start_row = cmp::min(tail.row(), head.row());
 4389        let end_row = cmp::max(tail.row(), head.row());
 4390        let start_column = cmp::min(tail.column(), goal_column);
 4391        let end_column = cmp::max(tail.column(), goal_column);
 4392        let reversed = start_column < tail.column();
 4393
 4394        let selection_ranges = (start_row.0..=end_row.0)
 4395            .map(DisplayRow)
 4396            .filter_map(|row| {
 4397                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4398                    || start_column <= display_map.line_len(row))
 4399                    && !display_map.is_block_line(row)
 4400                {
 4401                    let start = display_map
 4402                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4403                        .to_point(display_map);
 4404                    let end = display_map
 4405                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4406                        .to_point(display_map);
 4407                    if reversed {
 4408                        Some(end..start)
 4409                    } else {
 4410                        Some(start..end)
 4411                    }
 4412                } else {
 4413                    None
 4414                }
 4415            })
 4416            .collect::<Vec<_>>();
 4417        if selection_ranges.is_empty() {
 4418            return;
 4419        }
 4420
 4421        let ranges = match columnar_state {
 4422            ColumnarSelectionState::FromMouse { .. } => {
 4423                let mut non_empty_ranges = selection_ranges
 4424                    .iter()
 4425                    .filter(|selection_range| selection_range.start != selection_range.end)
 4426                    .peekable();
 4427                if non_empty_ranges.peek().is_some() {
 4428                    non_empty_ranges.cloned().collect()
 4429                } else {
 4430                    selection_ranges
 4431                }
 4432            }
 4433            _ => selection_ranges,
 4434        };
 4435
 4436        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4437            s.select_ranges(ranges);
 4438        });
 4439        cx.notify();
 4440    }
 4441
 4442    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4443        self.selections
 4444            .all_adjusted(snapshot)
 4445            .iter()
 4446            .any(|selection| !selection.is_empty())
 4447    }
 4448
 4449    pub fn has_pending_nonempty_selection(&self) -> bool {
 4450        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4451            Some(Selection { start, end, .. }) => start != end,
 4452            None => false,
 4453        };
 4454
 4455        pending_nonempty_selection
 4456            || (self.columnar_selection_state.is_some()
 4457                && self.selections.disjoint_anchors().len() > 1)
 4458    }
 4459
 4460    pub fn has_pending_selection(&self) -> bool {
 4461        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4462    }
 4463
 4464    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4465        self.selection_mark_mode = false;
 4466        self.selection_drag_state = SelectionDragState::None;
 4467
 4468        if self.dismiss_menus_and_popups(true, window, cx) {
 4469            cx.notify();
 4470            return;
 4471        }
 4472        if self.clear_expanded_diff_hunks(cx) {
 4473            cx.notify();
 4474            return;
 4475        }
 4476        if self.show_git_blame_gutter {
 4477            self.show_git_blame_gutter = false;
 4478            cx.notify();
 4479            return;
 4480        }
 4481
 4482        if self.mode.is_full()
 4483            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4484        {
 4485            cx.notify();
 4486            return;
 4487        }
 4488
 4489        cx.propagate();
 4490    }
 4491
 4492    pub fn dismiss_menus_and_popups(
 4493        &mut self,
 4494        is_user_requested: bool,
 4495        window: &mut Window,
 4496        cx: &mut Context<Self>,
 4497    ) -> bool {
 4498        let mut dismissed = false;
 4499
 4500        dismissed |= self.take_rename(false, window, cx).is_some();
 4501        dismissed |= self.hide_blame_popover(true, cx);
 4502        dismissed |= hide_hover(self, cx);
 4503        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4504        dismissed |= self.hide_context_menu(window, cx).is_some();
 4505        dismissed |= self.mouse_context_menu.take().is_some();
 4506        dismissed |= is_user_requested
 4507            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4508        dismissed |= self.snippet_stack.pop().is_some();
 4509        if self.diff_review_drag_state.is_some() {
 4510            self.cancel_diff_review_drag(cx);
 4511            dismissed = true;
 4512        }
 4513        if !self.diff_review_overlays.is_empty() {
 4514            self.dismiss_all_diff_review_overlays(cx);
 4515            dismissed = true;
 4516        }
 4517
 4518        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4519            self.dismiss_diagnostics(cx);
 4520            dismissed = true;
 4521        }
 4522
 4523        dismissed
 4524    }
 4525
 4526    fn linked_editing_ranges_for(
 4527        &self,
 4528        selection: Range<text::Anchor>,
 4529        cx: &App,
 4530    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4531        if self.linked_edit_ranges.is_empty() {
 4532            return None;
 4533        }
 4534        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4535            selection.end.buffer_id.and_then(|end_buffer_id| {
 4536                if selection.start.buffer_id != Some(end_buffer_id) {
 4537                    return None;
 4538                }
 4539                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4540                let snapshot = buffer.read(cx).snapshot();
 4541                self.linked_edit_ranges
 4542                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4543                    .map(|ranges| (ranges, snapshot, buffer))
 4544            })?;
 4545        use text::ToOffset as TO;
 4546        // find offset from the start of current range to current cursor position
 4547        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4548
 4549        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4550        let start_difference = start_offset - start_byte_offset;
 4551        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4552        let end_difference = end_offset - start_byte_offset;
 4553
 4554        // Current range has associated linked ranges.
 4555        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4556        for range in linked_ranges.iter() {
 4557            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4558            let end_offset = start_offset + end_difference;
 4559            let start_offset = start_offset + start_difference;
 4560            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4561                continue;
 4562            }
 4563            if self.selections.disjoint_anchor_ranges().any(|s| {
 4564                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4565                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4566                {
 4567                    return false;
 4568                }
 4569                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4570                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4571            }) {
 4572                continue;
 4573            }
 4574            let start = buffer_snapshot.anchor_after(start_offset);
 4575            let end = buffer_snapshot.anchor_after(end_offset);
 4576            linked_edits
 4577                .entry(buffer.clone())
 4578                .or_default()
 4579                .push(start..end);
 4580        }
 4581        Some(linked_edits)
 4582    }
 4583
 4584    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4585        let text: Arc<str> = text.into();
 4586
 4587        if self.read_only(cx) {
 4588            return;
 4589        }
 4590
 4591        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4592
 4593        self.unfold_buffers_with_selections(cx);
 4594
 4595        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4596        let mut bracket_inserted = false;
 4597        let mut edits = Vec::new();
 4598        let mut linked_edits = LinkedEdits::new();
 4599        let mut new_selections = Vec::with_capacity(selections.len());
 4600        let mut new_autoclose_regions = Vec::new();
 4601        let snapshot = self.buffer.read(cx).read(cx);
 4602        let mut clear_linked_edit_ranges = false;
 4603        let mut all_selections_read_only = true;
 4604        let mut has_adjacent_edits = false;
 4605        let mut in_adjacent_group = false;
 4606
 4607        let mut regions = self
 4608            .selections_with_autoclose_regions(selections, &snapshot)
 4609            .peekable();
 4610
 4611        while let Some((selection, autoclose_region)) = regions.next() {
 4612            if snapshot
 4613                .point_to_buffer_point(selection.head())
 4614                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4615            {
 4616                continue;
 4617            }
 4618            if snapshot
 4619                .point_to_buffer_point(selection.tail())
 4620                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4621            {
 4622                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4623                continue;
 4624            }
 4625            all_selections_read_only = false;
 4626
 4627            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4628                // Determine if the inserted text matches the opening or closing
 4629                // bracket of any of this language's bracket pairs.
 4630                let mut bracket_pair = None;
 4631                let mut is_bracket_pair_start = false;
 4632                let mut is_bracket_pair_end = false;
 4633                if !text.is_empty() {
 4634                    let mut bracket_pair_matching_end = None;
 4635                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4636                    //  and they are removing the character that triggered IME popup.
 4637                    for (pair, enabled) in scope.brackets() {
 4638                        if !pair.close && !pair.surround {
 4639                            continue;
 4640                        }
 4641
 4642                        if enabled && pair.start.ends_with(text.as_ref()) {
 4643                            let prefix_len = pair.start.len() - text.len();
 4644                            let preceding_text_matches_prefix = prefix_len == 0
 4645                                || (selection.start.column >= (prefix_len as u32)
 4646                                    && snapshot.contains_str_at(
 4647                                        Point::new(
 4648                                            selection.start.row,
 4649                                            selection.start.column - (prefix_len as u32),
 4650                                        ),
 4651                                        &pair.start[..prefix_len],
 4652                                    ));
 4653                            if preceding_text_matches_prefix {
 4654                                bracket_pair = Some(pair.clone());
 4655                                is_bracket_pair_start = true;
 4656                                break;
 4657                            }
 4658                        }
 4659                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4660                        {
 4661                            // take first bracket pair matching end, but don't break in case a later bracket
 4662                            // pair matches start
 4663                            bracket_pair_matching_end = Some(pair.clone());
 4664                        }
 4665                    }
 4666                    if let Some(end) = bracket_pair_matching_end
 4667                        && bracket_pair.is_none()
 4668                    {
 4669                        bracket_pair = Some(end);
 4670                        is_bracket_pair_end = true;
 4671                    }
 4672                }
 4673
 4674                if let Some(bracket_pair) = bracket_pair {
 4675                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4676                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4677                    let auto_surround =
 4678                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4679                    if selection.is_empty() {
 4680                        if is_bracket_pair_start {
 4681                            // If the inserted text is a suffix of an opening bracket and the
 4682                            // selection is preceded by the rest of the opening bracket, then
 4683                            // insert the closing bracket.
 4684                            let following_text_allows_autoclose = snapshot
 4685                                .chars_at(selection.start)
 4686                                .next()
 4687                                .is_none_or(|c| scope.should_autoclose_before(c));
 4688
 4689                            let preceding_text_allows_autoclose = selection.start.column == 0
 4690                                || snapshot
 4691                                    .reversed_chars_at(selection.start)
 4692                                    .next()
 4693                                    .is_none_or(|c| {
 4694                                        bracket_pair.start != bracket_pair.end
 4695                                            || !snapshot
 4696                                                .char_classifier_at(selection.start)
 4697                                                .is_word(c)
 4698                                    });
 4699
 4700                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4701                                && bracket_pair.start.len() == 1
 4702                            {
 4703                                let target = bracket_pair.start.chars().next().unwrap();
 4704                                let mut byte_offset = 0u32;
 4705                                let current_line_count = snapshot
 4706                                    .reversed_chars_at(selection.start)
 4707                                    .take_while(|&c| c != '\n')
 4708                                    .filter(|c| {
 4709                                        byte_offset += c.len_utf8() as u32;
 4710                                        if *c != target {
 4711                                            return false;
 4712                                        }
 4713
 4714                                        let point = Point::new(
 4715                                            selection.start.row,
 4716                                            selection.start.column.saturating_sub(byte_offset),
 4717                                        );
 4718
 4719                                        let is_enabled = snapshot
 4720                                            .language_scope_at(point)
 4721                                            .and_then(|scope| {
 4722                                                scope
 4723                                                    .brackets()
 4724                                                    .find(|(pair, _)| {
 4725                                                        pair.start == bracket_pair.start
 4726                                                    })
 4727                                                    .map(|(_, enabled)| enabled)
 4728                                            })
 4729                                            .unwrap_or(true);
 4730
 4731                                        let is_delimiter = snapshot
 4732                                            .language_scope_at(Point::new(
 4733                                                point.row,
 4734                                                point.column + 1,
 4735                                            ))
 4736                                            .and_then(|scope| {
 4737                                                scope
 4738                                                    .brackets()
 4739                                                    .find(|(pair, _)| {
 4740                                                        pair.start == bracket_pair.start
 4741                                                    })
 4742                                                    .map(|(_, enabled)| !enabled)
 4743                                            })
 4744                                            .unwrap_or(false);
 4745
 4746                                        is_enabled && !is_delimiter
 4747                                    })
 4748                                    .count();
 4749                                current_line_count % 2 == 1
 4750                            } else {
 4751                                false
 4752                            };
 4753
 4754                            if autoclose
 4755                                && bracket_pair.close
 4756                                && following_text_allows_autoclose
 4757                                && preceding_text_allows_autoclose
 4758                                && !is_closing_quote
 4759                            {
 4760                                let anchor = snapshot.anchor_before(selection.end);
 4761                                new_selections.push((selection.map(|_| anchor), text.len()));
 4762                                new_autoclose_regions.push((
 4763                                    anchor,
 4764                                    text.len(),
 4765                                    selection.id,
 4766                                    bracket_pair.clone(),
 4767                                ));
 4768                                edits.push((
 4769                                    selection.range(),
 4770                                    format!("{}{}", text, bracket_pair.end).into(),
 4771                                ));
 4772                                bracket_inserted = true;
 4773                                continue;
 4774                            }
 4775                        }
 4776
 4777                        if let Some(region) = autoclose_region {
 4778                            // If the selection is followed by an auto-inserted closing bracket,
 4779                            // then don't insert that closing bracket again; just move the selection
 4780                            // past the closing bracket.
 4781                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4782                                && text.as_ref() == region.pair.end.as_str()
 4783                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4784                            if should_skip {
 4785                                let anchor = snapshot.anchor_after(selection.end);
 4786                                new_selections
 4787                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4788                                continue;
 4789                            }
 4790                        }
 4791
 4792                        let always_treat_brackets_as_autoclosed = snapshot
 4793                            .language_settings_at(selection.start, cx)
 4794                            .always_treat_brackets_as_autoclosed;
 4795                        if always_treat_brackets_as_autoclosed
 4796                            && is_bracket_pair_end
 4797                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4798                        {
 4799                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4800                            // and the inserted text is a closing bracket and the selection is followed
 4801                            // by the closing bracket then move the selection past the closing bracket.
 4802                            let anchor = snapshot.anchor_after(selection.end);
 4803                            new_selections.push((selection.map(|_| anchor), text.len()));
 4804                            continue;
 4805                        }
 4806                    }
 4807                    // If an opening bracket is 1 character long and is typed while
 4808                    // text is selected, then surround that text with the bracket pair.
 4809                    else if auto_surround
 4810                        && bracket_pair.surround
 4811                        && is_bracket_pair_start
 4812                        && bracket_pair.start.chars().count() == 1
 4813                    {
 4814                        edits.push((selection.start..selection.start, text.clone()));
 4815                        edits.push((
 4816                            selection.end..selection.end,
 4817                            bracket_pair.end.as_str().into(),
 4818                        ));
 4819                        bracket_inserted = true;
 4820                        new_selections.push((
 4821                            Selection {
 4822                                id: selection.id,
 4823                                start: snapshot.anchor_after(selection.start),
 4824                                end: snapshot.anchor_before(selection.end),
 4825                                reversed: selection.reversed,
 4826                                goal: selection.goal,
 4827                            },
 4828                            0,
 4829                        ));
 4830                        continue;
 4831                    }
 4832                }
 4833            }
 4834
 4835            if self.auto_replace_emoji_shortcode
 4836                && selection.is_empty()
 4837                && text.as_ref().ends_with(':')
 4838                && let Some(possible_emoji_short_code) =
 4839                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4840                && !possible_emoji_short_code.is_empty()
 4841                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4842            {
 4843                let emoji_shortcode_start = Point::new(
 4844                    selection.start.row,
 4845                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4846                );
 4847
 4848                // Remove shortcode from buffer
 4849                edits.push((
 4850                    emoji_shortcode_start..selection.start,
 4851                    "".to_string().into(),
 4852                ));
 4853                new_selections.push((
 4854                    Selection {
 4855                        id: selection.id,
 4856                        start: snapshot.anchor_after(emoji_shortcode_start),
 4857                        end: snapshot.anchor_before(selection.start),
 4858                        reversed: selection.reversed,
 4859                        goal: selection.goal,
 4860                    },
 4861                    0,
 4862                ));
 4863
 4864                // Insert emoji
 4865                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4866                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4867                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4868
 4869                continue;
 4870            }
 4871
 4872            let next_is_adjacent = regions
 4873                .peek()
 4874                .is_some_and(|(next, _)| selection.end == next.start);
 4875
 4876            // If not handling any auto-close operation, then just replace the selected
 4877            // text with the given input and move the selection to the end of the
 4878            // newly inserted text.
 4879            let anchor = if in_adjacent_group || next_is_adjacent {
 4880                // After edits the right bias would shift those anchor to the next visible fragment
 4881                // but we want to resolve to the previous one
 4882                snapshot.anchor_before(selection.end)
 4883            } else {
 4884                snapshot.anchor_after(selection.end)
 4885            };
 4886
 4887            if !self.linked_edit_ranges.is_empty() {
 4888                let start_anchor = snapshot.anchor_before(selection.start);
 4889
 4890                let is_word_char = text.chars().next().is_none_or(|char| {
 4891                    let classifier = snapshot
 4892                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4893                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4894                    classifier.is_word(char)
 4895                });
 4896
 4897                if is_word_char {
 4898                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4899                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4900                } else {
 4901                    clear_linked_edit_ranges = true;
 4902                }
 4903            }
 4904
 4905            new_selections.push((selection.map(|_| anchor), 0));
 4906            edits.push((selection.start..selection.end, text.clone()));
 4907
 4908            has_adjacent_edits |= next_is_adjacent;
 4909            in_adjacent_group = next_is_adjacent;
 4910        }
 4911
 4912        if all_selections_read_only {
 4913            return;
 4914        }
 4915
 4916        drop(regions);
 4917        drop(snapshot);
 4918
 4919        self.transact(window, cx, |this, window, cx| {
 4920            if clear_linked_edit_ranges {
 4921                this.linked_edit_ranges.clear();
 4922            }
 4923            let initial_buffer_versions =
 4924                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4925
 4926            this.buffer.update(cx, |buffer, cx| {
 4927                if has_adjacent_edits {
 4928                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4929                } else {
 4930                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4931                }
 4932            });
 4933            linked_edits.apply(cx);
 4934            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4935            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4936            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4937            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4938                new_anchor_selections,
 4939                &map,
 4940            )
 4941            .zip(new_selection_deltas)
 4942            .map(|(selection, delta)| Selection {
 4943                id: selection.id,
 4944                start: selection.start + delta,
 4945                end: selection.end + delta,
 4946                reversed: selection.reversed,
 4947                goal: SelectionGoal::None,
 4948            })
 4949            .collect::<Vec<_>>();
 4950
 4951            let mut i = 0;
 4952            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4953                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4954                let start = map.buffer_snapshot().anchor_before(position);
 4955                let end = map.buffer_snapshot().anchor_after(position);
 4956                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4957                    match existing_state
 4958                        .range
 4959                        .start
 4960                        .cmp(&start, map.buffer_snapshot())
 4961                    {
 4962                        Ordering::Less => i += 1,
 4963                        Ordering::Greater => break,
 4964                        Ordering::Equal => {
 4965                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4966                                Ordering::Less => i += 1,
 4967                                Ordering::Equal => break,
 4968                                Ordering::Greater => break,
 4969                            }
 4970                        }
 4971                    }
 4972                }
 4973                this.autoclose_regions.insert(
 4974                    i,
 4975                    AutocloseRegion {
 4976                        selection_id,
 4977                        range: start..end,
 4978                        pair,
 4979                    },
 4980                );
 4981            }
 4982
 4983            let had_active_edit_prediction = this.has_active_edit_prediction();
 4984            this.change_selections(
 4985                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4986                window,
 4987                cx,
 4988                |s| s.select(new_selections),
 4989            );
 4990
 4991            if !bracket_inserted
 4992                && let Some(on_type_format_task) =
 4993                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4994            {
 4995                on_type_format_task.detach_and_log_err(cx);
 4996            }
 4997
 4998            let editor_settings = EditorSettings::get_global(cx);
 4999            if bracket_inserted
 5000                && (editor_settings.auto_signature_help
 5001                    || editor_settings.show_signature_help_after_edits)
 5002            {
 5003                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5004            }
 5005
 5006            let trigger_in_words =
 5007                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5008            if this.hard_wrap.is_some() {
 5009                let latest: Range<Point> = this.selections.newest(&map).range();
 5010                if latest.is_empty()
 5011                    && this
 5012                        .buffer()
 5013                        .read(cx)
 5014                        .snapshot(cx)
 5015                        .line_len(MultiBufferRow(latest.start.row))
 5016                        == latest.start.column
 5017                {
 5018                    this.rewrap_impl(
 5019                        RewrapOptions {
 5020                            override_language_settings: true,
 5021                            preserve_existing_whitespace: true,
 5022                        },
 5023                        cx,
 5024                    )
 5025                }
 5026            }
 5027            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5028            refresh_linked_ranges(this, window, cx);
 5029            this.refresh_edit_prediction(true, false, window, cx);
 5030            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5031        });
 5032    }
 5033
 5034    fn find_possible_emoji_shortcode_at_position(
 5035        snapshot: &MultiBufferSnapshot,
 5036        position: Point,
 5037    ) -> Option<String> {
 5038        let mut chars = Vec::new();
 5039        let mut found_colon = false;
 5040        for char in snapshot.reversed_chars_at(position).take(100) {
 5041            // Found a possible emoji shortcode in the middle of the buffer
 5042            if found_colon {
 5043                if char.is_whitespace() {
 5044                    chars.reverse();
 5045                    return Some(chars.iter().collect());
 5046                }
 5047                // If the previous character is not a whitespace, we are in the middle of a word
 5048                // and we only want to complete the shortcode if the word is made up of other emojis
 5049                let mut containing_word = String::new();
 5050                for ch in snapshot
 5051                    .reversed_chars_at(position)
 5052                    .skip(chars.len() + 1)
 5053                    .take(100)
 5054                {
 5055                    if ch.is_whitespace() {
 5056                        break;
 5057                    }
 5058                    containing_word.push(ch);
 5059                }
 5060                let containing_word = containing_word.chars().rev().collect::<String>();
 5061                if util::word_consists_of_emojis(containing_word.as_str()) {
 5062                    chars.reverse();
 5063                    return Some(chars.iter().collect());
 5064                }
 5065            }
 5066
 5067            if char.is_whitespace() || !char.is_ascii() {
 5068                return None;
 5069            }
 5070            if char == ':' {
 5071                found_colon = true;
 5072            } else {
 5073                chars.push(char);
 5074            }
 5075        }
 5076        // Found a possible emoji shortcode at the beginning of the buffer
 5077        chars.reverse();
 5078        Some(chars.iter().collect())
 5079    }
 5080
 5081    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5082        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5083        self.transact(window, cx, |this, window, cx| {
 5084            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5085                let selections = this
 5086                    .selections
 5087                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5088                let multi_buffer = this.buffer.read(cx);
 5089                let buffer = multi_buffer.snapshot(cx);
 5090                selections
 5091                    .iter()
 5092                    .map(|selection| {
 5093                        let start_point = selection.start.to_point(&buffer);
 5094                        let mut existing_indent =
 5095                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5096                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5097                        let start = selection.start;
 5098                        let end = selection.end;
 5099                        let selection_is_empty = start == end;
 5100                        let language_scope = buffer.language_scope_at(start);
 5101                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5102                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5103                                &buffer,
 5104                                start..end,
 5105                                language,
 5106                            )
 5107                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5108                                    &buffer,
 5109                                    start..end,
 5110                                );
 5111
 5112                            let mut newline_config = NewlineConfig::Newline {
 5113                                additional_indent: IndentSize::spaces(0),
 5114                                extra_line_additional_indent: if needs_extra_newline {
 5115                                    Some(IndentSize::spaces(0))
 5116                                } else {
 5117                                    None
 5118                                },
 5119                                prevent_auto_indent: false,
 5120                            };
 5121
 5122                            let comment_delimiter = maybe!({
 5123                                if !selection_is_empty {
 5124                                    return None;
 5125                                }
 5126
 5127                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5128                                    return None;
 5129                                }
 5130
 5131                                return comment_delimiter_for_newline(
 5132                                    &start_point,
 5133                                    &buffer,
 5134                                    language,
 5135                                );
 5136                            });
 5137
 5138                            let doc_delimiter = maybe!({
 5139                                if !selection_is_empty {
 5140                                    return None;
 5141                                }
 5142
 5143                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5144                                    return None;
 5145                                }
 5146
 5147                                return documentation_delimiter_for_newline(
 5148                                    &start_point,
 5149                                    &buffer,
 5150                                    language,
 5151                                    &mut newline_config,
 5152                                );
 5153                            });
 5154
 5155                            let list_delimiter = maybe!({
 5156                                if !selection_is_empty {
 5157                                    return None;
 5158                                }
 5159
 5160                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5161                                    return None;
 5162                                }
 5163
 5164                                return list_delimiter_for_newline(
 5165                                    &start_point,
 5166                                    &buffer,
 5167                                    language,
 5168                                    &mut newline_config,
 5169                                );
 5170                            });
 5171
 5172                            (
 5173                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5174                                newline_config,
 5175                            )
 5176                        } else {
 5177                            (
 5178                                None,
 5179                                NewlineConfig::Newline {
 5180                                    additional_indent: IndentSize::spaces(0),
 5181                                    extra_line_additional_indent: None,
 5182                                    prevent_auto_indent: false,
 5183                                },
 5184                            )
 5185                        };
 5186
 5187                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5188                            NewlineConfig::ClearCurrentLine => {
 5189                                let row_start =
 5190                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5191                                (row_start, String::new(), false)
 5192                            }
 5193                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5194                                let row_start =
 5195                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5196                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5197                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5198                                let reduced_indent =
 5199                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5200                                let mut new_text = String::new();
 5201                                new_text.extend(reduced_indent.chars());
 5202                                new_text.push_str(continuation);
 5203                                (row_start, new_text, true)
 5204                            }
 5205                            NewlineConfig::Newline {
 5206                                additional_indent,
 5207                                extra_line_additional_indent,
 5208                                prevent_auto_indent,
 5209                            } => {
 5210                                let capacity_for_delimiter =
 5211                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5212                                let extra_line_len = extra_line_additional_indent
 5213                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5214                                    .unwrap_or(0);
 5215                                let mut new_text = String::with_capacity(
 5216                                    1 + capacity_for_delimiter
 5217                                        + existing_indent.len as usize
 5218                                        + additional_indent.len as usize
 5219                                        + extra_line_len,
 5220                                );
 5221                                new_text.push('\n');
 5222                                new_text.extend(existing_indent.chars());
 5223                                new_text.extend(additional_indent.chars());
 5224                                if let Some(delimiter) = &delimiter {
 5225                                    new_text.push_str(delimiter);
 5226                                }
 5227                                if let Some(extra_indent) = extra_line_additional_indent {
 5228                                    new_text.push('\n');
 5229                                    new_text.extend(existing_indent.chars());
 5230                                    new_text.extend(extra_indent.chars());
 5231                                }
 5232                                (start, new_text, *prevent_auto_indent)
 5233                            }
 5234                        };
 5235
 5236                        let anchor = buffer.anchor_after(end);
 5237                        let new_selection = selection.map(|_| anchor);
 5238                        (
 5239                            ((edit_start..end, new_text), prevent_auto_indent),
 5240                            (newline_config.has_extra_line(), new_selection),
 5241                        )
 5242                    })
 5243                    .unzip()
 5244            };
 5245
 5246            let mut auto_indent_edits = Vec::new();
 5247            let mut edits = Vec::new();
 5248            for (edit, prevent_auto_indent) in edits_with_flags {
 5249                if prevent_auto_indent {
 5250                    edits.push(edit);
 5251                } else {
 5252                    auto_indent_edits.push(edit);
 5253                }
 5254            }
 5255            if !edits.is_empty() {
 5256                this.edit(edits, cx);
 5257            }
 5258            if !auto_indent_edits.is_empty() {
 5259                this.edit_with_autoindent(auto_indent_edits, cx);
 5260            }
 5261
 5262            let buffer = this.buffer.read(cx).snapshot(cx);
 5263            let new_selections = selection_info
 5264                .into_iter()
 5265                .map(|(extra_newline_inserted, new_selection)| {
 5266                    let mut cursor = new_selection.end.to_point(&buffer);
 5267                    if extra_newline_inserted {
 5268                        cursor.row -= 1;
 5269                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5270                    }
 5271                    new_selection.map(|_| cursor)
 5272                })
 5273                .collect();
 5274
 5275            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5276            this.refresh_edit_prediction(true, false, window, cx);
 5277            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5278                task.detach_and_log_err(cx);
 5279            }
 5280        });
 5281    }
 5282
 5283    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5284        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5285
 5286        let buffer = self.buffer.read(cx);
 5287        let snapshot = buffer.snapshot(cx);
 5288
 5289        let mut edits = Vec::new();
 5290        let mut rows = Vec::new();
 5291
 5292        for (rows_inserted, selection) in self
 5293            .selections
 5294            .all_adjusted(&self.display_snapshot(cx))
 5295            .into_iter()
 5296            .enumerate()
 5297        {
 5298            let cursor = selection.head();
 5299            let row = cursor.row;
 5300
 5301            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5302
 5303            let newline = "\n".to_string();
 5304            edits.push((start_of_line..start_of_line, newline));
 5305
 5306            rows.push(row + rows_inserted as u32);
 5307        }
 5308
 5309        self.transact(window, cx, |editor, window, cx| {
 5310            editor.edit(edits, cx);
 5311
 5312            editor.change_selections(Default::default(), window, cx, |s| {
 5313                let mut index = 0;
 5314                s.move_cursors_with(&mut |map, _, _| {
 5315                    let row = rows[index];
 5316                    index += 1;
 5317
 5318                    let point = Point::new(row, 0);
 5319                    let boundary = map.next_line_boundary(point).1;
 5320                    let clipped = map.clip_point(boundary, Bias::Left);
 5321
 5322                    (clipped, SelectionGoal::None)
 5323                });
 5324            });
 5325
 5326            let mut indent_edits = Vec::new();
 5327            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5328            for row in rows {
 5329                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5330                for (row, indent) in indents {
 5331                    if indent.len == 0 {
 5332                        continue;
 5333                    }
 5334
 5335                    let text = match indent.kind {
 5336                        IndentKind::Space => " ".repeat(indent.len as usize),
 5337                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5338                    };
 5339                    let point = Point::new(row.0, 0);
 5340                    indent_edits.push((point..point, text));
 5341                }
 5342            }
 5343            editor.edit(indent_edits, cx);
 5344            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5345                format.detach_and_log_err(cx);
 5346            }
 5347        });
 5348    }
 5349
 5350    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5351        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5352
 5353        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5354        let mut rows = Vec::new();
 5355        let mut rows_inserted = 0;
 5356
 5357        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5358            let cursor = selection.head();
 5359            let row = cursor.row;
 5360
 5361            let point = Point::new(row, 0);
 5362            let Some((buffer_handle, buffer_point, _)) =
 5363                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5364            else {
 5365                continue;
 5366            };
 5367
 5368            buffer_edits
 5369                .entry(buffer_handle.entity_id())
 5370                .or_insert_with(|| (buffer_handle, Vec::new()))
 5371                .1
 5372                .push(buffer_point);
 5373
 5374            rows_inserted += 1;
 5375            rows.push(row + rows_inserted);
 5376        }
 5377
 5378        self.transact(window, cx, |editor, window, cx| {
 5379            for (_, (buffer_handle, points)) in &buffer_edits {
 5380                buffer_handle.update(cx, |buffer, cx| {
 5381                    let edits: Vec<_> = points
 5382                        .iter()
 5383                        .map(|point| {
 5384                            let target = Point::new(point.row + 1, 0);
 5385                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5386                            (start_of_line..start_of_line, "\n")
 5387                        })
 5388                        .collect();
 5389                    buffer.edit(edits, None, cx);
 5390                });
 5391            }
 5392
 5393            editor.change_selections(Default::default(), window, cx, |s| {
 5394                let mut index = 0;
 5395                s.move_cursors_with(&mut |map, _, _| {
 5396                    let row = rows[index];
 5397                    index += 1;
 5398
 5399                    let point = Point::new(row, 0);
 5400                    let boundary = map.next_line_boundary(point).1;
 5401                    let clipped = map.clip_point(boundary, Bias::Left);
 5402
 5403                    (clipped, SelectionGoal::None)
 5404                });
 5405            });
 5406
 5407            let mut indent_edits = Vec::new();
 5408            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5409            for row in rows {
 5410                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5411                for (row, indent) in indents {
 5412                    if indent.len == 0 {
 5413                        continue;
 5414                    }
 5415
 5416                    let text = match indent.kind {
 5417                        IndentKind::Space => " ".repeat(indent.len as usize),
 5418                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5419                    };
 5420                    let point = Point::new(row.0, 0);
 5421                    indent_edits.push((point..point, text));
 5422                }
 5423            }
 5424            editor.edit(indent_edits, cx);
 5425            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5426                format.detach_and_log_err(cx);
 5427            }
 5428        });
 5429    }
 5430
 5431    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5432        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5433            original_indent_columns: Vec::new(),
 5434        });
 5435        self.replace_selections(text, autoindent, window, cx, false);
 5436    }
 5437
 5438    /// Replaces the editor's selections with the provided `text`, applying the
 5439    /// given `autoindent_mode` (`None` will skip autoindentation).
 5440    ///
 5441    /// Early returns if the editor is in read-only mode, without applying any
 5442    /// edits.
 5443    fn replace_selections(
 5444        &mut self,
 5445        text: &str,
 5446        autoindent_mode: Option<AutoindentMode>,
 5447        window: &mut Window,
 5448        cx: &mut Context<Self>,
 5449        apply_linked_edits: bool,
 5450    ) {
 5451        if self.read_only(cx) {
 5452            return;
 5453        }
 5454
 5455        let text: Arc<str> = text.into();
 5456        self.transact(window, cx, |this, window, cx| {
 5457            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5458            let linked_edits = if apply_linked_edits {
 5459                this.linked_edits_for_selections(text.clone(), cx)
 5460            } else {
 5461                LinkedEdits::new()
 5462            };
 5463
 5464            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5465                let anchors = {
 5466                    let snapshot = buffer.read(cx);
 5467                    old_selections
 5468                        .iter()
 5469                        .map(|s| {
 5470                            let anchor = snapshot.anchor_after(s.head());
 5471                            s.map(|_| anchor)
 5472                        })
 5473                        .collect::<Vec<_>>()
 5474                };
 5475                buffer.edit(
 5476                    old_selections
 5477                        .iter()
 5478                        .map(|s| (s.start..s.end, text.clone())),
 5479                    autoindent_mode,
 5480                    cx,
 5481                );
 5482                anchors
 5483            });
 5484
 5485            linked_edits.apply(cx);
 5486
 5487            this.change_selections(Default::default(), window, cx, |s| {
 5488                s.select_anchors(selection_anchors);
 5489            });
 5490
 5491            if apply_linked_edits {
 5492                refresh_linked_ranges(this, window, cx);
 5493            }
 5494
 5495            cx.notify();
 5496        });
 5497    }
 5498
 5499    /// Collects linked edits for the current selections, pairing each linked
 5500    /// range with `text`.
 5501    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5502        let mut linked_edits = LinkedEdits::new();
 5503        if !self.linked_edit_ranges.is_empty() {
 5504            for selection in self.selections.disjoint_anchors() {
 5505                let start = selection.start.text_anchor;
 5506                let end = selection.end.text_anchor;
 5507                linked_edits.push(self, start..end, text.clone(), cx);
 5508            }
 5509        }
 5510        linked_edits
 5511    }
 5512
 5513    /// Deletes the content covered by the current selections and applies
 5514    /// linked edits.
 5515    pub fn delete_selections_with_linked_edits(
 5516        &mut self,
 5517        window: &mut Window,
 5518        cx: &mut Context<Self>,
 5519    ) {
 5520        self.replace_selections("", None, window, cx, true);
 5521    }
 5522
 5523    #[cfg(any(test, feature = "test-support"))]
 5524    pub fn set_linked_edit_ranges_for_testing(
 5525        &mut self,
 5526        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5527        cx: &mut Context<Self>,
 5528    ) -> Option<()> {
 5529        let Some((buffer, _)) = self
 5530            .buffer
 5531            .read(cx)
 5532            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5533        else {
 5534            return None;
 5535        };
 5536        let buffer = buffer.read(cx);
 5537        let buffer_id = buffer.remote_id();
 5538        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5539        for (base_range, linked_ranges_points) in ranges {
 5540            let base_anchor =
 5541                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5542            let linked_anchors = linked_ranges_points
 5543                .into_iter()
 5544                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5545                .collect();
 5546            linked_ranges.push((base_anchor, linked_anchors));
 5547        }
 5548        let mut map = HashMap::default();
 5549        map.insert(buffer_id, linked_ranges);
 5550        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5551        Some(())
 5552    }
 5553
 5554    fn trigger_completion_on_input(
 5555        &mut self,
 5556        text: &str,
 5557        trigger_in_words: bool,
 5558        window: &mut Window,
 5559        cx: &mut Context<Self>,
 5560    ) {
 5561        let completions_source = self
 5562            .context_menu
 5563            .borrow()
 5564            .as_ref()
 5565            .and_then(|menu| match menu {
 5566                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5567                CodeContextMenu::CodeActions(_) => None,
 5568            });
 5569
 5570        match completions_source {
 5571            Some(CompletionsMenuSource::Words { .. }) => {
 5572                self.open_or_update_completions_menu(
 5573                    Some(CompletionsMenuSource::Words {
 5574                        ignore_threshold: false,
 5575                    }),
 5576                    None,
 5577                    trigger_in_words,
 5578                    window,
 5579                    cx,
 5580                );
 5581            }
 5582            _ => self.open_or_update_completions_menu(
 5583                None,
 5584                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5585                true,
 5586                window,
 5587                cx,
 5588            ),
 5589        }
 5590    }
 5591
 5592    /// If any empty selections is touching the start of its innermost containing autoclose
 5593    /// region, expand it to select the brackets.
 5594    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5595        let selections = self
 5596            .selections
 5597            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5598        let buffer = self.buffer.read(cx).read(cx);
 5599        let new_selections = self
 5600            .selections_with_autoclose_regions(selections, &buffer)
 5601            .map(|(mut selection, region)| {
 5602                if !selection.is_empty() {
 5603                    return selection;
 5604                }
 5605
 5606                if let Some(region) = region {
 5607                    let mut range = region.range.to_offset(&buffer);
 5608                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5609                        range.start -= region.pair.start.len();
 5610                        if buffer.contains_str_at(range.start, &region.pair.start)
 5611                            && buffer.contains_str_at(range.end, &region.pair.end)
 5612                        {
 5613                            range.end += region.pair.end.len();
 5614                            selection.start = range.start;
 5615                            selection.end = range.end;
 5616
 5617                            return selection;
 5618                        }
 5619                    }
 5620                }
 5621
 5622                let always_treat_brackets_as_autoclosed = buffer
 5623                    .language_settings_at(selection.start, cx)
 5624                    .always_treat_brackets_as_autoclosed;
 5625
 5626                if !always_treat_brackets_as_autoclosed {
 5627                    return selection;
 5628                }
 5629
 5630                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5631                    for (pair, enabled) in scope.brackets() {
 5632                        if !enabled || !pair.close {
 5633                            continue;
 5634                        }
 5635
 5636                        if buffer.contains_str_at(selection.start, &pair.end) {
 5637                            let pair_start_len = pair.start.len();
 5638                            if buffer.contains_str_at(
 5639                                selection.start.saturating_sub_usize(pair_start_len),
 5640                                &pair.start,
 5641                            ) {
 5642                                selection.start -= pair_start_len;
 5643                                selection.end += pair.end.len();
 5644
 5645                                return selection;
 5646                            }
 5647                        }
 5648                    }
 5649                }
 5650
 5651                selection
 5652            })
 5653            .collect();
 5654
 5655        drop(buffer);
 5656        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5657            selections.select(new_selections)
 5658        });
 5659    }
 5660
 5661    /// Iterate the given selections, and for each one, find the smallest surrounding
 5662    /// autoclose region. This uses the ordering of the selections and the autoclose
 5663    /// regions to avoid repeated comparisons.
 5664    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5665        &'a self,
 5666        selections: impl IntoIterator<Item = Selection<D>>,
 5667        buffer: &'a MultiBufferSnapshot,
 5668    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5669        let mut i = 0;
 5670        let mut regions = self.autoclose_regions.as_slice();
 5671        selections.into_iter().map(move |selection| {
 5672            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5673
 5674            let mut enclosing = None;
 5675            while let Some(pair_state) = regions.get(i) {
 5676                if pair_state.range.end.to_offset(buffer) < range.start {
 5677                    regions = &regions[i + 1..];
 5678                    i = 0;
 5679                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5680                    break;
 5681                } else {
 5682                    if pair_state.selection_id == selection.id {
 5683                        enclosing = Some(pair_state);
 5684                    }
 5685                    i += 1;
 5686                }
 5687            }
 5688
 5689            (selection, enclosing)
 5690        })
 5691    }
 5692
 5693    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5694    fn invalidate_autoclose_regions(
 5695        &mut self,
 5696        mut selections: &[Selection<Anchor>],
 5697        buffer: &MultiBufferSnapshot,
 5698    ) {
 5699        self.autoclose_regions.retain(|state| {
 5700            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5701                return false;
 5702            }
 5703
 5704            let mut i = 0;
 5705            while let Some(selection) = selections.get(i) {
 5706                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5707                    selections = &selections[1..];
 5708                    continue;
 5709                }
 5710                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5711                    break;
 5712                }
 5713                if selection.id == state.selection_id {
 5714                    return true;
 5715                } else {
 5716                    i += 1;
 5717                }
 5718            }
 5719            false
 5720        });
 5721    }
 5722
 5723    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5724        let offset = position.to_offset(buffer);
 5725        let (word_range, kind) =
 5726            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5727        if offset > word_range.start && kind == Some(CharKind::Word) {
 5728            Some(
 5729                buffer
 5730                    .text_for_range(word_range.start..offset)
 5731                    .collect::<String>(),
 5732            )
 5733        } else {
 5734            None
 5735        }
 5736    }
 5737
 5738    pub fn visible_excerpts(
 5739        &self,
 5740        lsp_related_only: bool,
 5741        cx: &mut Context<Editor>,
 5742    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5743        let project = self.project().cloned();
 5744        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5745        let multi_buffer = self.buffer().read(cx);
 5746        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5747        let multi_buffer_visible_start = self
 5748            .scroll_manager
 5749            .native_anchor(&display_snapshot, cx)
 5750            .anchor
 5751            .to_point(&multi_buffer_snapshot);
 5752        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5753            multi_buffer_visible_start
 5754                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5755            Bias::Left,
 5756        );
 5757        multi_buffer_snapshot
 5758            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5759            .into_iter()
 5760            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5761            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5762                if !lsp_related_only {
 5763                    return Some((
 5764                        excerpt_id,
 5765                        (
 5766                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5767                            buffer.version().clone(),
 5768                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5769                        ),
 5770                    ));
 5771                }
 5772
 5773                let project = project.as_ref()?.read(cx);
 5774                let buffer_file = project::File::from_dyn(buffer.file())?;
 5775                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5776                let worktree_entry = buffer_worktree
 5777                    .read(cx)
 5778                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5779                if worktree_entry.is_ignored {
 5780                    None
 5781                } else {
 5782                    Some((
 5783                        excerpt_id,
 5784                        (
 5785                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5786                            buffer.version().clone(),
 5787                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5788                        ),
 5789                    ))
 5790                }
 5791            })
 5792            .collect()
 5793    }
 5794
 5795    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5796        TextLayoutDetails {
 5797            text_system: window.text_system().clone(),
 5798            editor_style: self.style.clone().unwrap(),
 5799            rem_size: window.rem_size(),
 5800            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5801            visible_rows: self.visible_line_count(),
 5802            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5803        }
 5804    }
 5805
 5806    fn trigger_on_type_formatting(
 5807        &self,
 5808        input: String,
 5809        window: &mut Window,
 5810        cx: &mut Context<Self>,
 5811    ) -> Option<Task<Result<()>>> {
 5812        if input.chars().count() != 1 {
 5813            return None;
 5814        }
 5815
 5816        let project = self.project()?;
 5817        let position = self.selections.newest_anchor().head();
 5818        let (buffer, buffer_position) = self
 5819            .buffer
 5820            .read(cx)
 5821            .text_anchor_for_position(position, cx)?;
 5822
 5823        let settings = language_settings::language_settings(
 5824            buffer
 5825                .read(cx)
 5826                .language_at(buffer_position)
 5827                .map(|l| l.name()),
 5828            buffer.read(cx).file(),
 5829            cx,
 5830        );
 5831        if !settings.use_on_type_format {
 5832            return None;
 5833        }
 5834
 5835        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5836        // hence we do LSP request & edit on host side only — add formats to host's history.
 5837        let push_to_lsp_host_history = true;
 5838        // If this is not the host, append its history with new edits.
 5839        let push_to_client_history = project.read(cx).is_via_collab();
 5840
 5841        let on_type_formatting = project.update(cx, |project, cx| {
 5842            project.on_type_format(
 5843                buffer.clone(),
 5844                buffer_position,
 5845                input,
 5846                push_to_lsp_host_history,
 5847                cx,
 5848            )
 5849        });
 5850        Some(cx.spawn_in(window, async move |editor, cx| {
 5851            if let Some(transaction) = on_type_formatting.await? {
 5852                if push_to_client_history {
 5853                    buffer.update(cx, |buffer, _| {
 5854                        buffer.push_transaction(transaction, Instant::now());
 5855                        buffer.finalize_last_transaction();
 5856                    });
 5857                }
 5858                editor.update(cx, |editor, cx| {
 5859                    editor.refresh_document_highlights(cx);
 5860                })?;
 5861            }
 5862            Ok(())
 5863        }))
 5864    }
 5865
 5866    pub fn show_word_completions(
 5867        &mut self,
 5868        _: &ShowWordCompletions,
 5869        window: &mut Window,
 5870        cx: &mut Context<Self>,
 5871    ) {
 5872        self.open_or_update_completions_menu(
 5873            Some(CompletionsMenuSource::Words {
 5874                ignore_threshold: true,
 5875            }),
 5876            None,
 5877            false,
 5878            window,
 5879            cx,
 5880        );
 5881    }
 5882
 5883    pub fn show_completions(
 5884        &mut self,
 5885        _: &ShowCompletions,
 5886        window: &mut Window,
 5887        cx: &mut Context<Self>,
 5888    ) {
 5889        self.open_or_update_completions_menu(None, None, false, window, cx);
 5890    }
 5891
 5892    fn open_or_update_completions_menu(
 5893        &mut self,
 5894        requested_source: Option<CompletionsMenuSource>,
 5895        trigger: Option<String>,
 5896        trigger_in_words: bool,
 5897        window: &mut Window,
 5898        cx: &mut Context<Self>,
 5899    ) {
 5900        if self.pending_rename.is_some() {
 5901            return;
 5902        }
 5903
 5904        let completions_source = self
 5905            .context_menu
 5906            .borrow()
 5907            .as_ref()
 5908            .and_then(|menu| match menu {
 5909                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5910                CodeContextMenu::CodeActions(_) => None,
 5911            });
 5912
 5913        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5914
 5915        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5916        // inserted and selected. To handle that case, the start of the selection is used so that
 5917        // the menu starts with all choices.
 5918        let position = self
 5919            .selections
 5920            .newest_anchor()
 5921            .start
 5922            .bias_right(&multibuffer_snapshot);
 5923        if position.diff_base_anchor.is_some() {
 5924            return;
 5925        }
 5926        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5927        let Some(buffer) = buffer_position
 5928            .text_anchor
 5929            .buffer_id
 5930            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5931        else {
 5932            return;
 5933        };
 5934        let buffer_snapshot = buffer.read(cx).snapshot();
 5935
 5936        let menu_is_open = matches!(
 5937            self.context_menu.borrow().as_ref(),
 5938            Some(CodeContextMenu::Completions(_))
 5939        );
 5940
 5941        let language = buffer_snapshot
 5942            .language_at(buffer_position.text_anchor)
 5943            .map(|language| language.name());
 5944
 5945        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5946        let completion_settings = language_settings.completions.clone();
 5947
 5948        let show_completions_on_input = self
 5949            .show_completions_on_input_override
 5950            .unwrap_or(language_settings.show_completions_on_input);
 5951        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5952            return;
 5953        }
 5954
 5955        let query: Option<Arc<String>> =
 5956            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5957                .map(|query| query.into());
 5958
 5959        drop(multibuffer_snapshot);
 5960
 5961        // Hide the current completions menu when query is empty. Without this, cached
 5962        // completions from before the trigger char may be reused (#32774).
 5963        if query.is_none() && menu_is_open {
 5964            self.hide_context_menu(window, cx);
 5965        }
 5966
 5967        let mut ignore_word_threshold = false;
 5968        let provider = match requested_source {
 5969            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5970            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5971                ignore_word_threshold = ignore_threshold;
 5972                None
 5973            }
 5974            Some(CompletionsMenuSource::SnippetChoices)
 5975            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5976                log::error!("bug: SnippetChoices requested_source is not handled");
 5977                None
 5978            }
 5979        };
 5980
 5981        let sort_completions = provider
 5982            .as_ref()
 5983            .is_some_and(|provider| provider.sort_completions());
 5984
 5985        let filter_completions = provider
 5986            .as_ref()
 5987            .is_none_or(|provider| provider.filter_completions());
 5988
 5989        let was_snippets_only = matches!(
 5990            completions_source,
 5991            Some(CompletionsMenuSource::SnippetsOnly)
 5992        );
 5993
 5994        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5995            if filter_completions {
 5996                menu.filter(
 5997                    query.clone().unwrap_or_default(),
 5998                    buffer_position.text_anchor,
 5999                    &buffer,
 6000                    provider.clone(),
 6001                    window,
 6002                    cx,
 6003                );
 6004            }
 6005            // When `is_incomplete` is false, no need to re-query completions when the current query
 6006            // is a suffix of the initial query.
 6007            let was_complete = !menu.is_incomplete;
 6008            if was_complete && !was_snippets_only {
 6009                // If the new query is a suffix of the old query (typing more characters) and
 6010                // the previous result was complete, the existing completions can be filtered.
 6011                //
 6012                // Note that snippet completions are always complete.
 6013                let query_matches = match (&menu.initial_query, &query) {
 6014                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6015                    (None, _) => true,
 6016                    _ => false,
 6017                };
 6018                if query_matches {
 6019                    let position_matches = if menu.initial_position == position {
 6020                        true
 6021                    } else {
 6022                        let snapshot = self.buffer.read(cx).read(cx);
 6023                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6024                    };
 6025                    if position_matches {
 6026                        return;
 6027                    }
 6028                }
 6029            }
 6030        };
 6031
 6032        let Anchor {
 6033            excerpt_id: buffer_excerpt_id,
 6034            text_anchor: buffer_position,
 6035            ..
 6036        } = buffer_position;
 6037
 6038        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6039            buffer_snapshot.surrounding_word(buffer_position, None)
 6040        {
 6041            let word_to_exclude = buffer_snapshot
 6042                .text_for_range(word_range.clone())
 6043                .collect::<String>();
 6044            (
 6045                buffer_snapshot.anchor_before(word_range.start)
 6046                    ..buffer_snapshot.anchor_after(buffer_position),
 6047                Some(word_to_exclude),
 6048            )
 6049        } else {
 6050            (buffer_position..buffer_position, None)
 6051        };
 6052
 6053        let show_completion_documentation = buffer_snapshot
 6054            .settings_at(buffer_position, cx)
 6055            .show_completion_documentation;
 6056
 6057        // The document can be large, so stay in reasonable bounds when searching for words,
 6058        // otherwise completion pop-up might be slow to appear.
 6059        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6060        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6061        let min_word_search = buffer_snapshot.clip_point(
 6062            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6063            Bias::Left,
 6064        );
 6065        let max_word_search = buffer_snapshot.clip_point(
 6066            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6067            Bias::Right,
 6068        );
 6069        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6070            ..buffer_snapshot.point_to_offset(max_word_search);
 6071
 6072        let skip_digits = query
 6073            .as_ref()
 6074            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6075
 6076        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6077            trigger.as_ref().is_none_or(|trigger| {
 6078                provider.is_completion_trigger(
 6079                    &buffer,
 6080                    position.text_anchor,
 6081                    trigger,
 6082                    trigger_in_words,
 6083                    cx,
 6084                )
 6085            })
 6086        });
 6087
 6088        let provider_responses = if let Some(provider) = &provider
 6089            && load_provider_completions
 6090        {
 6091            let trigger_character =
 6092                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6093            let completion_context = CompletionContext {
 6094                trigger_kind: match &trigger_character {
 6095                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6096                    None => CompletionTriggerKind::INVOKED,
 6097                },
 6098                trigger_character,
 6099            };
 6100
 6101            provider.completions(
 6102                buffer_excerpt_id,
 6103                &buffer,
 6104                buffer_position,
 6105                completion_context,
 6106                window,
 6107                cx,
 6108            )
 6109        } else {
 6110            Task::ready(Ok(Vec::new()))
 6111        };
 6112
 6113        let load_word_completions = if !self.word_completions_enabled {
 6114            false
 6115        } else if requested_source
 6116            == Some(CompletionsMenuSource::Words {
 6117                ignore_threshold: true,
 6118            })
 6119        {
 6120            true
 6121        } else {
 6122            load_provider_completions
 6123                && completion_settings.words != WordsCompletionMode::Disabled
 6124                && (ignore_word_threshold || {
 6125                    let words_min_length = completion_settings.words_min_length;
 6126                    // check whether word has at least `words_min_length` characters
 6127                    let query_chars = query.iter().flat_map(|q| q.chars());
 6128                    query_chars.take(words_min_length).count() == words_min_length
 6129                })
 6130        };
 6131
 6132        let mut words = if load_word_completions {
 6133            cx.background_spawn({
 6134                let buffer_snapshot = buffer_snapshot.clone();
 6135                async move {
 6136                    buffer_snapshot.words_in_range(WordsQuery {
 6137                        fuzzy_contents: None,
 6138                        range: word_search_range,
 6139                        skip_digits,
 6140                    })
 6141                }
 6142            })
 6143        } else {
 6144            Task::ready(BTreeMap::default())
 6145        };
 6146
 6147        let snippets = if let Some(provider) = &provider
 6148            && provider.show_snippets()
 6149            && let Some(project) = self.project()
 6150        {
 6151            let char_classifier = buffer_snapshot
 6152                .char_classifier_at(buffer_position)
 6153                .scope_context(Some(CharScopeContext::Completion));
 6154            project.update(cx, |project, cx| {
 6155                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6156            })
 6157        } else {
 6158            Task::ready(Ok(CompletionResponse {
 6159                completions: Vec::new(),
 6160                display_options: Default::default(),
 6161                is_incomplete: false,
 6162            }))
 6163        };
 6164
 6165        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6166
 6167        let id = post_inc(&mut self.next_completion_id);
 6168        let task = cx.spawn_in(window, async move |editor, cx| {
 6169            let Ok(()) = editor.update(cx, |this, _| {
 6170                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6171            }) else {
 6172                return;
 6173            };
 6174
 6175            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6176            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6177            let mut completions = Vec::new();
 6178            let mut is_incomplete = false;
 6179            let mut display_options: Option<CompletionDisplayOptions> = None;
 6180            if let Some(provider_responses) = provider_responses.await.log_err()
 6181                && !provider_responses.is_empty()
 6182            {
 6183                for response in provider_responses {
 6184                    completions.extend(response.completions);
 6185                    is_incomplete = is_incomplete || response.is_incomplete;
 6186                    match display_options.as_mut() {
 6187                        None => {
 6188                            display_options = Some(response.display_options);
 6189                        }
 6190                        Some(options) => options.merge(&response.display_options),
 6191                    }
 6192                }
 6193                if completion_settings.words == WordsCompletionMode::Fallback {
 6194                    words = Task::ready(BTreeMap::default());
 6195                }
 6196            }
 6197            let display_options = display_options.unwrap_or_default();
 6198
 6199            let mut words = words.await;
 6200            if let Some(word_to_exclude) = &word_to_exclude {
 6201                words.remove(word_to_exclude);
 6202            }
 6203            for lsp_completion in &completions {
 6204                words.remove(&lsp_completion.new_text);
 6205            }
 6206            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6207                replace_range: word_replace_range.clone(),
 6208                new_text: word.clone(),
 6209                label: CodeLabel::plain(word, None),
 6210                match_start: None,
 6211                snippet_deduplication_key: None,
 6212                icon_path: None,
 6213                documentation: None,
 6214                source: CompletionSource::BufferWord {
 6215                    word_range,
 6216                    resolved: false,
 6217                },
 6218                insert_text_mode: Some(InsertTextMode::AS_IS),
 6219                confirm: None,
 6220            }));
 6221
 6222            completions.extend(
 6223                snippets
 6224                    .await
 6225                    .into_iter()
 6226                    .flat_map(|response| response.completions),
 6227            );
 6228
 6229            let menu = if completions.is_empty() {
 6230                None
 6231            } else {
 6232                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6233                    let languages = editor
 6234                        .workspace
 6235                        .as_ref()
 6236                        .and_then(|(workspace, _)| workspace.upgrade())
 6237                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6238                    let menu = CompletionsMenu::new(
 6239                        id,
 6240                        requested_source.unwrap_or(if load_provider_completions {
 6241                            CompletionsMenuSource::Normal
 6242                        } else {
 6243                            CompletionsMenuSource::SnippetsOnly
 6244                        }),
 6245                        sort_completions,
 6246                        show_completion_documentation,
 6247                        position,
 6248                        query.clone(),
 6249                        is_incomplete,
 6250                        buffer.clone(),
 6251                        completions.into(),
 6252                        editor
 6253                            .context_menu()
 6254                            .borrow_mut()
 6255                            .as_ref()
 6256                            .map(|menu| menu.primary_scroll_handle()),
 6257                        display_options,
 6258                        snippet_sort_order,
 6259                        languages,
 6260                        language,
 6261                        cx,
 6262                    );
 6263
 6264                    let query = if filter_completions { query } else { None };
 6265                    let matches_task = menu.do_async_filtering(
 6266                        query.unwrap_or_default(),
 6267                        buffer_position,
 6268                        &buffer,
 6269                        cx,
 6270                    );
 6271                    (menu, matches_task)
 6272                }) else {
 6273                    return;
 6274                };
 6275
 6276                let matches = matches_task.await;
 6277
 6278                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6279                    // Newer menu already set, so exit.
 6280                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6281                        editor.context_menu.borrow().as_ref()
 6282                        && prev_menu.id > id
 6283                    {
 6284                        return;
 6285                    };
 6286
 6287                    // Only valid to take prev_menu because either the new menu is immediately set
 6288                    // below, or the menu is hidden.
 6289                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6290                        editor.context_menu.borrow_mut().take()
 6291                    {
 6292                        let position_matches =
 6293                            if prev_menu.initial_position == menu.initial_position {
 6294                                true
 6295                            } else {
 6296                                let snapshot = editor.buffer.read(cx).read(cx);
 6297                                prev_menu.initial_position.to_offset(&snapshot)
 6298                                    == menu.initial_position.to_offset(&snapshot)
 6299                            };
 6300                        if position_matches {
 6301                            // Preserve markdown cache before `set_filter_results` because it will
 6302                            // try to populate the documentation cache.
 6303                            menu.preserve_markdown_cache(prev_menu);
 6304                        }
 6305                    };
 6306
 6307                    menu.set_filter_results(matches, provider, window, cx);
 6308                }) else {
 6309                    return;
 6310                };
 6311
 6312                menu.visible().then_some(menu)
 6313            };
 6314
 6315            editor
 6316                .update_in(cx, |editor, window, cx| {
 6317                    if editor.focus_handle.is_focused(window)
 6318                        && let Some(menu) = menu
 6319                    {
 6320                        *editor.context_menu.borrow_mut() =
 6321                            Some(CodeContextMenu::Completions(menu));
 6322
 6323                        crate::hover_popover::hide_hover(editor, cx);
 6324                        if editor.show_edit_predictions_in_menu() {
 6325                            editor.update_visible_edit_prediction(window, cx);
 6326                        } else {
 6327                            editor
 6328                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6329                        }
 6330
 6331                        cx.notify();
 6332                        return;
 6333                    }
 6334
 6335                    if editor.completion_tasks.len() <= 1 {
 6336                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6337                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6338                        // If it was already hidden and we don't show edit predictions in the menu,
 6339                        // we should also show the edit prediction when available.
 6340                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6341                            editor.update_visible_edit_prediction(window, cx);
 6342                        }
 6343                    }
 6344                })
 6345                .ok();
 6346        });
 6347
 6348        self.completion_tasks.push((id, task));
 6349    }
 6350
 6351    #[cfg(any(test, feature = "test-support"))]
 6352    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6353        let menu = self.context_menu.borrow();
 6354        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6355            let completions = menu.completions.borrow();
 6356            Some(completions.to_vec())
 6357        } else {
 6358            None
 6359        }
 6360    }
 6361
 6362    pub fn with_completions_menu_matching_id<R>(
 6363        &self,
 6364        id: CompletionId,
 6365        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6366    ) -> R {
 6367        let mut context_menu = self.context_menu.borrow_mut();
 6368        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6369            return f(None);
 6370        };
 6371        if completions_menu.id != id {
 6372            return f(None);
 6373        }
 6374        f(Some(completions_menu))
 6375    }
 6376
 6377    pub fn confirm_completion(
 6378        &mut self,
 6379        action: &ConfirmCompletion,
 6380        window: &mut Window,
 6381        cx: &mut Context<Self>,
 6382    ) -> Option<Task<Result<()>>> {
 6383        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6384        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6385    }
 6386
 6387    pub fn confirm_completion_insert(
 6388        &mut self,
 6389        _: &ConfirmCompletionInsert,
 6390        window: &mut Window,
 6391        cx: &mut Context<Self>,
 6392    ) -> Option<Task<Result<()>>> {
 6393        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6394        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6395    }
 6396
 6397    pub fn confirm_completion_replace(
 6398        &mut self,
 6399        _: &ConfirmCompletionReplace,
 6400        window: &mut Window,
 6401        cx: &mut Context<Self>,
 6402    ) -> Option<Task<Result<()>>> {
 6403        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6404        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6405    }
 6406
 6407    pub fn compose_completion(
 6408        &mut self,
 6409        action: &ComposeCompletion,
 6410        window: &mut Window,
 6411        cx: &mut Context<Self>,
 6412    ) -> Option<Task<Result<()>>> {
 6413        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6414        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6415    }
 6416
 6417    fn do_completion(
 6418        &mut self,
 6419        item_ix: Option<usize>,
 6420        intent: CompletionIntent,
 6421        window: &mut Window,
 6422        cx: &mut Context<Editor>,
 6423    ) -> Option<Task<Result<()>>> {
 6424        use language::ToOffset as _;
 6425
 6426        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6427        else {
 6428            return None;
 6429        };
 6430
 6431        let candidate_id = {
 6432            let entries = completions_menu.entries.borrow();
 6433            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6434            if self.show_edit_predictions_in_menu() {
 6435                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6436            }
 6437            mat.candidate_id
 6438        };
 6439
 6440        let completion = completions_menu
 6441            .completions
 6442            .borrow()
 6443            .get(candidate_id)?
 6444            .clone();
 6445        cx.stop_propagation();
 6446
 6447        let buffer_handle = completions_menu.buffer.clone();
 6448
 6449        let CompletionEdit {
 6450            new_text,
 6451            snippet,
 6452            replace_range,
 6453        } = process_completion_for_edit(
 6454            &completion,
 6455            intent,
 6456            &buffer_handle,
 6457            &completions_menu.initial_position.text_anchor,
 6458            cx,
 6459        );
 6460
 6461        let buffer = buffer_handle.read(cx);
 6462        let snapshot = self.buffer.read(cx).snapshot(cx);
 6463        let newest_anchor = self.selections.newest_anchor();
 6464        let replace_range_multibuffer = {
 6465            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6466            excerpt.map_range_from_buffer(replace_range.clone())
 6467        };
 6468        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6469            return None;
 6470        }
 6471
 6472        let old_text = buffer
 6473            .text_for_range(replace_range.clone())
 6474            .collect::<String>();
 6475        let lookbehind = newest_anchor
 6476            .start
 6477            .text_anchor
 6478            .to_offset(buffer)
 6479            .saturating_sub(replace_range.start.0);
 6480        let lookahead = replace_range
 6481            .end
 6482            .0
 6483            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6484        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6485        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6486
 6487        let selections = self
 6488            .selections
 6489            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6490        let mut ranges = Vec::new();
 6491        let mut linked_edits = LinkedEdits::new();
 6492
 6493        let text: Arc<str> = new_text.clone().into();
 6494        for selection in &selections {
 6495            let range = if selection.id == newest_anchor.id {
 6496                replace_range_multibuffer.clone()
 6497            } else {
 6498                let mut range = selection.range();
 6499
 6500                // if prefix is present, don't duplicate it
 6501                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6502                    range.start = range.start.saturating_sub_usize(lookbehind);
 6503
 6504                    // if suffix is also present, mimic the newest cursor and replace it
 6505                    if selection.id != newest_anchor.id
 6506                        && snapshot.contains_str_at(range.end, suffix)
 6507                    {
 6508                        range.end += lookahead;
 6509                    }
 6510                }
 6511                range
 6512            };
 6513
 6514            ranges.push(range.clone());
 6515
 6516            if !self.linked_edit_ranges.is_empty() {
 6517                let start_anchor = snapshot.anchor_before(range.start);
 6518                let end_anchor = snapshot.anchor_after(range.end);
 6519                let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6520                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6521            }
 6522        }
 6523
 6524        let common_prefix_len = old_text
 6525            .chars()
 6526            .zip(new_text.chars())
 6527            .take_while(|(a, b)| a == b)
 6528            .map(|(a, _)| a.len_utf8())
 6529            .sum::<usize>();
 6530
 6531        cx.emit(EditorEvent::InputHandled {
 6532            utf16_range_to_replace: None,
 6533            text: new_text[common_prefix_len..].into(),
 6534        });
 6535
 6536        self.transact(window, cx, |editor, window, cx| {
 6537            if let Some(mut snippet) = snippet {
 6538                snippet.text = new_text.to_string();
 6539                editor
 6540                    .insert_snippet(&ranges, snippet, window, cx)
 6541                    .log_err();
 6542            } else {
 6543                editor.buffer.update(cx, |multi_buffer, cx| {
 6544                    let auto_indent = match completion.insert_text_mode {
 6545                        Some(InsertTextMode::AS_IS) => None,
 6546                        _ => editor.autoindent_mode.clone(),
 6547                    };
 6548                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6549                    multi_buffer.edit(edits, auto_indent, cx);
 6550                });
 6551            }
 6552            linked_edits.apply(cx);
 6553            editor.refresh_edit_prediction(true, false, window, cx);
 6554        });
 6555        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6556
 6557        let show_new_completions_on_confirm = completion
 6558            .confirm
 6559            .as_ref()
 6560            .is_some_and(|confirm| confirm(intent, window, cx));
 6561        if show_new_completions_on_confirm {
 6562            self.open_or_update_completions_menu(None, None, false, window, cx);
 6563        }
 6564
 6565        let provider = self.completion_provider.as_ref()?;
 6566
 6567        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6568        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6569            let CompletionSource::Lsp {
 6570                lsp_completion,
 6571                server_id,
 6572                ..
 6573            } = &completion.source
 6574            else {
 6575                return None;
 6576            };
 6577            let lsp_command = lsp_completion.command.as_ref()?;
 6578            let available_commands = lsp_store
 6579                .read(cx)
 6580                .lsp_server_capabilities
 6581                .get(server_id)
 6582                .and_then(|server_capabilities| {
 6583                    server_capabilities
 6584                        .execute_command_provider
 6585                        .as_ref()
 6586                        .map(|options| options.commands.as_slice())
 6587                })?;
 6588            if available_commands.contains(&lsp_command.command) {
 6589                Some(CodeAction {
 6590                    server_id: *server_id,
 6591                    range: language::Anchor::MIN..language::Anchor::MIN,
 6592                    lsp_action: LspAction::Command(lsp_command.clone()),
 6593                    resolved: false,
 6594                })
 6595            } else {
 6596                None
 6597            }
 6598        });
 6599
 6600        drop(completion);
 6601        let apply_edits = provider.apply_additional_edits_for_completion(
 6602            buffer_handle.clone(),
 6603            completions_menu.completions.clone(),
 6604            candidate_id,
 6605            true,
 6606            cx,
 6607        );
 6608
 6609        let editor_settings = EditorSettings::get_global(cx);
 6610        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6611            // After the code completion is finished, users often want to know what signatures are needed.
 6612            // so we should automatically call signature_help
 6613            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6614        }
 6615
 6616        Some(cx.spawn_in(window, async move |editor, cx| {
 6617            apply_edits.await?;
 6618
 6619            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6620                let title = command.lsp_action.title().to_owned();
 6621                let project_transaction = lsp_store
 6622                    .update(cx, |lsp_store, cx| {
 6623                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6624                    })
 6625                    .await
 6626                    .context("applying post-completion command")?;
 6627                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6628                    Self::open_project_transaction(
 6629                        &editor,
 6630                        workspace.downgrade(),
 6631                        project_transaction,
 6632                        title,
 6633                        cx,
 6634                    )
 6635                    .await?;
 6636                }
 6637            }
 6638
 6639            Ok(())
 6640        }))
 6641    }
 6642
 6643    pub fn toggle_code_actions(
 6644        &mut self,
 6645        action: &ToggleCodeActions,
 6646        window: &mut Window,
 6647        cx: &mut Context<Self>,
 6648    ) {
 6649        let quick_launch = action.quick_launch;
 6650        let mut context_menu = self.context_menu.borrow_mut();
 6651        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6652            if code_actions.deployed_from == action.deployed_from {
 6653                // Toggle if we're selecting the same one
 6654                *context_menu = None;
 6655                cx.notify();
 6656                return;
 6657            } else {
 6658                // Otherwise, clear it and start a new one
 6659                *context_menu = None;
 6660                cx.notify();
 6661            }
 6662        }
 6663        drop(context_menu);
 6664        let snapshot = self.snapshot(window, cx);
 6665        let deployed_from = action.deployed_from.clone();
 6666        let action = action.clone();
 6667        self.completion_tasks.clear();
 6668        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6669
 6670        let multibuffer_point = match &action.deployed_from {
 6671            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6672                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6673            }
 6674            _ => self
 6675                .selections
 6676                .newest::<Point>(&snapshot.display_snapshot)
 6677                .head(),
 6678        };
 6679        let Some((buffer, buffer_row)) = snapshot
 6680            .buffer_snapshot()
 6681            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6682            .and_then(|(buffer_snapshot, range)| {
 6683                self.buffer()
 6684                    .read(cx)
 6685                    .buffer(buffer_snapshot.remote_id())
 6686                    .map(|buffer| (buffer, range.start.row))
 6687            })
 6688        else {
 6689            return;
 6690        };
 6691        let buffer_id = buffer.read(cx).remote_id();
 6692        let tasks = self
 6693            .tasks
 6694            .get(&(buffer_id, buffer_row))
 6695            .map(|t| Arc::new(t.to_owned()));
 6696
 6697        if !self.focus_handle.is_focused(window) {
 6698            return;
 6699        }
 6700        let project = self.project.clone();
 6701
 6702        let code_actions_task = match deployed_from {
 6703            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6704            _ => self.code_actions(buffer_row, window, cx),
 6705        };
 6706
 6707        let runnable_task = match deployed_from {
 6708            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6709            _ => {
 6710                let mut task_context_task = Task::ready(None);
 6711                if let Some(tasks) = &tasks
 6712                    && let Some(project) = project
 6713                {
 6714                    task_context_task =
 6715                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6716                }
 6717
 6718                cx.spawn_in(window, {
 6719                    let buffer = buffer.clone();
 6720                    async move |editor, cx| {
 6721                        let task_context = task_context_task.await;
 6722
 6723                        let resolved_tasks =
 6724                            tasks
 6725                                .zip(task_context.clone())
 6726                                .map(|(tasks, task_context)| ResolvedTasks {
 6727                                    templates: tasks.resolve(&task_context).collect(),
 6728                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6729                                        multibuffer_point.row,
 6730                                        tasks.column,
 6731                                    )),
 6732                                });
 6733                        let debug_scenarios = editor
 6734                            .update(cx, |editor, cx| {
 6735                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6736                            })?
 6737                            .await;
 6738                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6739                    }
 6740                })
 6741            }
 6742        };
 6743
 6744        cx.spawn_in(window, async move |editor, cx| {
 6745            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6746            let code_actions = code_actions_task.await;
 6747            let spawn_straight_away = quick_launch
 6748                && resolved_tasks
 6749                    .as_ref()
 6750                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6751                && code_actions
 6752                    .as_ref()
 6753                    .is_none_or(|actions| actions.is_empty())
 6754                && debug_scenarios.is_empty();
 6755
 6756            editor.update_in(cx, |editor, window, cx| {
 6757                crate::hover_popover::hide_hover(editor, cx);
 6758                let actions = CodeActionContents::new(
 6759                    resolved_tasks,
 6760                    code_actions,
 6761                    debug_scenarios,
 6762                    task_context.unwrap_or_default(),
 6763                );
 6764
 6765                // Don't show the menu if there are no actions available
 6766                if actions.is_empty() {
 6767                    cx.notify();
 6768                    return Task::ready(Ok(()));
 6769                }
 6770
 6771                *editor.context_menu.borrow_mut() =
 6772                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6773                        buffer,
 6774                        actions,
 6775                        selected_item: Default::default(),
 6776                        scroll_handle: UniformListScrollHandle::default(),
 6777                        deployed_from,
 6778                    }));
 6779                cx.notify();
 6780                if spawn_straight_away
 6781                    && let Some(task) = editor.confirm_code_action(
 6782                        &ConfirmCodeAction { item_ix: Some(0) },
 6783                        window,
 6784                        cx,
 6785                    )
 6786                {
 6787                    return task;
 6788                }
 6789
 6790                Task::ready(Ok(()))
 6791            })
 6792        })
 6793        .detach_and_log_err(cx);
 6794    }
 6795
 6796    fn debug_scenarios(
 6797        &mut self,
 6798        resolved_tasks: &Option<ResolvedTasks>,
 6799        buffer: &Entity<Buffer>,
 6800        cx: &mut App,
 6801    ) -> Task<Vec<task::DebugScenario>> {
 6802        maybe!({
 6803            let project = self.project()?;
 6804            let dap_store = project.read(cx).dap_store();
 6805            let mut scenarios = vec![];
 6806            let resolved_tasks = resolved_tasks.as_ref()?;
 6807            let buffer = buffer.read(cx);
 6808            let language = buffer.language()?;
 6809            let file = buffer.file();
 6810            let debug_adapter = language_settings(language.name().into(), file, cx)
 6811                .debuggers
 6812                .first()
 6813                .map(SharedString::from)
 6814                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6815
 6816            dap_store.update(cx, |dap_store, cx| {
 6817                for (_, task) in &resolved_tasks.templates {
 6818                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6819                        task.original_task().clone(),
 6820                        debug_adapter.clone().into(),
 6821                        task.display_label().to_owned().into(),
 6822                        cx,
 6823                    );
 6824                    scenarios.push(maybe_scenario);
 6825                }
 6826            });
 6827            Some(cx.background_spawn(async move {
 6828                futures::future::join_all(scenarios)
 6829                    .await
 6830                    .into_iter()
 6831                    .flatten()
 6832                    .collect::<Vec<_>>()
 6833            }))
 6834        })
 6835        .unwrap_or_else(|| Task::ready(vec![]))
 6836    }
 6837
 6838    fn code_actions(
 6839        &mut self,
 6840        buffer_row: u32,
 6841        window: &mut Window,
 6842        cx: &mut Context<Self>,
 6843    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6844        let mut task = self.code_actions_task.take();
 6845        cx.spawn_in(window, async move |editor, cx| {
 6846            while let Some(prev_task) = task {
 6847                prev_task.await.log_err();
 6848                task = editor
 6849                    .update(cx, |this, _| this.code_actions_task.take())
 6850                    .ok()?;
 6851            }
 6852
 6853            editor
 6854                .update(cx, |editor, cx| {
 6855                    editor
 6856                        .available_code_actions
 6857                        .clone()
 6858                        .and_then(|(location, code_actions)| {
 6859                            let snapshot = location.buffer.read(cx).snapshot();
 6860                            let point_range = location.range.to_point(&snapshot);
 6861                            let point_range = point_range.start.row..=point_range.end.row;
 6862                            if point_range.contains(&buffer_row) {
 6863                                Some(code_actions)
 6864                            } else {
 6865                                None
 6866                            }
 6867                        })
 6868                })
 6869                .ok()
 6870                .flatten()
 6871        })
 6872    }
 6873
 6874    pub fn confirm_code_action(
 6875        &mut self,
 6876        action: &ConfirmCodeAction,
 6877        window: &mut Window,
 6878        cx: &mut Context<Self>,
 6879    ) -> Option<Task<Result<()>>> {
 6880        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6881
 6882        let actions_menu =
 6883            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6884                menu
 6885            } else {
 6886                return None;
 6887            };
 6888
 6889        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6890        let action = actions_menu.actions.get(action_ix)?;
 6891        let title = action.label();
 6892        let buffer = actions_menu.buffer;
 6893        let workspace = self.workspace()?;
 6894
 6895        match action {
 6896            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6897                workspace.update(cx, |workspace, cx| {
 6898                    workspace.schedule_resolved_task(
 6899                        task_source_kind,
 6900                        resolved_task,
 6901                        false,
 6902                        window,
 6903                        cx,
 6904                    );
 6905
 6906                    Some(Task::ready(Ok(())))
 6907                })
 6908            }
 6909            CodeActionsItem::CodeAction {
 6910                excerpt_id,
 6911                action,
 6912                provider,
 6913            } => {
 6914                let apply_code_action =
 6915                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6916                let workspace = workspace.downgrade();
 6917                Some(cx.spawn_in(window, async move |editor, cx| {
 6918                    let project_transaction = apply_code_action.await?;
 6919                    Self::open_project_transaction(
 6920                        &editor,
 6921                        workspace,
 6922                        project_transaction,
 6923                        title,
 6924                        cx,
 6925                    )
 6926                    .await
 6927                }))
 6928            }
 6929            CodeActionsItem::DebugScenario(scenario) => {
 6930                let context = actions_menu.actions.context.into();
 6931
 6932                workspace.update(cx, |workspace, cx| {
 6933                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6934                    workspace.start_debug_session(
 6935                        scenario,
 6936                        context,
 6937                        Some(buffer),
 6938                        None,
 6939                        window,
 6940                        cx,
 6941                    );
 6942                });
 6943                Some(Task::ready(Ok(())))
 6944            }
 6945        }
 6946    }
 6947
 6948    fn open_transaction_for_hidden_buffers(
 6949        workspace: Entity<Workspace>,
 6950        transaction: ProjectTransaction,
 6951        title: String,
 6952        window: &mut Window,
 6953        cx: &mut Context<Self>,
 6954    ) {
 6955        if transaction.0.is_empty() {
 6956            return;
 6957        }
 6958
 6959        let edited_buffers_already_open = {
 6960            let other_editors: Vec<Entity<Editor>> = workspace
 6961                .read(cx)
 6962                .panes()
 6963                .iter()
 6964                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6965                .filter(|editor| editor.entity_id() != cx.entity_id())
 6966                .collect();
 6967
 6968            transaction.0.keys().all(|buffer| {
 6969                other_editors.iter().any(|editor| {
 6970                    let multi_buffer = editor.read(cx).buffer();
 6971                    multi_buffer.read(cx).is_singleton()
 6972                        && multi_buffer
 6973                            .read(cx)
 6974                            .as_singleton()
 6975                            .map_or(false, |singleton| {
 6976                                singleton.entity_id() == buffer.entity_id()
 6977                            })
 6978                })
 6979            })
 6980        };
 6981        if !edited_buffers_already_open {
 6982            let workspace = workspace.downgrade();
 6983            cx.defer_in(window, move |_, window, cx| {
 6984                cx.spawn_in(window, async move |editor, cx| {
 6985                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6986                        .await
 6987                        .ok()
 6988                })
 6989                .detach();
 6990            });
 6991        }
 6992    }
 6993
 6994    pub async fn open_project_transaction(
 6995        editor: &WeakEntity<Editor>,
 6996        workspace: WeakEntity<Workspace>,
 6997        transaction: ProjectTransaction,
 6998        title: String,
 6999        cx: &mut AsyncWindowContext,
 7000    ) -> Result<()> {
 7001        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7002        cx.update(|_, cx| {
 7003            entries.sort_unstable_by_key(|(buffer, _)| {
 7004                buffer.read(cx).file().map(|f| f.path().clone())
 7005            });
 7006        })?;
 7007        if entries.is_empty() {
 7008            return Ok(());
 7009        }
 7010
 7011        // If the project transaction's edits are all contained within this editor, then
 7012        // avoid opening a new editor to display them.
 7013
 7014        if let [(buffer, transaction)] = &*entries {
 7015            let excerpt = editor.update(cx, |editor, cx| {
 7016                editor
 7017                    .buffer()
 7018                    .read(cx)
 7019                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7020            })?;
 7021            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7022                && excerpted_buffer == *buffer
 7023            {
 7024                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7025                    let excerpt_range = excerpt_range.to_offset(buffer);
 7026                    buffer
 7027                        .edited_ranges_for_transaction::<usize>(transaction)
 7028                        .all(|range| {
 7029                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7030                        })
 7031                });
 7032
 7033                if all_edits_within_excerpt {
 7034                    return Ok(());
 7035                }
 7036            }
 7037        }
 7038
 7039        let mut ranges_to_highlight = Vec::new();
 7040        let excerpt_buffer = cx.new(|cx| {
 7041            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7042            for (buffer_handle, transaction) in &entries {
 7043                let edited_ranges = buffer_handle
 7044                    .read(cx)
 7045                    .edited_ranges_for_transaction::<Point>(transaction)
 7046                    .collect::<Vec<_>>();
 7047                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7048                    PathKey::for_buffer(buffer_handle, cx),
 7049                    buffer_handle.clone(),
 7050                    edited_ranges,
 7051                    multibuffer_context_lines(cx),
 7052                    cx,
 7053                );
 7054
 7055                ranges_to_highlight.extend(ranges);
 7056            }
 7057            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7058            multibuffer
 7059        });
 7060
 7061        workspace.update_in(cx, |workspace, window, cx| {
 7062            let project = workspace.project().clone();
 7063            let editor =
 7064                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7065            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7066            editor.update(cx, |editor, cx| {
 7067                editor.highlight_background(
 7068                    HighlightKey::Editor,
 7069                    &ranges_to_highlight,
 7070                    |_, theme| theme.colors().editor_highlighted_line_background,
 7071                    cx,
 7072                );
 7073            });
 7074        })?;
 7075
 7076        Ok(())
 7077    }
 7078
 7079    pub fn clear_code_action_providers(&mut self) {
 7080        self.code_action_providers.clear();
 7081        self.available_code_actions.take();
 7082    }
 7083
 7084    pub fn add_code_action_provider(
 7085        &mut self,
 7086        provider: Rc<dyn CodeActionProvider>,
 7087        window: &mut Window,
 7088        cx: &mut Context<Self>,
 7089    ) {
 7090        if self
 7091            .code_action_providers
 7092            .iter()
 7093            .any(|existing_provider| existing_provider.id() == provider.id())
 7094        {
 7095            return;
 7096        }
 7097
 7098        self.code_action_providers.push(provider);
 7099        self.refresh_code_actions(window, cx);
 7100    }
 7101
 7102    pub fn remove_code_action_provider(
 7103        &mut self,
 7104        id: Arc<str>,
 7105        window: &mut Window,
 7106        cx: &mut Context<Self>,
 7107    ) {
 7108        self.code_action_providers
 7109            .retain(|provider| provider.id() != id);
 7110        self.refresh_code_actions(window, cx);
 7111    }
 7112
 7113    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7114        !self.code_action_providers.is_empty()
 7115            && EditorSettings::get_global(cx).toolbar.code_actions
 7116    }
 7117
 7118    pub fn has_available_code_actions(&self) -> bool {
 7119        self.available_code_actions
 7120            .as_ref()
 7121            .is_some_and(|(_, actions)| !actions.is_empty())
 7122    }
 7123
 7124    fn render_inline_code_actions(
 7125        &self,
 7126        icon_size: ui::IconSize,
 7127        display_row: DisplayRow,
 7128        is_active: bool,
 7129        cx: &mut Context<Self>,
 7130    ) -> AnyElement {
 7131        let show_tooltip = !self.context_menu_visible();
 7132        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7133            .icon_size(icon_size)
 7134            .shape(ui::IconButtonShape::Square)
 7135            .icon_color(ui::Color::Hidden)
 7136            .toggle_state(is_active)
 7137            .when(show_tooltip, |this| {
 7138                this.tooltip({
 7139                    let focus_handle = self.focus_handle.clone();
 7140                    move |_window, cx| {
 7141                        Tooltip::for_action_in(
 7142                            "Toggle Code Actions",
 7143                            &ToggleCodeActions {
 7144                                deployed_from: None,
 7145                                quick_launch: false,
 7146                            },
 7147                            &focus_handle,
 7148                            cx,
 7149                        )
 7150                    }
 7151                })
 7152            })
 7153            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7154                window.focus(&editor.focus_handle(cx), cx);
 7155                editor.toggle_code_actions(
 7156                    &crate::actions::ToggleCodeActions {
 7157                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7158                            display_row,
 7159                        )),
 7160                        quick_launch: false,
 7161                    },
 7162                    window,
 7163                    cx,
 7164                );
 7165            }))
 7166            .into_any_element()
 7167    }
 7168
 7169    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7170        &self.context_menu
 7171    }
 7172
 7173    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7174        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7175            cx.background_executor()
 7176                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7177                .await;
 7178
 7179            let (start_buffer, start, _, end, newest_selection) = this
 7180                .update(cx, |this, cx| {
 7181                    let newest_selection = this.selections.newest_anchor().clone();
 7182                    if newest_selection.head().diff_base_anchor.is_some() {
 7183                        return None;
 7184                    }
 7185                    let display_snapshot = this.display_snapshot(cx);
 7186                    let newest_selection_adjusted =
 7187                        this.selections.newest_adjusted(&display_snapshot);
 7188                    let buffer = this.buffer.read(cx);
 7189
 7190                    let (start_buffer, start) =
 7191                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7192                    let (end_buffer, end) =
 7193                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7194
 7195                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7196                })?
 7197                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7198                .context(
 7199                    "Expected selection to lie in a single buffer when refreshing code actions",
 7200                )?;
 7201            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7202                let providers = this.code_action_providers.clone();
 7203                let tasks = this
 7204                    .code_action_providers
 7205                    .iter()
 7206                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7207                    .collect::<Vec<_>>();
 7208                (providers, tasks)
 7209            })?;
 7210
 7211            let mut actions = Vec::new();
 7212            for (provider, provider_actions) in
 7213                providers.into_iter().zip(future::join_all(tasks).await)
 7214            {
 7215                if let Some(provider_actions) = provider_actions.log_err() {
 7216                    actions.extend(provider_actions.into_iter().map(|action| {
 7217                        AvailableCodeAction {
 7218                            excerpt_id: newest_selection.start.excerpt_id,
 7219                            action,
 7220                            provider: provider.clone(),
 7221                        }
 7222                    }));
 7223                }
 7224            }
 7225
 7226            this.update(cx, |this, cx| {
 7227                this.available_code_actions = if actions.is_empty() {
 7228                    None
 7229                } else {
 7230                    Some((
 7231                        Location {
 7232                            buffer: start_buffer,
 7233                            range: start..end,
 7234                        },
 7235                        actions.into(),
 7236                    ))
 7237                };
 7238                cx.notify();
 7239            })
 7240        }));
 7241    }
 7242
 7243    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7244        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7245            self.show_git_blame_inline = false;
 7246
 7247            self.show_git_blame_inline_delay_task =
 7248                Some(cx.spawn_in(window, async move |this, cx| {
 7249                    cx.background_executor().timer(delay).await;
 7250
 7251                    this.update(cx, |this, cx| {
 7252                        this.show_git_blame_inline = true;
 7253                        cx.notify();
 7254                    })
 7255                    .log_err();
 7256                }));
 7257        }
 7258    }
 7259
 7260    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7261        let snapshot = self.snapshot(window, cx);
 7262        let cursor = self
 7263            .selections
 7264            .newest::<Point>(&snapshot.display_snapshot)
 7265            .head();
 7266        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7267        else {
 7268            return;
 7269        };
 7270
 7271        if self.blame.is_none() {
 7272            self.start_git_blame(true, window, cx);
 7273        }
 7274        let Some(blame) = self.blame.as_ref() else {
 7275            return;
 7276        };
 7277
 7278        let row_info = RowInfo {
 7279            buffer_id: Some(buffer.remote_id()),
 7280            buffer_row: Some(point.row),
 7281            ..Default::default()
 7282        };
 7283        let Some((buffer, blame_entry)) = blame
 7284            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7285            .flatten()
 7286        else {
 7287            return;
 7288        };
 7289
 7290        let anchor = self.selections.newest_anchor().head();
 7291        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7292        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7293            self.show_blame_popover(
 7294                buffer,
 7295                &blame_entry,
 7296                position + last_bounds.origin,
 7297                true,
 7298                cx,
 7299            );
 7300        };
 7301    }
 7302
 7303    fn show_blame_popover(
 7304        &mut self,
 7305        buffer: BufferId,
 7306        blame_entry: &BlameEntry,
 7307        position: gpui::Point<Pixels>,
 7308        ignore_timeout: bool,
 7309        cx: &mut Context<Self>,
 7310    ) {
 7311        if let Some(state) = &mut self.inline_blame_popover {
 7312            state.hide_task.take();
 7313        } else {
 7314            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7315            let blame_entry = blame_entry.clone();
 7316            let show_task = cx.spawn(async move |editor, cx| {
 7317                if !ignore_timeout {
 7318                    cx.background_executor()
 7319                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7320                        .await;
 7321                }
 7322                editor
 7323                    .update(cx, |editor, cx| {
 7324                        editor.inline_blame_popover_show_task.take();
 7325                        let Some(blame) = editor.blame.as_ref() else {
 7326                            return;
 7327                        };
 7328                        let blame = blame.read(cx);
 7329                        let details = blame.details_for_entry(buffer, &blame_entry);
 7330                        let markdown = cx.new(|cx| {
 7331                            Markdown::new(
 7332                                details
 7333                                    .as_ref()
 7334                                    .map(|message| message.message.clone())
 7335                                    .unwrap_or_default(),
 7336                                None,
 7337                                None,
 7338                                cx,
 7339                            )
 7340                        });
 7341                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7342                            position,
 7343                            hide_task: None,
 7344                            popover_bounds: None,
 7345                            popover_state: InlineBlamePopoverState {
 7346                                scroll_handle: ScrollHandle::new(),
 7347                                commit_message: details,
 7348                                markdown,
 7349                            },
 7350                            keyboard_grace: ignore_timeout,
 7351                        });
 7352                        cx.notify();
 7353                    })
 7354                    .ok();
 7355            });
 7356            self.inline_blame_popover_show_task = Some(show_task);
 7357        }
 7358    }
 7359
 7360    pub fn has_mouse_context_menu(&self) -> bool {
 7361        self.mouse_context_menu.is_some()
 7362    }
 7363
 7364    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7365        self.inline_blame_popover_show_task.take();
 7366        if let Some(state) = &mut self.inline_blame_popover {
 7367            let hide_task = cx.spawn(async move |editor, cx| {
 7368                if !ignore_timeout {
 7369                    cx.background_executor()
 7370                        .timer(std::time::Duration::from_millis(100))
 7371                        .await;
 7372                }
 7373                editor
 7374                    .update(cx, |editor, cx| {
 7375                        editor.inline_blame_popover.take();
 7376                        cx.notify();
 7377                    })
 7378                    .ok();
 7379            });
 7380            state.hide_task = Some(hide_task);
 7381            true
 7382        } else {
 7383            false
 7384        }
 7385    }
 7386
 7387    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7388        if self.pending_rename.is_some() {
 7389            return None;
 7390        }
 7391
 7392        let provider = self.semantics_provider.clone()?;
 7393        let buffer = self.buffer.read(cx);
 7394        let newest_selection = self.selections.newest_anchor().clone();
 7395        let cursor_position = newest_selection.head();
 7396        let (cursor_buffer, cursor_buffer_position) =
 7397            buffer.text_anchor_for_position(cursor_position, cx)?;
 7398        let (tail_buffer, tail_buffer_position) =
 7399            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7400        if cursor_buffer != tail_buffer {
 7401            return None;
 7402        }
 7403
 7404        let snapshot = cursor_buffer.read(cx).snapshot();
 7405        let word_ranges = cx.background_spawn(async move {
 7406            // this might look odd to put on the background thread, but
 7407            // `surrounding_word` can be quite expensive as it calls into
 7408            // tree-sitter language scopes
 7409            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7410            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7411            (start_word_range, end_word_range)
 7412        });
 7413
 7414        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7415        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7416            let (start_word_range, end_word_range) = word_ranges.await;
 7417            if start_word_range != end_word_range {
 7418                this.update(cx, |this, cx| {
 7419                    this.document_highlights_task.take();
 7420                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7421                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7422                })
 7423                .ok();
 7424                return;
 7425            }
 7426            cx.background_executor()
 7427                .timer(Duration::from_millis(debounce))
 7428                .await;
 7429
 7430            let highlights = if let Some(highlights) = cx.update(|cx| {
 7431                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7432            }) {
 7433                highlights.await.log_err()
 7434            } else {
 7435                None
 7436            };
 7437
 7438            if let Some(highlights) = highlights {
 7439                this.update(cx, |this, cx| {
 7440                    if this.pending_rename.is_some() {
 7441                        return;
 7442                    }
 7443
 7444                    let buffer = this.buffer.read(cx);
 7445                    if buffer
 7446                        .text_anchor_for_position(cursor_position, cx)
 7447                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7448                    {
 7449                        return;
 7450                    }
 7451
 7452                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7453                    let mut write_ranges = Vec::new();
 7454                    let mut read_ranges = Vec::new();
 7455                    for highlight in highlights {
 7456                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7457                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7458                        {
 7459                            let start = highlight
 7460                                .range
 7461                                .start
 7462                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7463                            let end = highlight
 7464                                .range
 7465                                .end
 7466                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7467                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7468                                continue;
 7469                            }
 7470
 7471                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7472                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7473                                write_ranges.push(range);
 7474                            } else {
 7475                                read_ranges.push(range);
 7476                            }
 7477                        }
 7478                    }
 7479
 7480                    this.highlight_background(
 7481                        HighlightKey::DocumentHighlightRead,
 7482                        &read_ranges,
 7483                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7484                        cx,
 7485                    );
 7486                    this.highlight_background(
 7487                        HighlightKey::DocumentHighlightWrite,
 7488                        &write_ranges,
 7489                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7490                        cx,
 7491                    );
 7492                    cx.notify();
 7493                })
 7494                .log_err();
 7495            }
 7496        }));
 7497        None
 7498    }
 7499
 7500    fn prepare_highlight_query_from_selection(
 7501        &mut self,
 7502        window: &Window,
 7503        cx: &mut Context<Editor>,
 7504    ) -> Option<(String, Range<Anchor>)> {
 7505        if matches!(self.mode, EditorMode::SingleLine) {
 7506            return None;
 7507        }
 7508        if !EditorSettings::get_global(cx).selection_highlight {
 7509            return None;
 7510        }
 7511        if self.selections.count() != 1 || self.selections.line_mode() {
 7512            return None;
 7513        }
 7514        let snapshot = self.snapshot(window, cx);
 7515        let selection = self.selections.newest::<Point>(&snapshot);
 7516        // If the selection spans multiple rows OR it is empty
 7517        if selection.start.row != selection.end.row
 7518            || selection.start.column == selection.end.column
 7519        {
 7520            return None;
 7521        }
 7522        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7523        let query = snapshot
 7524            .buffer_snapshot()
 7525            .text_for_range(selection_anchor_range.clone())
 7526            .collect::<String>();
 7527        if query.trim().is_empty() {
 7528            return None;
 7529        }
 7530        Some((query, selection_anchor_range))
 7531    }
 7532
 7533    #[ztracing::instrument(skip_all)]
 7534    fn update_selection_occurrence_highlights(
 7535        &mut self,
 7536        query_text: String,
 7537        query_range: Range<Anchor>,
 7538        multi_buffer_range_to_query: Range<Point>,
 7539        use_debounce: bool,
 7540        window: &mut Window,
 7541        cx: &mut Context<Editor>,
 7542    ) -> Task<()> {
 7543        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7544        cx.spawn_in(window, async move |editor, cx| {
 7545            if use_debounce {
 7546                cx.background_executor()
 7547                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7548                    .await;
 7549            }
 7550            let match_task = cx.background_spawn(async move {
 7551                let buffer_ranges = multi_buffer_snapshot
 7552                    .range_to_buffer_ranges(
 7553                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7554                    )
 7555                    .into_iter()
 7556                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7557                let mut match_ranges = Vec::new();
 7558                let Ok(regex) = project::search::SearchQuery::text(
 7559                    query_text.clone(),
 7560                    false,
 7561                    false,
 7562                    false,
 7563                    Default::default(),
 7564                    Default::default(),
 7565                    false,
 7566                    None,
 7567                ) else {
 7568                    return Vec::default();
 7569                };
 7570                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7571                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7572                    match_ranges.extend(
 7573                        regex
 7574                            .search(
 7575                                buffer_snapshot,
 7576                                Some(search_range.start.0..search_range.end.0),
 7577                            )
 7578                            .await
 7579                            .into_iter()
 7580                            .filter_map(|match_range| {
 7581                                let match_start = buffer_snapshot
 7582                                    .anchor_after(search_range.start + match_range.start);
 7583                                let match_end = buffer_snapshot
 7584                                    .anchor_before(search_range.start + match_range.end);
 7585                                let match_anchor_range =
 7586                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7587                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7588                            }),
 7589                    );
 7590                }
 7591                match_ranges
 7592            });
 7593            let match_ranges = match_task.await;
 7594            editor
 7595                .update_in(cx, |editor, _, cx| {
 7596                    if use_debounce {
 7597                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7598                        editor.debounced_selection_highlight_complete = true;
 7599                    } else if editor.debounced_selection_highlight_complete {
 7600                        return;
 7601                    }
 7602                    if !match_ranges.is_empty() {
 7603                        editor.highlight_background(
 7604                            HighlightKey::SelectedTextHighlight,
 7605                            &match_ranges,
 7606                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7607                            cx,
 7608                        )
 7609                    }
 7610                })
 7611                .log_err();
 7612        })
 7613    }
 7614
 7615    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7616        struct NewlineFold;
 7617        let type_id = std::any::TypeId::of::<NewlineFold>();
 7618        if !self.mode.is_single_line() {
 7619            return;
 7620        }
 7621        let snapshot = self.snapshot(window, cx);
 7622        if snapshot.buffer_snapshot().max_point().row == 0 {
 7623            return;
 7624        }
 7625        let task = cx.background_spawn(async move {
 7626            let new_newlines = snapshot
 7627                .buffer_chars_at(MultiBufferOffset(0))
 7628                .filter_map(|(c, i)| {
 7629                    if c == '\n' {
 7630                        Some(
 7631                            snapshot.buffer_snapshot().anchor_after(i)
 7632                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7633                        )
 7634                    } else {
 7635                        None
 7636                    }
 7637                })
 7638                .collect::<Vec<_>>();
 7639            let existing_newlines = snapshot
 7640                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7641                .filter_map(|fold| {
 7642                    if fold.placeholder.type_tag == Some(type_id) {
 7643                        Some(fold.range.start..fold.range.end)
 7644                    } else {
 7645                        None
 7646                    }
 7647                })
 7648                .collect::<Vec<_>>();
 7649
 7650            (new_newlines, existing_newlines)
 7651        });
 7652        self.folding_newlines = cx.spawn(async move |this, cx| {
 7653            let (new_newlines, existing_newlines) = task.await;
 7654            if new_newlines == existing_newlines {
 7655                return;
 7656            }
 7657            let placeholder = FoldPlaceholder {
 7658                render: Arc::new(move |_, _, cx| {
 7659                    div()
 7660                        .bg(cx.theme().status().hint_background)
 7661                        .border_b_1()
 7662                        .size_full()
 7663                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7664                        .border_color(cx.theme().status().hint)
 7665                        .child("\\n")
 7666                        .into_any()
 7667                }),
 7668                constrain_width: false,
 7669                merge_adjacent: false,
 7670                type_tag: Some(type_id),
 7671                collapsed_text: None,
 7672            };
 7673            let creases = new_newlines
 7674                .into_iter()
 7675                .map(|range| Crease::simple(range, placeholder.clone()))
 7676                .collect();
 7677            this.update(cx, |this, cx| {
 7678                this.display_map.update(cx, |display_map, cx| {
 7679                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7680                    display_map.fold(creases, cx);
 7681                });
 7682            })
 7683            .ok();
 7684        });
 7685    }
 7686
 7687    #[ztracing::instrument(skip_all)]
 7688    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7689        if !self.mode.is_full() {
 7690            return;
 7691        }
 7692        let cursor = self.selections.newest_anchor().head();
 7693        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7694
 7695        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7696            self.outline_symbols_at_cursor =
 7697                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7698            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7699            cx.notify();
 7700        } else {
 7701            let syntax = cx.theme().syntax().clone();
 7702            let background_task = cx.background_spawn(async move {
 7703                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7704            });
 7705            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7706                cx.spawn(async move |this, cx| {
 7707                    let symbols = background_task.await;
 7708                    this.update(cx, |this, cx| {
 7709                        this.outline_symbols_at_cursor = symbols;
 7710                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7711                        cx.notify();
 7712                    })
 7713                    .ok();
 7714                });
 7715        }
 7716    }
 7717
 7718    #[ztracing::instrument(skip_all)]
 7719    fn refresh_selected_text_highlights(
 7720        &mut self,
 7721        on_buffer_edit: bool,
 7722        window: &mut Window,
 7723        cx: &mut Context<Editor>,
 7724    ) {
 7725        let Some((query_text, query_range)) =
 7726            self.prepare_highlight_query_from_selection(window, cx)
 7727        else {
 7728            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7729            self.quick_selection_highlight_task.take();
 7730            self.debounced_selection_highlight_task.take();
 7731            self.debounced_selection_highlight_complete = false;
 7732            return;
 7733        };
 7734        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7735        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7736        let query_changed = self
 7737            .quick_selection_highlight_task
 7738            .as_ref()
 7739            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7740        if query_changed {
 7741            self.debounced_selection_highlight_complete = false;
 7742        }
 7743        if on_buffer_edit || query_changed {
 7744            let multi_buffer_visible_start = self
 7745                .scroll_manager
 7746                .native_anchor(&display_snapshot, cx)
 7747                .anchor
 7748                .to_point(&multi_buffer_snapshot);
 7749            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7750                multi_buffer_visible_start
 7751                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7752                Bias::Left,
 7753            );
 7754            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7755            self.quick_selection_highlight_task = Some((
 7756                query_range.clone(),
 7757                self.update_selection_occurrence_highlights(
 7758                    query_text.clone(),
 7759                    query_range.clone(),
 7760                    multi_buffer_visible_range,
 7761                    false,
 7762                    window,
 7763                    cx,
 7764                ),
 7765            ));
 7766        }
 7767        if on_buffer_edit
 7768            || self
 7769                .debounced_selection_highlight_task
 7770                .as_ref()
 7771                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7772        {
 7773            let multi_buffer_start = multi_buffer_snapshot
 7774                .anchor_before(MultiBufferOffset(0))
 7775                .to_point(&multi_buffer_snapshot);
 7776            let multi_buffer_end = multi_buffer_snapshot
 7777                .anchor_after(multi_buffer_snapshot.len())
 7778                .to_point(&multi_buffer_snapshot);
 7779            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7780            self.debounced_selection_highlight_task = Some((
 7781                query_range.clone(),
 7782                self.update_selection_occurrence_highlights(
 7783                    query_text,
 7784                    query_range,
 7785                    multi_buffer_full_range,
 7786                    true,
 7787                    window,
 7788                    cx,
 7789                ),
 7790            ));
 7791        }
 7792    }
 7793
 7794    pub fn refresh_edit_prediction(
 7795        &mut self,
 7796        debounce: bool,
 7797        user_requested: bool,
 7798        window: &mut Window,
 7799        cx: &mut Context<Self>,
 7800    ) -> Option<()> {
 7801        let provider = self.edit_prediction_provider()?;
 7802        let cursor = self.selections.newest_anchor().head();
 7803        let (buffer, cursor_buffer_position) =
 7804            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7805
 7806        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7807            return None;
 7808        }
 7809
 7810        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7811            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7812            return None;
 7813        }
 7814
 7815        self.update_visible_edit_prediction(window, cx);
 7816
 7817        if !user_requested
 7818            && (!self.should_show_edit_predictions()
 7819                || !self.is_focused(window)
 7820                || buffer.read(cx).is_empty())
 7821        {
 7822            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7823            return None;
 7824        }
 7825
 7826        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7827        Some(())
 7828    }
 7829
 7830    fn show_edit_predictions_in_menu(&self) -> bool {
 7831        match self.edit_prediction_settings {
 7832            EditPredictionSettings::Disabled => false,
 7833            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7834        }
 7835    }
 7836
 7837    pub fn edit_predictions_enabled(&self) -> bool {
 7838        match self.edit_prediction_settings {
 7839            EditPredictionSettings::Disabled => false,
 7840            EditPredictionSettings::Enabled { .. } => true,
 7841        }
 7842    }
 7843
 7844    fn edit_prediction_requires_modifier(&self) -> bool {
 7845        match self.edit_prediction_settings {
 7846            EditPredictionSettings::Disabled => false,
 7847            EditPredictionSettings::Enabled {
 7848                preview_requires_modifier,
 7849                ..
 7850            } => preview_requires_modifier,
 7851        }
 7852    }
 7853
 7854    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7855        if self.edit_prediction_provider.is_none() {
 7856            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7857            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7858            return;
 7859        }
 7860
 7861        let selection = self.selections.newest_anchor();
 7862        let cursor = selection.head();
 7863
 7864        if let Some((buffer, cursor_buffer_position)) =
 7865            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7866        {
 7867            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7868                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7869                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7870                return;
 7871            }
 7872            self.edit_prediction_settings =
 7873                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7874        }
 7875    }
 7876
 7877    fn edit_prediction_settings_at_position(
 7878        &self,
 7879        buffer: &Entity<Buffer>,
 7880        buffer_position: language::Anchor,
 7881        cx: &App,
 7882    ) -> EditPredictionSettings {
 7883        if !self.mode.is_full()
 7884            || !self.show_edit_predictions_override.unwrap_or(true)
 7885            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7886        {
 7887            return EditPredictionSettings::Disabled;
 7888        }
 7889
 7890        let buffer = buffer.read(cx);
 7891
 7892        let file = buffer.file();
 7893
 7894        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7895            return EditPredictionSettings::Disabled;
 7896        };
 7897
 7898        let by_provider = matches!(
 7899            self.menu_edit_predictions_policy,
 7900            MenuEditPredictionsPolicy::ByProvider
 7901        );
 7902
 7903        let show_in_menu = by_provider
 7904            && self
 7905                .edit_prediction_provider
 7906                .as_ref()
 7907                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7908
 7909        let preview_requires_modifier =
 7910            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7911
 7912        EditPredictionSettings::Enabled {
 7913            show_in_menu,
 7914            preview_requires_modifier,
 7915        }
 7916    }
 7917
 7918    fn should_show_edit_predictions(&self) -> bool {
 7919        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7920    }
 7921
 7922    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7923        matches!(
 7924            self.edit_prediction_preview,
 7925            EditPredictionPreview::Active { .. }
 7926        )
 7927    }
 7928
 7929    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7930        let cursor = self.selections.newest_anchor().head();
 7931        if let Some((buffer, cursor_position)) =
 7932            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7933        {
 7934            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7935        } else {
 7936            false
 7937        }
 7938    }
 7939
 7940    pub fn supports_minimap(&self, cx: &App) -> bool {
 7941        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7942    }
 7943
 7944    fn edit_predictions_enabled_in_buffer(
 7945        &self,
 7946        buffer: &Entity<Buffer>,
 7947        buffer_position: language::Anchor,
 7948        cx: &App,
 7949    ) -> bool {
 7950        maybe!({
 7951            if self.read_only(cx) {
 7952                return Some(false);
 7953            }
 7954            let provider = self.edit_prediction_provider()?;
 7955            if !provider.is_enabled(buffer, buffer_position, cx) {
 7956                return Some(false);
 7957            }
 7958            let buffer = buffer.read(cx);
 7959            let Some(file) = buffer.file() else {
 7960                return Some(true);
 7961            };
 7962            let settings = all_language_settings(Some(file), cx);
 7963            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7964        })
 7965        .unwrap_or(false)
 7966    }
 7967
 7968    pub fn show_edit_prediction(
 7969        &mut self,
 7970        _: &ShowEditPrediction,
 7971        window: &mut Window,
 7972        cx: &mut Context<Self>,
 7973    ) {
 7974        if !self.has_active_edit_prediction() {
 7975            self.refresh_edit_prediction(false, true, window, cx);
 7976            return;
 7977        }
 7978
 7979        self.update_visible_edit_prediction(window, cx);
 7980    }
 7981
 7982    pub fn display_cursor_names(
 7983        &mut self,
 7984        _: &DisplayCursorNames,
 7985        window: &mut Window,
 7986        cx: &mut Context<Self>,
 7987    ) {
 7988        self.show_cursor_names(window, cx);
 7989    }
 7990
 7991    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7992        self.show_cursor_names = true;
 7993        cx.notify();
 7994        cx.spawn_in(window, async move |this, cx| {
 7995            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7996            this.update(cx, |this, cx| {
 7997                this.show_cursor_names = false;
 7998                cx.notify()
 7999            })
 8000            .ok()
 8001        })
 8002        .detach();
 8003    }
 8004
 8005    pub fn accept_partial_edit_prediction(
 8006        &mut self,
 8007        granularity: EditPredictionGranularity,
 8008        window: &mut Window,
 8009        cx: &mut Context<Self>,
 8010    ) {
 8011        if self.show_edit_predictions_in_menu() {
 8012            self.hide_context_menu(window, cx);
 8013        }
 8014
 8015        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8016            return;
 8017        };
 8018
 8019        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8020            return;
 8021        }
 8022
 8023        match &active_edit_prediction.completion {
 8024            EditPrediction::MoveWithin { target, .. } => {
 8025                let target = *target;
 8026
 8027                if matches!(granularity, EditPredictionGranularity::Full) {
 8028                    if let Some(position_map) = &self.last_position_map {
 8029                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8030                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8031
 8032                        if is_visible || !self.edit_prediction_requires_modifier() {
 8033                            self.unfold_ranges(&[target..target], true, false, cx);
 8034                            self.change_selections(
 8035                                SelectionEffects::scroll(Autoscroll::newest()),
 8036                                window,
 8037                                cx,
 8038                                |selections| {
 8039                                    selections.select_anchor_ranges([target..target]);
 8040                                },
 8041                            );
 8042                            self.clear_row_highlights::<EditPredictionPreview>();
 8043                            self.edit_prediction_preview
 8044                                .set_previous_scroll_position(None);
 8045                        } else {
 8046                            // Highlight and request scroll
 8047                            self.edit_prediction_preview
 8048                                .set_previous_scroll_position(Some(
 8049                                    position_map.snapshot.scroll_anchor,
 8050                                ));
 8051                            self.highlight_rows::<EditPredictionPreview>(
 8052                                target..target,
 8053                                cx.theme().colors().editor_highlighted_line_background,
 8054                                RowHighlightOptions {
 8055                                    autoscroll: true,
 8056                                    ..Default::default()
 8057                                },
 8058                                cx,
 8059                            );
 8060                            self.request_autoscroll(Autoscroll::fit(), cx);
 8061                        }
 8062                    }
 8063                } else {
 8064                    self.change_selections(
 8065                        SelectionEffects::scroll(Autoscroll::newest()),
 8066                        window,
 8067                        cx,
 8068                        |selections| {
 8069                            selections.select_anchor_ranges([target..target]);
 8070                        },
 8071                    );
 8072                }
 8073            }
 8074            EditPrediction::MoveOutside { snapshot, target } => {
 8075                if let Some(workspace) = self.workspace() {
 8076                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8077                        .detach_and_log_err(cx);
 8078                }
 8079            }
 8080            EditPrediction::Edit {
 8081                edits,
 8082                cursor_position,
 8083                ..
 8084            } => {
 8085                self.report_edit_prediction_event(
 8086                    active_edit_prediction.completion_id.clone(),
 8087                    true,
 8088                    cx,
 8089                );
 8090
 8091                match granularity {
 8092                    EditPredictionGranularity::Full => {
 8093                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8094
 8095                        // Compute fallback cursor position BEFORE applying the edit,
 8096                        // so the anchor tracks through the edit correctly
 8097                        let fallback_cursor_target = {
 8098                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8099                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8100                        };
 8101
 8102                        self.buffer.update(cx, |buffer, cx| {
 8103                            buffer.edit(edits.iter().cloned(), None, cx)
 8104                        });
 8105
 8106                        if let Some(provider) = self.edit_prediction_provider() {
 8107                            provider.accept(cx);
 8108                        }
 8109
 8110                        // Resolve cursor position after the edit is applied
 8111                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8112                            // The anchor tracks through the edit, then we add the offset
 8113                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8114                            let base_offset = anchor.to_offset(&snapshot).0;
 8115                            let target_offset =
 8116                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8117                            snapshot.anchor_after(target_offset)
 8118                        } else {
 8119                            fallback_cursor_target
 8120                        };
 8121
 8122                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8123                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8124                        });
 8125
 8126                        let selections = self.selections.disjoint_anchors_arc();
 8127                        if let Some(transaction_id_now) =
 8128                            self.buffer.read(cx).last_transaction_id(cx)
 8129                        {
 8130                            if transaction_id_prev != Some(transaction_id_now) {
 8131                                self.selection_history
 8132                                    .insert_transaction(transaction_id_now, selections);
 8133                            }
 8134                        }
 8135
 8136                        self.update_visible_edit_prediction(window, cx);
 8137                        if self.active_edit_prediction.is_none() {
 8138                            self.refresh_edit_prediction(true, true, window, cx);
 8139                        }
 8140                        cx.notify();
 8141                    }
 8142                    _ => {
 8143                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8144                        let cursor_offset = self
 8145                            .selections
 8146                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8147                            .head();
 8148
 8149                        let insertion = edits.iter().find_map(|(range, text)| {
 8150                            let range = range.to_offset(&snapshot);
 8151                            if range.is_empty() && range.start == cursor_offset {
 8152                                Some(text)
 8153                            } else {
 8154                                None
 8155                            }
 8156                        });
 8157
 8158                        if let Some(text) = insertion {
 8159                            let text_to_insert = match granularity {
 8160                                EditPredictionGranularity::Word => {
 8161                                    let mut partial = text
 8162                                        .chars()
 8163                                        .by_ref()
 8164                                        .take_while(|c| c.is_alphabetic())
 8165                                        .collect::<String>();
 8166                                    if partial.is_empty() {
 8167                                        partial = text
 8168                                            .chars()
 8169                                            .by_ref()
 8170                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8171                                            .collect::<String>();
 8172                                    }
 8173                                    partial
 8174                                }
 8175                                EditPredictionGranularity::Line => {
 8176                                    if let Some(line) = text.split_inclusive('\n').next() {
 8177                                        line.to_string()
 8178                                    } else {
 8179                                        text.to_string()
 8180                                    }
 8181                                }
 8182                                EditPredictionGranularity::Full => unreachable!(),
 8183                            };
 8184
 8185                            cx.emit(EditorEvent::InputHandled {
 8186                                utf16_range_to_replace: None,
 8187                                text: text_to_insert.clone().into(),
 8188                            });
 8189
 8190                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8191                            self.refresh_edit_prediction(true, true, window, cx);
 8192                            cx.notify();
 8193                        } else {
 8194                            self.accept_partial_edit_prediction(
 8195                                EditPredictionGranularity::Full,
 8196                                window,
 8197                                cx,
 8198                            );
 8199                        }
 8200                    }
 8201                }
 8202            }
 8203        }
 8204
 8205        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8206    }
 8207
 8208    pub fn accept_next_word_edit_prediction(
 8209        &mut self,
 8210        _: &AcceptNextWordEditPrediction,
 8211        window: &mut Window,
 8212        cx: &mut Context<Self>,
 8213    ) {
 8214        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8215    }
 8216
 8217    pub fn accept_next_line_edit_prediction(
 8218        &mut self,
 8219        _: &AcceptNextLineEditPrediction,
 8220        window: &mut Window,
 8221        cx: &mut Context<Self>,
 8222    ) {
 8223        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8224    }
 8225
 8226    pub fn accept_edit_prediction(
 8227        &mut self,
 8228        _: &AcceptEditPrediction,
 8229        window: &mut Window,
 8230        cx: &mut Context<Self>,
 8231    ) {
 8232        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8233    }
 8234
 8235    fn discard_edit_prediction(
 8236        &mut self,
 8237        reason: EditPredictionDiscardReason,
 8238        cx: &mut Context<Self>,
 8239    ) -> bool {
 8240        if reason == EditPredictionDiscardReason::Rejected {
 8241            let completion_id = self
 8242                .active_edit_prediction
 8243                .as_ref()
 8244                .and_then(|active_completion| active_completion.completion_id.clone());
 8245
 8246            self.report_edit_prediction_event(completion_id, false, cx);
 8247        }
 8248
 8249        if let Some(provider) = self.edit_prediction_provider() {
 8250            provider.discard(reason, cx);
 8251        }
 8252
 8253        self.take_active_edit_prediction(cx)
 8254    }
 8255
 8256    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8257        let Some(provider) = self.edit_prediction_provider() else {
 8258            return;
 8259        };
 8260
 8261        let Some((_, buffer, _)) = self
 8262            .buffer
 8263            .read(cx)
 8264            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8265        else {
 8266            return;
 8267        };
 8268
 8269        let extension = buffer
 8270            .read(cx)
 8271            .file()
 8272            .and_then(|file| Some(file.path().extension()?.to_string()));
 8273
 8274        let event_type = match accepted {
 8275            true => "Edit Prediction Accepted",
 8276            false => "Edit Prediction Discarded",
 8277        };
 8278        telemetry::event!(
 8279            event_type,
 8280            provider = provider.name(),
 8281            prediction_id = id,
 8282            suggestion_accepted = accepted,
 8283            file_extension = extension,
 8284        );
 8285    }
 8286
 8287    fn open_editor_at_anchor(
 8288        snapshot: &language::BufferSnapshot,
 8289        target: language::Anchor,
 8290        workspace: &Entity<Workspace>,
 8291        window: &mut Window,
 8292        cx: &mut App,
 8293    ) -> Task<Result<()>> {
 8294        workspace.update(cx, |workspace, cx| {
 8295            let path = snapshot.file().map(|file| file.full_path(cx));
 8296            let Some(path) =
 8297                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8298            else {
 8299                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8300            };
 8301            let target = text::ToPoint::to_point(&target, snapshot);
 8302            let item = workspace.open_path(path, None, true, window, cx);
 8303            window.spawn(cx, async move |cx| {
 8304                let Some(editor) = item.await?.downcast::<Editor>() else {
 8305                    return Ok(());
 8306                };
 8307                editor
 8308                    .update_in(cx, |editor, window, cx| {
 8309                        editor.go_to_singleton_buffer_point(target, window, cx);
 8310                    })
 8311                    .ok();
 8312                anyhow::Ok(())
 8313            })
 8314        })
 8315    }
 8316
 8317    pub fn has_active_edit_prediction(&self) -> bool {
 8318        self.active_edit_prediction.is_some()
 8319    }
 8320
 8321    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8322        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8323            return false;
 8324        };
 8325
 8326        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8327        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8328        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8329        true
 8330    }
 8331
 8332    /// Returns true when we're displaying the edit prediction popover below the cursor
 8333    /// like we are not previewing and the LSP autocomplete menu is visible
 8334    /// or we are in `when_holding_modifier` mode.
 8335    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8336        if self.edit_prediction_preview_is_active()
 8337            || !self.show_edit_predictions_in_menu()
 8338            || !self.edit_predictions_enabled()
 8339        {
 8340            return false;
 8341        }
 8342
 8343        if self.has_visible_completions_menu() {
 8344            return true;
 8345        }
 8346
 8347        has_completion && self.edit_prediction_requires_modifier()
 8348    }
 8349
 8350    fn handle_modifiers_changed(
 8351        &mut self,
 8352        modifiers: Modifiers,
 8353        position_map: &PositionMap,
 8354        window: &mut Window,
 8355        cx: &mut Context<Self>,
 8356    ) {
 8357        // Ensure that the edit prediction preview is updated, even when not
 8358        // enabled, if there's an active edit prediction preview.
 8359        if self.show_edit_predictions_in_menu()
 8360            || matches!(
 8361                self.edit_prediction_preview,
 8362                EditPredictionPreview::Active { .. }
 8363            )
 8364        {
 8365            self.update_edit_prediction_preview(&modifiers, window, cx);
 8366        }
 8367
 8368        self.update_selection_mode(&modifiers, position_map, window, cx);
 8369
 8370        let mouse_position = window.mouse_position();
 8371        if !position_map.text_hitbox.is_hovered(window) {
 8372            return;
 8373        }
 8374
 8375        self.update_hovered_link(
 8376            position_map.point_for_position(mouse_position),
 8377            &position_map.snapshot,
 8378            modifiers,
 8379            window,
 8380            cx,
 8381        )
 8382    }
 8383
 8384    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8385        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8386            MultiCursorModifier::Alt => modifiers.secondary(),
 8387            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8388        }
 8389    }
 8390
 8391    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8392        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8393            MultiCursorModifier::Alt => modifiers.alt,
 8394            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8395        }
 8396    }
 8397
 8398    fn columnar_selection_mode(
 8399        modifiers: &Modifiers,
 8400        cx: &mut Context<Self>,
 8401    ) -> Option<ColumnarMode> {
 8402        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8403            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8404                Some(ColumnarMode::FromMouse)
 8405            } else if Self::is_alt_pressed(modifiers, cx) {
 8406                Some(ColumnarMode::FromSelection)
 8407            } else {
 8408                None
 8409            }
 8410        } else {
 8411            None
 8412        }
 8413    }
 8414
 8415    fn update_selection_mode(
 8416        &mut self,
 8417        modifiers: &Modifiers,
 8418        position_map: &PositionMap,
 8419        window: &mut Window,
 8420        cx: &mut Context<Self>,
 8421    ) {
 8422        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8423            return;
 8424        };
 8425        if self.selections.pending_anchor().is_none() {
 8426            return;
 8427        }
 8428
 8429        let mouse_position = window.mouse_position();
 8430        let point_for_position = position_map.point_for_position(mouse_position);
 8431        let position = point_for_position.previous_valid;
 8432
 8433        self.select(
 8434            SelectPhase::BeginColumnar {
 8435                position,
 8436                reset: false,
 8437                mode,
 8438                goal_column: point_for_position.exact_unclipped.column(),
 8439            },
 8440            window,
 8441            cx,
 8442        );
 8443    }
 8444
 8445    fn update_edit_prediction_preview(
 8446        &mut self,
 8447        modifiers: &Modifiers,
 8448        window: &mut Window,
 8449        cx: &mut Context<Self>,
 8450    ) {
 8451        let mut modifiers_held = false;
 8452
 8453        // Check bindings for all granularities.
 8454        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8455        let granularities = [
 8456            EditPredictionGranularity::Full,
 8457            EditPredictionGranularity::Line,
 8458            EditPredictionGranularity::Word,
 8459        ];
 8460
 8461        for granularity in granularities {
 8462            if let Some(keystroke) = self
 8463                .accept_edit_prediction_keybind(granularity, window, cx)
 8464                .keystroke()
 8465            {
 8466                modifiers_held = modifiers_held
 8467                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8468            }
 8469        }
 8470
 8471        if modifiers_held {
 8472            if matches!(
 8473                self.edit_prediction_preview,
 8474                EditPredictionPreview::Inactive { .. }
 8475            ) {
 8476                self.edit_prediction_preview = EditPredictionPreview::Active {
 8477                    previous_scroll_position: None,
 8478                    since: Instant::now(),
 8479                };
 8480
 8481                self.update_visible_edit_prediction(window, cx);
 8482                cx.notify();
 8483            }
 8484        } else if let EditPredictionPreview::Active {
 8485            previous_scroll_position,
 8486            since,
 8487        } = self.edit_prediction_preview
 8488        {
 8489            if let (Some(previous_scroll_position), Some(position_map)) =
 8490                (previous_scroll_position, self.last_position_map.as_ref())
 8491            {
 8492                self.set_scroll_position(
 8493                    previous_scroll_position
 8494                        .scroll_position(&position_map.snapshot.display_snapshot),
 8495                    window,
 8496                    cx,
 8497                );
 8498            }
 8499
 8500            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8501                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8502            };
 8503            self.clear_row_highlights::<EditPredictionPreview>();
 8504            self.update_visible_edit_prediction(window, cx);
 8505            cx.notify();
 8506        }
 8507    }
 8508
 8509    fn update_visible_edit_prediction(
 8510        &mut self,
 8511        _window: &mut Window,
 8512        cx: &mut Context<Self>,
 8513    ) -> Option<()> {
 8514        if self.ime_transaction.is_some() {
 8515            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8516            return None;
 8517        }
 8518
 8519        let selection = self.selections.newest_anchor();
 8520        let cursor = selection.head();
 8521        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8522
 8523        // Check project-level disable_ai setting for the current buffer
 8524        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8525            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8526                return None;
 8527            }
 8528        }
 8529        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8530        let excerpt_id = cursor.excerpt_id;
 8531
 8532        let show_in_menu = self.show_edit_predictions_in_menu();
 8533        let completions_menu_has_precedence = !show_in_menu
 8534            && (self.context_menu.borrow().is_some()
 8535                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8536
 8537        if completions_menu_has_precedence
 8538            || !offset_selection.is_empty()
 8539            || self
 8540                .active_edit_prediction
 8541                .as_ref()
 8542                .is_some_and(|completion| {
 8543                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8544                        return false;
 8545                    };
 8546                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8547                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8548                    !invalidation_range.contains(&offset_selection.head())
 8549                })
 8550        {
 8551            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8552            return None;
 8553        }
 8554
 8555        self.take_active_edit_prediction(cx);
 8556        let Some(provider) = self.edit_prediction_provider() else {
 8557            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8558            return None;
 8559        };
 8560
 8561        let (buffer, cursor_buffer_position) =
 8562            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8563
 8564        self.edit_prediction_settings =
 8565            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8566
 8567        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8568
 8569        if self.edit_prediction_indent_conflict {
 8570            let cursor_point = cursor.to_point(&multibuffer);
 8571            let mut suggested_indent = None;
 8572            multibuffer.suggested_indents_callback(
 8573                cursor_point.row..cursor_point.row + 1,
 8574                &mut |_, indent| {
 8575                    suggested_indent = Some(indent);
 8576                    ControlFlow::Break(())
 8577                },
 8578                cx,
 8579            );
 8580
 8581            if let Some(indent) = suggested_indent
 8582                && indent.len == cursor_point.column
 8583            {
 8584                self.edit_prediction_indent_conflict = false;
 8585            }
 8586        }
 8587
 8588        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8589
 8590        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8591        {
 8592            edit_prediction_types::EditPrediction::Local {
 8593                id,
 8594                edits,
 8595                cursor_position,
 8596                edit_preview,
 8597            } => (id, edits, cursor_position, edit_preview),
 8598            edit_prediction_types::EditPrediction::Jump {
 8599                id,
 8600                snapshot,
 8601                target,
 8602            } => {
 8603                if let Some(provider) = &self.edit_prediction_provider {
 8604                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8605                }
 8606                self.stale_edit_prediction_in_menu = None;
 8607                self.active_edit_prediction = Some(EditPredictionState {
 8608                    inlay_ids: vec![],
 8609                    completion: EditPrediction::MoveOutside { snapshot, target },
 8610                    completion_id: id,
 8611                    invalidation_range: None,
 8612                });
 8613                cx.notify();
 8614                return Some(());
 8615            }
 8616        };
 8617
 8618        let edits = edits
 8619            .into_iter()
 8620            .flat_map(|(range, new_text)| {
 8621                Some((
 8622                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8623                    new_text,
 8624                ))
 8625            })
 8626            .collect::<Vec<_>>();
 8627        if edits.is_empty() {
 8628            return None;
 8629        }
 8630
 8631        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8632            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8633            Some((anchor, predicted.offset))
 8634        });
 8635
 8636        let first_edit_start = edits.first().unwrap().0.start;
 8637        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8638        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8639
 8640        let last_edit_end = edits.last().unwrap().0.end;
 8641        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8642        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8643
 8644        let cursor_row = cursor.to_point(&multibuffer).row;
 8645
 8646        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8647
 8648        let mut inlay_ids = Vec::new();
 8649        let invalidation_row_range;
 8650        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8651            Some(cursor_row..edit_end_row)
 8652        } else if cursor_row > edit_end_row {
 8653            Some(edit_start_row..cursor_row)
 8654        } else {
 8655            None
 8656        };
 8657        let supports_jump = self
 8658            .edit_prediction_provider
 8659            .as_ref()
 8660            .map(|provider| provider.provider.supports_jump_to_edit())
 8661            .unwrap_or(true);
 8662
 8663        let is_move = supports_jump
 8664            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8665        let completion = if is_move {
 8666            if let Some(provider) = &self.edit_prediction_provider {
 8667                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8668            }
 8669            invalidation_row_range =
 8670                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8671            let target = first_edit_start;
 8672            EditPrediction::MoveWithin { target, snapshot }
 8673        } else {
 8674            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8675                && !self.edit_predictions_hidden_for_vim_mode;
 8676
 8677            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8678                if provider.show_tab_accept_marker() {
 8679                    EditDisplayMode::TabAccept
 8680                } else {
 8681                    EditDisplayMode::Inline
 8682                }
 8683            } else {
 8684                EditDisplayMode::DiffPopover
 8685            };
 8686
 8687            if show_completions_in_buffer {
 8688                if let Some(provider) = &self.edit_prediction_provider {
 8689                    let suggestion_display_type = match display_mode {
 8690                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8691                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8692                            SuggestionDisplayType::GhostText
 8693                        }
 8694                    };
 8695                    provider.provider.did_show(suggestion_display_type, cx);
 8696                }
 8697                if edits
 8698                    .iter()
 8699                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8700                {
 8701                    let mut inlays = Vec::new();
 8702                    for (range, new_text) in &edits {
 8703                        let inlay = Inlay::edit_prediction(
 8704                            post_inc(&mut self.next_inlay_id),
 8705                            range.start,
 8706                            new_text.as_ref(),
 8707                        );
 8708                        inlay_ids.push(inlay.id);
 8709                        inlays.push(inlay);
 8710                    }
 8711
 8712                    self.splice_inlays(&[], inlays, cx);
 8713                } else {
 8714                    let background_color = cx.theme().status().deleted_background;
 8715                    self.highlight_text(
 8716                        HighlightKey::EditPredictionHighlight,
 8717                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8718                        HighlightStyle {
 8719                            background_color: Some(background_color),
 8720                            ..Default::default()
 8721                        },
 8722                        cx,
 8723                    );
 8724                }
 8725            }
 8726
 8727            invalidation_row_range = edit_start_row..edit_end_row;
 8728
 8729            EditPrediction::Edit {
 8730                edits,
 8731                cursor_position,
 8732                edit_preview,
 8733                display_mode,
 8734                snapshot,
 8735            }
 8736        };
 8737
 8738        let invalidation_range = multibuffer
 8739            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8740            ..multibuffer.anchor_after(Point::new(
 8741                invalidation_row_range.end,
 8742                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8743            ));
 8744
 8745        self.stale_edit_prediction_in_menu = None;
 8746        self.active_edit_prediction = Some(EditPredictionState {
 8747            inlay_ids,
 8748            completion,
 8749            completion_id,
 8750            invalidation_range: Some(invalidation_range),
 8751        });
 8752
 8753        cx.notify();
 8754
 8755        Some(())
 8756    }
 8757
 8758    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8759        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8760    }
 8761
 8762    fn clear_tasks(&mut self) {
 8763        self.tasks.clear()
 8764    }
 8765
 8766    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8767        if self.tasks.insert(key, value).is_some() {
 8768            // This case should hopefully be rare, but just in case...
 8769            log::error!(
 8770                "multiple different run targets found on a single line, only the last target will be rendered"
 8771            )
 8772        }
 8773    }
 8774
 8775    /// Get all display points of breakpoints that will be rendered within editor
 8776    ///
 8777    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8778    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8779    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8780    fn active_breakpoints(
 8781        &self,
 8782        range: Range<DisplayRow>,
 8783        window: &mut Window,
 8784        cx: &mut Context<Self>,
 8785    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8786        let mut breakpoint_display_points = HashMap::default();
 8787
 8788        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8789            return breakpoint_display_points;
 8790        };
 8791
 8792        let snapshot = self.snapshot(window, cx);
 8793
 8794        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8795        let Some(project) = self.project() else {
 8796            return breakpoint_display_points;
 8797        };
 8798
 8799        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8800            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8801
 8802        for (buffer_snapshot, range, excerpt_id) in
 8803            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8804        {
 8805            let Some(buffer) = project
 8806                .read(cx)
 8807                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8808            else {
 8809                continue;
 8810            };
 8811            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8812                &buffer,
 8813                Some(
 8814                    buffer_snapshot.anchor_before(range.start)
 8815                        ..buffer_snapshot.anchor_after(range.end),
 8816                ),
 8817                buffer_snapshot,
 8818                cx,
 8819            );
 8820            for (breakpoint, state) in breakpoints {
 8821                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8822                let position = multi_buffer_anchor
 8823                    .to_point(&multi_buffer_snapshot)
 8824                    .to_display_point(&snapshot);
 8825
 8826                breakpoint_display_points.insert(
 8827                    position.row(),
 8828                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8829                );
 8830            }
 8831        }
 8832
 8833        breakpoint_display_points
 8834    }
 8835
 8836    fn breakpoint_context_menu(
 8837        &self,
 8838        anchor: Anchor,
 8839        window: &mut Window,
 8840        cx: &mut Context<Self>,
 8841    ) -> Entity<ui::ContextMenu> {
 8842        let weak_editor = cx.weak_entity();
 8843        let focus_handle = self.focus_handle(cx);
 8844
 8845        let row = self
 8846            .buffer
 8847            .read(cx)
 8848            .snapshot(cx)
 8849            .summary_for_anchor::<Point>(&anchor)
 8850            .row;
 8851
 8852        let breakpoint = self
 8853            .breakpoint_at_row(row, window, cx)
 8854            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8855
 8856        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8857            "Edit Log Breakpoint"
 8858        } else {
 8859            "Set Log Breakpoint"
 8860        };
 8861
 8862        let condition_breakpoint_msg = if breakpoint
 8863            .as_ref()
 8864            .is_some_and(|bp| bp.1.condition.is_some())
 8865        {
 8866            "Edit Condition Breakpoint"
 8867        } else {
 8868            "Set Condition Breakpoint"
 8869        };
 8870
 8871        let hit_condition_breakpoint_msg = if breakpoint
 8872            .as_ref()
 8873            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8874        {
 8875            "Edit Hit Condition Breakpoint"
 8876        } else {
 8877            "Set Hit Condition Breakpoint"
 8878        };
 8879
 8880        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8881            "Unset Breakpoint"
 8882        } else {
 8883            "Set Breakpoint"
 8884        };
 8885
 8886        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8887
 8888        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8889            BreakpointState::Enabled => Some("Disable"),
 8890            BreakpointState::Disabled => Some("Enable"),
 8891        });
 8892
 8893        let (anchor, breakpoint) =
 8894            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8895
 8896        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8897            menu.on_blur_subscription(Subscription::new(|| {}))
 8898                .context(focus_handle)
 8899                .when(run_to_cursor, |this| {
 8900                    let weak_editor = weak_editor.clone();
 8901                    this.entry("Run to Cursor", None, move |window, cx| {
 8902                        weak_editor
 8903                            .update(cx, |editor, cx| {
 8904                                editor.change_selections(
 8905                                    SelectionEffects::no_scroll(),
 8906                                    window,
 8907                                    cx,
 8908                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8909                                );
 8910                            })
 8911                            .ok();
 8912
 8913                        window.dispatch_action(Box::new(RunToCursor), cx);
 8914                    })
 8915                    .separator()
 8916                })
 8917                .when_some(toggle_state_msg, |this, msg| {
 8918                    this.entry(msg, None, {
 8919                        let weak_editor = weak_editor.clone();
 8920                        let breakpoint = breakpoint.clone();
 8921                        move |_window, cx| {
 8922                            weak_editor
 8923                                .update(cx, |this, cx| {
 8924                                    this.edit_breakpoint_at_anchor(
 8925                                        anchor,
 8926                                        breakpoint.as_ref().clone(),
 8927                                        BreakpointEditAction::InvertState,
 8928                                        cx,
 8929                                    );
 8930                                })
 8931                                .log_err();
 8932                        }
 8933                    })
 8934                })
 8935                .entry(set_breakpoint_msg, None, {
 8936                    let weak_editor = weak_editor.clone();
 8937                    let breakpoint = breakpoint.clone();
 8938                    move |_window, cx| {
 8939                        weak_editor
 8940                            .update(cx, |this, cx| {
 8941                                this.edit_breakpoint_at_anchor(
 8942                                    anchor,
 8943                                    breakpoint.as_ref().clone(),
 8944                                    BreakpointEditAction::Toggle,
 8945                                    cx,
 8946                                );
 8947                            })
 8948                            .log_err();
 8949                    }
 8950                })
 8951                .entry(log_breakpoint_msg, None, {
 8952                    let breakpoint = breakpoint.clone();
 8953                    let weak_editor = weak_editor.clone();
 8954                    move |window, cx| {
 8955                        weak_editor
 8956                            .update(cx, |this, cx| {
 8957                                this.add_edit_breakpoint_block(
 8958                                    anchor,
 8959                                    breakpoint.as_ref(),
 8960                                    BreakpointPromptEditAction::Log,
 8961                                    window,
 8962                                    cx,
 8963                                );
 8964                            })
 8965                            .log_err();
 8966                    }
 8967                })
 8968                .entry(condition_breakpoint_msg, None, {
 8969                    let breakpoint = breakpoint.clone();
 8970                    let weak_editor = weak_editor.clone();
 8971                    move |window, cx| {
 8972                        weak_editor
 8973                            .update(cx, |this, cx| {
 8974                                this.add_edit_breakpoint_block(
 8975                                    anchor,
 8976                                    breakpoint.as_ref(),
 8977                                    BreakpointPromptEditAction::Condition,
 8978                                    window,
 8979                                    cx,
 8980                                );
 8981                            })
 8982                            .log_err();
 8983                    }
 8984                })
 8985                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8986                    weak_editor
 8987                        .update(cx, |this, cx| {
 8988                            this.add_edit_breakpoint_block(
 8989                                anchor,
 8990                                breakpoint.as_ref(),
 8991                                BreakpointPromptEditAction::HitCondition,
 8992                                window,
 8993                                cx,
 8994                            );
 8995                        })
 8996                        .log_err();
 8997                })
 8998        })
 8999    }
 9000
 9001    fn render_breakpoint(
 9002        &self,
 9003        position: Anchor,
 9004        row: DisplayRow,
 9005        breakpoint: &Breakpoint,
 9006        state: Option<BreakpointSessionState>,
 9007        cx: &mut Context<Self>,
 9008    ) -> IconButton {
 9009        let is_rejected = state.is_some_and(|s| !s.verified);
 9010        // Is it a breakpoint that shows up when hovering over gutter?
 9011        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9012            (false, false),
 9013            |PhantomBreakpointIndicator {
 9014                 is_active,
 9015                 display_row,
 9016                 collides_with_existing_breakpoint,
 9017             }| {
 9018                (
 9019                    is_active && display_row == row,
 9020                    collides_with_existing_breakpoint,
 9021                )
 9022            },
 9023        );
 9024
 9025        let (color, icon) = {
 9026            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9027                (false, false) => ui::IconName::DebugBreakpoint,
 9028                (true, false) => ui::IconName::DebugLogBreakpoint,
 9029                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9030                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9031            };
 9032
 9033            let theme_colors = cx.theme().colors();
 9034
 9035            let color = if is_phantom {
 9036                if collides_with_existing {
 9037                    Color::Custom(
 9038                        theme_colors
 9039                            .debugger_accent
 9040                            .blend(theme_colors.text.opacity(0.6)),
 9041                    )
 9042                } else {
 9043                    Color::Hint
 9044                }
 9045            } else if is_rejected {
 9046                Color::Disabled
 9047            } else {
 9048                Color::Debugger
 9049            };
 9050
 9051            (color, icon)
 9052        };
 9053
 9054        let breakpoint = Arc::from(breakpoint.clone());
 9055
 9056        let alt_as_text = gpui::Keystroke {
 9057            modifiers: Modifiers::secondary_key(),
 9058            ..Default::default()
 9059        };
 9060        let primary_action_text = if breakpoint.is_disabled() {
 9061            "Enable breakpoint"
 9062        } else if is_phantom && !collides_with_existing {
 9063            "Set breakpoint"
 9064        } else {
 9065            "Unset breakpoint"
 9066        };
 9067        let focus_handle = self.focus_handle.clone();
 9068
 9069        let meta = if is_rejected {
 9070            SharedString::from("No executable code is associated with this line.")
 9071        } else if collides_with_existing && !breakpoint.is_disabled() {
 9072            SharedString::from(format!(
 9073                "{alt_as_text}-click to disable,\nright-click for more options."
 9074            ))
 9075        } else {
 9076            SharedString::from("Right-click for more options.")
 9077        };
 9078        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9079            .icon_size(IconSize::XSmall)
 9080            .size(ui::ButtonSize::None)
 9081            .when(is_rejected, |this| {
 9082                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9083            })
 9084            .icon_color(color)
 9085            .style(ButtonStyle::Transparent)
 9086            .on_click(cx.listener({
 9087                move |editor, event: &ClickEvent, window, cx| {
 9088                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9089                        BreakpointEditAction::InvertState
 9090                    } else {
 9091                        BreakpointEditAction::Toggle
 9092                    };
 9093
 9094                    window.focus(&editor.focus_handle(cx), cx);
 9095                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9096                    editor.edit_breakpoint_at_anchor(
 9097                        position,
 9098                        breakpoint.as_ref().clone(),
 9099                        edit_action,
 9100                        cx,
 9101                    );
 9102                }
 9103            }))
 9104            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9105                editor.set_breakpoint_context_menu(
 9106                    row,
 9107                    Some(position),
 9108                    event.position(),
 9109                    window,
 9110                    cx,
 9111                );
 9112            }))
 9113            .tooltip(move |_window, cx| {
 9114                Tooltip::with_meta_in(
 9115                    primary_action_text,
 9116                    Some(&ToggleBreakpoint),
 9117                    meta.clone(),
 9118                    &focus_handle,
 9119                    cx,
 9120                )
 9121            })
 9122    }
 9123
 9124    fn build_tasks_context(
 9125        project: &Entity<Project>,
 9126        buffer: &Entity<Buffer>,
 9127        buffer_row: u32,
 9128        tasks: &Arc<RunnableTasks>,
 9129        cx: &mut Context<Self>,
 9130    ) -> Task<Option<task::TaskContext>> {
 9131        let position = Point::new(buffer_row, tasks.column);
 9132        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9133        let location = Location {
 9134            buffer: buffer.clone(),
 9135            range: range_start..range_start,
 9136        };
 9137        // Fill in the environmental variables from the tree-sitter captures
 9138        let mut captured_task_variables = TaskVariables::default();
 9139        for (capture_name, value) in tasks.extra_variables.clone() {
 9140            captured_task_variables.insert(
 9141                task::VariableName::Custom(capture_name.into()),
 9142                value.clone(),
 9143            );
 9144        }
 9145        project.update(cx, |project, cx| {
 9146            project.task_store().update(cx, |task_store, cx| {
 9147                task_store.task_context_for_location(captured_task_variables, location, cx)
 9148            })
 9149        })
 9150    }
 9151
 9152    pub fn spawn_nearest_task(
 9153        &mut self,
 9154        action: &SpawnNearestTask,
 9155        window: &mut Window,
 9156        cx: &mut Context<Self>,
 9157    ) {
 9158        let Some((workspace, _)) = self.workspace.clone() else {
 9159            return;
 9160        };
 9161        let Some(project) = self.project.clone() else {
 9162            return;
 9163        };
 9164
 9165        // Try to find a closest, enclosing node using tree-sitter that has a task
 9166        let Some((buffer, buffer_row, tasks)) = self
 9167            .find_enclosing_node_task(cx)
 9168            // Or find the task that's closest in row-distance.
 9169            .or_else(|| self.find_closest_task(cx))
 9170        else {
 9171            return;
 9172        };
 9173
 9174        let reveal_strategy = action.reveal;
 9175        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9176        cx.spawn_in(window, async move |_, cx| {
 9177            let context = task_context.await?;
 9178            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9179
 9180            let resolved = &mut resolved_task.resolved;
 9181            resolved.reveal = reveal_strategy;
 9182
 9183            workspace
 9184                .update_in(cx, |workspace, window, cx| {
 9185                    workspace.schedule_resolved_task(
 9186                        task_source_kind,
 9187                        resolved_task,
 9188                        false,
 9189                        window,
 9190                        cx,
 9191                    );
 9192                })
 9193                .ok()
 9194        })
 9195        .detach();
 9196    }
 9197
 9198    fn find_closest_task(
 9199        &mut self,
 9200        cx: &mut Context<Self>,
 9201    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9202        let cursor_row = self
 9203            .selections
 9204            .newest_adjusted(&self.display_snapshot(cx))
 9205            .head()
 9206            .row;
 9207
 9208        let ((buffer_id, row), tasks) = self
 9209            .tasks
 9210            .iter()
 9211            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9212
 9213        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9214        let tasks = Arc::new(tasks.to_owned());
 9215        Some((buffer, *row, tasks))
 9216    }
 9217
 9218    fn find_enclosing_node_task(
 9219        &mut self,
 9220        cx: &mut Context<Self>,
 9221    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9222        let snapshot = self.buffer.read(cx).snapshot(cx);
 9223        let offset = self
 9224            .selections
 9225            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9226            .head();
 9227        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9228        let offset = excerpt.map_offset_to_buffer(offset);
 9229        let buffer_id = excerpt.buffer().remote_id();
 9230
 9231        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9232        let mut cursor = layer.node().walk();
 9233
 9234        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9235            if cursor.node().end_byte() == offset.0 {
 9236                cursor.goto_next_sibling();
 9237            }
 9238        }
 9239
 9240        // Ascend to the smallest ancestor that contains the range and has a task.
 9241        loop {
 9242            let node = cursor.node();
 9243            let node_range = node.byte_range();
 9244            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9245
 9246            // Check if this node contains our offset
 9247            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9248                // If it contains offset, check for task
 9249                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9250                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9251                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9252                }
 9253            }
 9254
 9255            if !cursor.goto_parent() {
 9256                break;
 9257            }
 9258        }
 9259        None
 9260    }
 9261
 9262    fn render_run_indicator(
 9263        &self,
 9264        _style: &EditorStyle,
 9265        is_active: bool,
 9266        row: DisplayRow,
 9267        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9268        cx: &mut Context<Self>,
 9269    ) -> IconButton {
 9270        let color = Color::Muted;
 9271        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9272
 9273        IconButton::new(
 9274            ("run_indicator", row.0 as usize),
 9275            ui::IconName::PlayOutlined,
 9276        )
 9277        .shape(ui::IconButtonShape::Square)
 9278        .icon_size(IconSize::XSmall)
 9279        .icon_color(color)
 9280        .toggle_state(is_active)
 9281        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9282            let quick_launch = match e {
 9283                ClickEvent::Keyboard(_) => true,
 9284                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9285            };
 9286
 9287            window.focus(&editor.focus_handle(cx), cx);
 9288            editor.toggle_code_actions(
 9289                &ToggleCodeActions {
 9290                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9291                    quick_launch,
 9292                },
 9293                window,
 9294                cx,
 9295            );
 9296        }))
 9297        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9298            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9299        }))
 9300    }
 9301
 9302    pub fn context_menu_visible(&self) -> bool {
 9303        !self.edit_prediction_preview_is_active()
 9304            && self
 9305                .context_menu
 9306                .borrow()
 9307                .as_ref()
 9308                .is_some_and(|menu| menu.visible())
 9309    }
 9310
 9311    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9312        self.context_menu
 9313            .borrow()
 9314            .as_ref()
 9315            .map(|menu| menu.origin())
 9316    }
 9317
 9318    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9319        self.context_menu_options = Some(options);
 9320    }
 9321
 9322    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9323    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9324
 9325    fn render_edit_prediction_popover(
 9326        &mut self,
 9327        text_bounds: &Bounds<Pixels>,
 9328        content_origin: gpui::Point<Pixels>,
 9329        right_margin: Pixels,
 9330        editor_snapshot: &EditorSnapshot,
 9331        visible_row_range: Range<DisplayRow>,
 9332        scroll_top: ScrollOffset,
 9333        scroll_bottom: ScrollOffset,
 9334        line_layouts: &[LineWithInvisibles],
 9335        line_height: Pixels,
 9336        scroll_position: gpui::Point<ScrollOffset>,
 9337        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9338        newest_selection_head: Option<DisplayPoint>,
 9339        editor_width: Pixels,
 9340        style: &EditorStyle,
 9341        window: &mut Window,
 9342        cx: &mut App,
 9343    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9344        if self.mode().is_minimap() {
 9345            return None;
 9346        }
 9347        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9348
 9349        if self.edit_prediction_visible_in_cursor_popover(true) {
 9350            return None;
 9351        }
 9352
 9353        match &active_edit_prediction.completion {
 9354            EditPrediction::MoveWithin { target, .. } => {
 9355                let target_display_point = target.to_display_point(editor_snapshot);
 9356
 9357                if self.edit_prediction_requires_modifier() {
 9358                    if !self.edit_prediction_preview_is_active() {
 9359                        return None;
 9360                    }
 9361
 9362                    self.render_edit_prediction_modifier_jump_popover(
 9363                        text_bounds,
 9364                        content_origin,
 9365                        visible_row_range,
 9366                        line_layouts,
 9367                        line_height,
 9368                        scroll_pixel_position,
 9369                        newest_selection_head,
 9370                        target_display_point,
 9371                        window,
 9372                        cx,
 9373                    )
 9374                } else {
 9375                    self.render_edit_prediction_eager_jump_popover(
 9376                        text_bounds,
 9377                        content_origin,
 9378                        editor_snapshot,
 9379                        visible_row_range,
 9380                        scroll_top,
 9381                        scroll_bottom,
 9382                        line_height,
 9383                        scroll_pixel_position,
 9384                        target_display_point,
 9385                        editor_width,
 9386                        window,
 9387                        cx,
 9388                    )
 9389                }
 9390            }
 9391            EditPrediction::Edit {
 9392                display_mode: EditDisplayMode::Inline,
 9393                ..
 9394            } => None,
 9395            EditPrediction::Edit {
 9396                display_mode: EditDisplayMode::TabAccept,
 9397                edits,
 9398                ..
 9399            } => {
 9400                let range = &edits.first()?.0;
 9401                let target_display_point = range.end.to_display_point(editor_snapshot);
 9402
 9403                self.render_edit_prediction_end_of_line_popover(
 9404                    "Accept",
 9405                    editor_snapshot,
 9406                    visible_row_range,
 9407                    target_display_point,
 9408                    line_height,
 9409                    scroll_pixel_position,
 9410                    content_origin,
 9411                    editor_width,
 9412                    window,
 9413                    cx,
 9414                )
 9415            }
 9416            EditPrediction::Edit {
 9417                edits,
 9418                edit_preview,
 9419                display_mode: EditDisplayMode::DiffPopover,
 9420                snapshot,
 9421                ..
 9422            } => self.render_edit_prediction_diff_popover(
 9423                text_bounds,
 9424                content_origin,
 9425                right_margin,
 9426                editor_snapshot,
 9427                visible_row_range,
 9428                line_layouts,
 9429                line_height,
 9430                scroll_position,
 9431                scroll_pixel_position,
 9432                newest_selection_head,
 9433                editor_width,
 9434                style,
 9435                edits,
 9436                edit_preview,
 9437                snapshot,
 9438                window,
 9439                cx,
 9440            ),
 9441            EditPrediction::MoveOutside { snapshot, .. } => {
 9442                let mut element = self
 9443                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9444                    .into_any();
 9445
 9446                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9447                let origin_x = text_bounds.size.width - size.width - px(30.);
 9448                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9449                element.prepaint_at(origin, window, cx);
 9450
 9451                Some((element, origin))
 9452            }
 9453        }
 9454    }
 9455
 9456    fn render_edit_prediction_modifier_jump_popover(
 9457        &mut self,
 9458        text_bounds: &Bounds<Pixels>,
 9459        content_origin: gpui::Point<Pixels>,
 9460        visible_row_range: Range<DisplayRow>,
 9461        line_layouts: &[LineWithInvisibles],
 9462        line_height: Pixels,
 9463        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9464        newest_selection_head: Option<DisplayPoint>,
 9465        target_display_point: DisplayPoint,
 9466        window: &mut Window,
 9467        cx: &mut App,
 9468    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9469        let scrolled_content_origin =
 9470            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9471
 9472        const SCROLL_PADDING_Y: Pixels = px(12.);
 9473
 9474        if target_display_point.row() < visible_row_range.start {
 9475            return self.render_edit_prediction_scroll_popover(
 9476                &|_| SCROLL_PADDING_Y,
 9477                IconName::ArrowUp,
 9478                visible_row_range,
 9479                line_layouts,
 9480                newest_selection_head,
 9481                scrolled_content_origin,
 9482                window,
 9483                cx,
 9484            );
 9485        } else if target_display_point.row() >= visible_row_range.end {
 9486            return self.render_edit_prediction_scroll_popover(
 9487                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9488                IconName::ArrowDown,
 9489                visible_row_range,
 9490                line_layouts,
 9491                newest_selection_head,
 9492                scrolled_content_origin,
 9493                window,
 9494                cx,
 9495            );
 9496        }
 9497
 9498        const POLE_WIDTH: Pixels = px(2.);
 9499
 9500        let line_layout =
 9501            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9502        let target_column = target_display_point.column() as usize;
 9503
 9504        let target_x = line_layout.x_for_index(target_column);
 9505        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9506            - scroll_pixel_position.y;
 9507
 9508        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9509
 9510        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9511        border_color.l += 0.001;
 9512
 9513        let mut element = v_flex()
 9514            .items_end()
 9515            .when(flag_on_right, |el| el.items_start())
 9516            .child(if flag_on_right {
 9517                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9518                    .rounded_bl(px(0.))
 9519                    .rounded_tl(px(0.))
 9520                    .border_l_2()
 9521                    .border_color(border_color)
 9522            } else {
 9523                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9524                    .rounded_br(px(0.))
 9525                    .rounded_tr(px(0.))
 9526                    .border_r_2()
 9527                    .border_color(border_color)
 9528            })
 9529            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9530            .into_any();
 9531
 9532        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9533
 9534        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9535            - point(
 9536                if flag_on_right {
 9537                    POLE_WIDTH
 9538                } else {
 9539                    size.width - POLE_WIDTH
 9540                },
 9541                size.height - line_height,
 9542            );
 9543
 9544        origin.x = origin.x.max(content_origin.x);
 9545
 9546        element.prepaint_at(origin, window, cx);
 9547
 9548        Some((element, origin))
 9549    }
 9550
 9551    fn render_edit_prediction_scroll_popover(
 9552        &mut self,
 9553        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9554        scroll_icon: IconName,
 9555        visible_row_range: Range<DisplayRow>,
 9556        line_layouts: &[LineWithInvisibles],
 9557        newest_selection_head: Option<DisplayPoint>,
 9558        scrolled_content_origin: gpui::Point<Pixels>,
 9559        window: &mut Window,
 9560        cx: &mut App,
 9561    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9562        let mut element = self
 9563            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9564            .into_any();
 9565
 9566        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9567
 9568        let cursor = newest_selection_head?;
 9569        let cursor_row_layout =
 9570            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9571        let cursor_column = cursor.column() as usize;
 9572
 9573        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9574
 9575        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9576
 9577        element.prepaint_at(origin, window, cx);
 9578        Some((element, origin))
 9579    }
 9580
 9581    fn render_edit_prediction_eager_jump_popover(
 9582        &mut self,
 9583        text_bounds: &Bounds<Pixels>,
 9584        content_origin: gpui::Point<Pixels>,
 9585        editor_snapshot: &EditorSnapshot,
 9586        visible_row_range: Range<DisplayRow>,
 9587        scroll_top: ScrollOffset,
 9588        scroll_bottom: ScrollOffset,
 9589        line_height: Pixels,
 9590        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9591        target_display_point: DisplayPoint,
 9592        editor_width: Pixels,
 9593        window: &mut Window,
 9594        cx: &mut App,
 9595    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9596        if target_display_point.row().as_f64() < scroll_top {
 9597            let mut element = self
 9598                .render_edit_prediction_line_popover(
 9599                    "Jump to Edit",
 9600                    Some(IconName::ArrowUp),
 9601                    window,
 9602                    cx,
 9603                )
 9604                .into_any();
 9605
 9606            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9607            let offset = point(
 9608                (text_bounds.size.width - size.width) / 2.,
 9609                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9610            );
 9611
 9612            let origin = text_bounds.origin + offset;
 9613            element.prepaint_at(origin, window, cx);
 9614            Some((element, origin))
 9615        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9616            let mut element = self
 9617                .render_edit_prediction_line_popover(
 9618                    "Jump to Edit",
 9619                    Some(IconName::ArrowDown),
 9620                    window,
 9621                    cx,
 9622                )
 9623                .into_any();
 9624
 9625            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9626            let offset = point(
 9627                (text_bounds.size.width - size.width) / 2.,
 9628                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9629            );
 9630
 9631            let origin = text_bounds.origin + offset;
 9632            element.prepaint_at(origin, window, cx);
 9633            Some((element, origin))
 9634        } else {
 9635            self.render_edit_prediction_end_of_line_popover(
 9636                "Jump to Edit",
 9637                editor_snapshot,
 9638                visible_row_range,
 9639                target_display_point,
 9640                line_height,
 9641                scroll_pixel_position,
 9642                content_origin,
 9643                editor_width,
 9644                window,
 9645                cx,
 9646            )
 9647        }
 9648    }
 9649
 9650    fn render_edit_prediction_end_of_line_popover(
 9651        self: &mut Editor,
 9652        label: &'static str,
 9653        editor_snapshot: &EditorSnapshot,
 9654        visible_row_range: Range<DisplayRow>,
 9655        target_display_point: DisplayPoint,
 9656        line_height: Pixels,
 9657        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9658        content_origin: gpui::Point<Pixels>,
 9659        editor_width: Pixels,
 9660        window: &mut Window,
 9661        cx: &mut App,
 9662    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9663        let target_line_end = DisplayPoint::new(
 9664            target_display_point.row(),
 9665            editor_snapshot.line_len(target_display_point.row()),
 9666        );
 9667
 9668        let mut element = self
 9669            .render_edit_prediction_line_popover(label, None, window, cx)
 9670            .into_any();
 9671
 9672        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9673
 9674        let line_origin =
 9675            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9676
 9677        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9678        let mut origin = start_point
 9679            + line_origin
 9680            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9681        origin.x = origin.x.max(content_origin.x);
 9682
 9683        let max_x = content_origin.x + editor_width - size.width;
 9684
 9685        if origin.x > max_x {
 9686            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9687
 9688            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9689                origin.y += offset;
 9690                IconName::ArrowUp
 9691            } else {
 9692                origin.y -= offset;
 9693                IconName::ArrowDown
 9694            };
 9695
 9696            element = self
 9697                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9698                .into_any();
 9699
 9700            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9701
 9702            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9703        }
 9704
 9705        element.prepaint_at(origin, window, cx);
 9706        Some((element, origin))
 9707    }
 9708
 9709    fn render_edit_prediction_diff_popover(
 9710        self: &Editor,
 9711        text_bounds: &Bounds<Pixels>,
 9712        content_origin: gpui::Point<Pixels>,
 9713        right_margin: Pixels,
 9714        editor_snapshot: &EditorSnapshot,
 9715        visible_row_range: Range<DisplayRow>,
 9716        line_layouts: &[LineWithInvisibles],
 9717        line_height: Pixels,
 9718        scroll_position: gpui::Point<ScrollOffset>,
 9719        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9720        newest_selection_head: Option<DisplayPoint>,
 9721        editor_width: Pixels,
 9722        style: &EditorStyle,
 9723        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9724        edit_preview: &Option<language::EditPreview>,
 9725        snapshot: &language::BufferSnapshot,
 9726        window: &mut Window,
 9727        cx: &mut App,
 9728    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9729        let edit_start = edits
 9730            .first()
 9731            .unwrap()
 9732            .0
 9733            .start
 9734            .to_display_point(editor_snapshot);
 9735        let edit_end = edits
 9736            .last()
 9737            .unwrap()
 9738            .0
 9739            .end
 9740            .to_display_point(editor_snapshot);
 9741
 9742        let is_visible = visible_row_range.contains(&edit_start.row())
 9743            || visible_row_range.contains(&edit_end.row());
 9744        if !is_visible {
 9745            return None;
 9746        }
 9747
 9748        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9749            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9750        } else {
 9751            // Fallback for providers without edit_preview
 9752            crate::edit_prediction_fallback_text(edits, cx)
 9753        };
 9754
 9755        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9756        let line_count = highlighted_edits.text.lines().count();
 9757
 9758        const BORDER_WIDTH: Pixels = px(1.);
 9759
 9760        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9761        let has_keybind = keybind.is_some();
 9762
 9763        let mut element = h_flex()
 9764            .items_start()
 9765            .child(
 9766                h_flex()
 9767                    .bg(cx.theme().colors().editor_background)
 9768                    .border(BORDER_WIDTH)
 9769                    .shadow_xs()
 9770                    .border_color(cx.theme().colors().border)
 9771                    .rounded_l_lg()
 9772                    .when(line_count > 1, |el| el.rounded_br_lg())
 9773                    .pr_1()
 9774                    .child(styled_text),
 9775            )
 9776            .child(
 9777                h_flex()
 9778                    .h(line_height + BORDER_WIDTH * 2.)
 9779                    .px_1p5()
 9780                    .gap_1()
 9781                    // Workaround: For some reason, there's a gap if we don't do this
 9782                    .ml(-BORDER_WIDTH)
 9783                    .shadow(vec![gpui::BoxShadow {
 9784                        color: gpui::black().opacity(0.05),
 9785                        offset: point(px(1.), px(1.)),
 9786                        blur_radius: px(2.),
 9787                        spread_radius: px(0.),
 9788                    }])
 9789                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9790                    .border(BORDER_WIDTH)
 9791                    .border_color(cx.theme().colors().border)
 9792                    .rounded_r_lg()
 9793                    .id("edit_prediction_diff_popover_keybind")
 9794                    .when(!has_keybind, |el| {
 9795                        let status_colors = cx.theme().status();
 9796
 9797                        el.bg(status_colors.error_background)
 9798                            .border_color(status_colors.error.opacity(0.6))
 9799                            .child(Icon::new(IconName::Info).color(Color::Error))
 9800                            .cursor_default()
 9801                            .hoverable_tooltip(move |_window, cx| {
 9802                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9803                            })
 9804                    })
 9805                    .children(keybind),
 9806            )
 9807            .into_any();
 9808
 9809        let longest_row =
 9810            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9811        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9812            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9813        } else {
 9814            layout_line(
 9815                longest_row,
 9816                editor_snapshot,
 9817                style,
 9818                editor_width,
 9819                |_| false,
 9820                window,
 9821                cx,
 9822            )
 9823            .width
 9824        };
 9825
 9826        let viewport_bounds =
 9827            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9828                right: -right_margin,
 9829                ..Default::default()
 9830            });
 9831
 9832        let x_after_longest = Pixels::from(
 9833            ScrollPixelOffset::from(
 9834                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9835            ) - scroll_pixel_position.x,
 9836        );
 9837
 9838        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9839
 9840        // Fully visible if it can be displayed within the window (allow overlapping other
 9841        // panes). However, this is only allowed if the popover starts within text_bounds.
 9842        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9843            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9844
 9845        let mut origin = if can_position_to_the_right {
 9846            point(
 9847                x_after_longest,
 9848                text_bounds.origin.y
 9849                    + Pixels::from(
 9850                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9851                            - scroll_pixel_position.y,
 9852                    ),
 9853            )
 9854        } else {
 9855            let cursor_row = newest_selection_head.map(|head| head.row());
 9856            let above_edit = edit_start
 9857                .row()
 9858                .0
 9859                .checked_sub(line_count as u32)
 9860                .map(DisplayRow);
 9861            let below_edit = Some(edit_end.row() + 1);
 9862            let above_cursor =
 9863                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9864            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9865
 9866            // Place the edit popover adjacent to the edit if there is a location
 9867            // available that is onscreen and does not obscure the cursor. Otherwise,
 9868            // place it adjacent to the cursor.
 9869            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9870                .into_iter()
 9871                .flatten()
 9872                .find(|&start_row| {
 9873                    let end_row = start_row + line_count as u32;
 9874                    visible_row_range.contains(&start_row)
 9875                        && visible_row_range.contains(&end_row)
 9876                        && cursor_row
 9877                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9878                })?;
 9879
 9880            content_origin
 9881                + point(
 9882                    Pixels::from(-scroll_pixel_position.x),
 9883                    Pixels::from(
 9884                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9885                    ),
 9886                )
 9887        };
 9888
 9889        origin.x -= BORDER_WIDTH;
 9890
 9891        window.defer_draw(element, origin, 1);
 9892
 9893        // Do not return an element, since it will already be drawn due to defer_draw.
 9894        None
 9895    }
 9896
 9897    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9898        px(30.)
 9899    }
 9900
 9901    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9902        if self.read_only(cx) {
 9903            cx.theme().players().read_only()
 9904        } else {
 9905            self.style.as_ref().unwrap().local_player
 9906        }
 9907    }
 9908
 9909    fn render_edit_prediction_accept_keybind(
 9910        &self,
 9911        window: &mut Window,
 9912        cx: &mut App,
 9913    ) -> Option<AnyElement> {
 9914        let accept_binding =
 9915            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9916        let accept_keystroke = accept_binding.keystroke()?;
 9917
 9918        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9919
 9920        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9921            Color::Accent
 9922        } else {
 9923            Color::Muted
 9924        };
 9925
 9926        h_flex()
 9927            .px_0p5()
 9928            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9929            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9930            .text_size(TextSize::XSmall.rems(cx))
 9931            .child(h_flex().children(ui::render_modifiers(
 9932                accept_keystroke.modifiers(),
 9933                PlatformStyle::platform(),
 9934                Some(modifiers_color),
 9935                Some(IconSize::XSmall.rems().into()),
 9936                true,
 9937            )))
 9938            .when(is_platform_style_mac, |parent| {
 9939                parent.child(accept_keystroke.key().to_string())
 9940            })
 9941            .when(!is_platform_style_mac, |parent| {
 9942                parent.child(
 9943                    Key::new(
 9944                        util::capitalize(accept_keystroke.key()),
 9945                        Some(Color::Default),
 9946                    )
 9947                    .size(Some(IconSize::XSmall.rems().into())),
 9948                )
 9949            })
 9950            .into_any()
 9951            .into()
 9952    }
 9953
 9954    fn render_edit_prediction_line_popover(
 9955        &self,
 9956        label: impl Into<SharedString>,
 9957        icon: Option<IconName>,
 9958        window: &mut Window,
 9959        cx: &mut App,
 9960    ) -> Stateful<Div> {
 9961        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9962
 9963        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9964        let has_keybind = keybind.is_some();
 9965        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9966
 9967        h_flex()
 9968            .id("ep-line-popover")
 9969            .py_0p5()
 9970            .pl_1()
 9971            .pr(padding_right)
 9972            .gap_1()
 9973            .rounded_md()
 9974            .border_1()
 9975            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9976            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9977            .shadow_xs()
 9978            .when(!has_keybind, |el| {
 9979                let status_colors = cx.theme().status();
 9980
 9981                el.bg(status_colors.error_background)
 9982                    .border_color(status_colors.error.opacity(0.6))
 9983                    .pl_2()
 9984                    .child(Icon::new(icons.error).color(Color::Error))
 9985                    .cursor_default()
 9986                    .hoverable_tooltip(move |_window, cx| {
 9987                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9988                    })
 9989            })
 9990            .children(keybind)
 9991            .child(
 9992                Label::new(label)
 9993                    .size(LabelSize::Small)
 9994                    .when(!has_keybind, |el| {
 9995                        el.color(cx.theme().status().error.into()).strikethrough()
 9996                    }),
 9997            )
 9998            .when(!has_keybind, |el| {
 9999                el.child(
10000                    h_flex().ml_1().child(
10001                        Icon::new(IconName::Info)
10002                            .size(IconSize::Small)
10003                            .color(cx.theme().status().error.into()),
10004                    ),
10005                )
10006            })
10007            .when_some(icon, |element, icon| {
10008                element.child(
10009                    div()
10010                        .mt(px(1.5))
10011                        .child(Icon::new(icon).size(IconSize::Small)),
10012                )
10013            })
10014    }
10015
10016    fn render_edit_prediction_jump_outside_popover(
10017        &self,
10018        snapshot: &BufferSnapshot,
10019        window: &mut Window,
10020        cx: &mut App,
10021    ) -> Stateful<Div> {
10022        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
10023        let has_keybind = keybind.is_some();
10024        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10025
10026        let file_name = snapshot
10027            .file()
10028            .map(|file| SharedString::new(file.file_name(cx)))
10029            .unwrap_or(SharedString::new_static("untitled"));
10030
10031        h_flex()
10032            .id("ep-jump-outside-popover")
10033            .py_1()
10034            .px_2()
10035            .gap_1()
10036            .rounded_md()
10037            .border_1()
10038            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10039            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10040            .shadow_xs()
10041            .when(!has_keybind, |el| {
10042                let status_colors = cx.theme().status();
10043
10044                el.bg(status_colors.error_background)
10045                    .border_color(status_colors.error.opacity(0.6))
10046                    .pl_2()
10047                    .child(Icon::new(icons.error).color(Color::Error))
10048                    .cursor_default()
10049                    .hoverable_tooltip(move |_window, cx| {
10050                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10051                    })
10052            })
10053            .children(keybind)
10054            .child(
10055                Label::new(file_name)
10056                    .size(LabelSize::Small)
10057                    .buffer_font(cx)
10058                    .when(!has_keybind, |el| {
10059                        el.color(cx.theme().status().error.into()).strikethrough()
10060                    }),
10061            )
10062            .when(!has_keybind, |el| {
10063                el.child(
10064                    h_flex().ml_1().child(
10065                        Icon::new(IconName::Info)
10066                            .size(IconSize::Small)
10067                            .color(cx.theme().status().error.into()),
10068                    ),
10069                )
10070            })
10071            .child(
10072                div()
10073                    .mt(px(1.5))
10074                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10075            )
10076    }
10077
10078    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10079        let accent_color = cx.theme().colors().text_accent;
10080        let editor_bg_color = cx.theme().colors().editor_background;
10081        editor_bg_color.blend(accent_color.opacity(0.1))
10082    }
10083
10084    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10085        let accent_color = cx.theme().colors().text_accent;
10086        let editor_bg_color = cx.theme().colors().editor_background;
10087        editor_bg_color.blend(accent_color.opacity(0.6))
10088    }
10089    fn get_prediction_provider_icons(
10090        provider: &Option<RegisteredEditPredictionDelegate>,
10091        cx: &App,
10092    ) -> edit_prediction_types::EditPredictionIconSet {
10093        match provider {
10094            Some(provider) => provider.provider.icons(cx),
10095            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10096        }
10097    }
10098
10099    fn render_edit_prediction_cursor_popover(
10100        &self,
10101        min_width: Pixels,
10102        max_width: Pixels,
10103        cursor_point: Point,
10104        style: &EditorStyle,
10105        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10106        _window: &Window,
10107        cx: &mut Context<Editor>,
10108    ) -> Option<AnyElement> {
10109        let provider = self.edit_prediction_provider.as_ref()?;
10110        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10111
10112        let is_refreshing = provider.provider.is_refreshing(cx);
10113
10114        fn pending_completion_container(icon: IconName) -> Div {
10115            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10116        }
10117
10118        let completion = match &self.active_edit_prediction {
10119            Some(prediction) => {
10120                if !self.has_visible_completions_menu() {
10121                    const RADIUS: Pixels = px(6.);
10122                    const BORDER_WIDTH: Pixels = px(1.);
10123
10124                    return Some(
10125                        h_flex()
10126                            .elevation_2(cx)
10127                            .border(BORDER_WIDTH)
10128                            .border_color(cx.theme().colors().border)
10129                            .when(accept_keystroke.is_none(), |el| {
10130                                el.border_color(cx.theme().status().error)
10131                            })
10132                            .rounded(RADIUS)
10133                            .rounded_tl(px(0.))
10134                            .overflow_hidden()
10135                            .child(div().px_1p5().child(match &prediction.completion {
10136                                EditPrediction::MoveWithin { target, snapshot } => {
10137                                    use text::ToPoint as _;
10138                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10139                                    {
10140                                        Icon::new(icons.down)
10141                                    } else {
10142                                        Icon::new(icons.up)
10143                                    }
10144                                }
10145                                EditPrediction::MoveOutside { .. } => {
10146                                    // TODO [zeta2] custom icon for external jump?
10147                                    Icon::new(icons.base)
10148                                }
10149                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10150                            }))
10151                            .child(
10152                                h_flex()
10153                                    .gap_1()
10154                                    .py_1()
10155                                    .px_2()
10156                                    .rounded_r(RADIUS - BORDER_WIDTH)
10157                                    .border_l_1()
10158                                    .border_color(cx.theme().colors().border)
10159                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10160                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10161                                        el.child(
10162                                            Label::new("Hold")
10163                                                .size(LabelSize::Small)
10164                                                .when(accept_keystroke.is_none(), |el| {
10165                                                    el.strikethrough()
10166                                                })
10167                                                .line_height_style(LineHeightStyle::UiLabel),
10168                                        )
10169                                    })
10170                                    .id("edit_prediction_cursor_popover_keybind")
10171                                    .when(accept_keystroke.is_none(), |el| {
10172                                        let status_colors = cx.theme().status();
10173
10174                                        el.bg(status_colors.error_background)
10175                                            .border_color(status_colors.error.opacity(0.6))
10176                                            .child(Icon::new(IconName::Info).color(Color::Error))
10177                                            .cursor_default()
10178                                            .hoverable_tooltip(move |_window, cx| {
10179                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10180                                                    .into()
10181                                            })
10182                                    })
10183                                    .when_some(
10184                                        accept_keystroke.as_ref(),
10185                                        |el, accept_keystroke| {
10186                                            el.child(h_flex().children(ui::render_modifiers(
10187                                                accept_keystroke.modifiers(),
10188                                                PlatformStyle::platform(),
10189                                                Some(Color::Default),
10190                                                Some(IconSize::XSmall.rems().into()),
10191                                                false,
10192                                            )))
10193                                        },
10194                                    ),
10195                            )
10196                            .into_any(),
10197                    );
10198                }
10199
10200                self.render_edit_prediction_cursor_popover_preview(
10201                    prediction,
10202                    cursor_point,
10203                    style,
10204                    cx,
10205                )?
10206            }
10207
10208            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10209                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10210                    stale_completion,
10211                    cursor_point,
10212                    style,
10213                    cx,
10214                )?,
10215
10216                None => pending_completion_container(icons.base)
10217                    .child(Label::new("...").size(LabelSize::Small)),
10218            },
10219
10220            None => pending_completion_container(icons.base)
10221                .child(Label::new("...").size(LabelSize::Small)),
10222        };
10223
10224        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10225            completion
10226                .with_animation(
10227                    "loading-completion",
10228                    Animation::new(Duration::from_secs(2))
10229                        .repeat()
10230                        .with_easing(pulsating_between(0.4, 0.8)),
10231                    |label, delta| label.opacity(delta),
10232                )
10233                .into_any_element()
10234        } else {
10235            completion.into_any_element()
10236        };
10237
10238        let has_completion = self.active_edit_prediction.is_some();
10239
10240        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10241        Some(
10242            h_flex()
10243                .min_w(min_width)
10244                .max_w(max_width)
10245                .flex_1()
10246                .elevation_2(cx)
10247                .border_color(cx.theme().colors().border)
10248                .child(
10249                    div()
10250                        .flex_1()
10251                        .py_1()
10252                        .px_2()
10253                        .overflow_hidden()
10254                        .child(completion),
10255                )
10256                .when_some(accept_keystroke, |el, accept_keystroke| {
10257                    if !accept_keystroke.modifiers().modified() {
10258                        return el;
10259                    }
10260
10261                    el.child(
10262                        h_flex()
10263                            .h_full()
10264                            .border_l_1()
10265                            .rounded_r_lg()
10266                            .border_color(cx.theme().colors().border)
10267                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10268                            .gap_1()
10269                            .py_1()
10270                            .px_2()
10271                            .child(
10272                                h_flex()
10273                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10274                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10275                                    .child(h_flex().children(ui::render_modifiers(
10276                                        accept_keystroke.modifiers(),
10277                                        PlatformStyle::platform(),
10278                                        Some(if !has_completion {
10279                                            Color::Muted
10280                                        } else {
10281                                            Color::Default
10282                                        }),
10283                                        None,
10284                                        false,
10285                                    ))),
10286                            )
10287                            .child(Label::new("Preview").into_any_element())
10288                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10289                    )
10290                })
10291                .into_any(),
10292        )
10293    }
10294
10295    fn render_edit_prediction_cursor_popover_preview(
10296        &self,
10297        completion: &EditPredictionState,
10298        cursor_point: Point,
10299        style: &EditorStyle,
10300        cx: &mut Context<Editor>,
10301    ) -> Option<Div> {
10302        use text::ToPoint as _;
10303
10304        fn render_relative_row_jump(
10305            prefix: impl Into<String>,
10306            current_row: u32,
10307            target_row: u32,
10308        ) -> Div {
10309            let (row_diff, arrow) = if target_row < current_row {
10310                (current_row - target_row, IconName::ArrowUp)
10311            } else {
10312                (target_row - current_row, IconName::ArrowDown)
10313            };
10314
10315            h_flex()
10316                .child(
10317                    Label::new(format!("{}{}", prefix.into(), row_diff))
10318                        .color(Color::Muted)
10319                        .size(LabelSize::Small),
10320                )
10321                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10322        }
10323
10324        let supports_jump = self
10325            .edit_prediction_provider
10326            .as_ref()
10327            .map(|provider| provider.provider.supports_jump_to_edit())
10328            .unwrap_or(true);
10329
10330        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10331
10332        match &completion.completion {
10333            EditPrediction::MoveWithin {
10334                target, snapshot, ..
10335            } => {
10336                if !supports_jump {
10337                    return None;
10338                }
10339
10340                Some(
10341                    h_flex()
10342                        .px_2()
10343                        .gap_2()
10344                        .flex_1()
10345                        .child(
10346                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10347                                Icon::new(icons.down)
10348                            } else {
10349                                Icon::new(icons.up)
10350                            },
10351                        )
10352                        .child(Label::new("Jump to Edit")),
10353                )
10354            }
10355            EditPrediction::MoveOutside { snapshot, .. } => {
10356                let file_name = snapshot
10357                    .file()
10358                    .map(|file| file.file_name(cx))
10359                    .unwrap_or("untitled");
10360                Some(
10361                    h_flex()
10362                        .px_2()
10363                        .gap_2()
10364                        .flex_1()
10365                        .child(Icon::new(icons.base))
10366                        .child(Label::new(format!("Jump to {file_name}"))),
10367                )
10368            }
10369            EditPrediction::Edit {
10370                edits,
10371                edit_preview,
10372                snapshot,
10373                ..
10374            } => {
10375                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10376
10377                let (highlighted_edits, has_more_lines) =
10378                    if let Some(edit_preview) = edit_preview.as_ref() {
10379                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10380                            .first_line_preview()
10381                    } else {
10382                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10383                    };
10384
10385                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10386                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10387
10388                let preview = h_flex()
10389                    .gap_1()
10390                    .min_w_16()
10391                    .child(styled_text)
10392                    .when(has_more_lines, |parent| parent.child(""));
10393
10394                let left = if supports_jump && first_edit_row != cursor_point.row {
10395                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10396                        .into_any_element()
10397                } else {
10398                    Icon::new(icons.base).into_any_element()
10399                };
10400
10401                Some(
10402                    h_flex()
10403                        .h_full()
10404                        .flex_1()
10405                        .gap_2()
10406                        .pr_1()
10407                        .overflow_x_hidden()
10408                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10409                        .child(left)
10410                        .child(preview),
10411                )
10412            }
10413        }
10414    }
10415
10416    pub fn render_context_menu(
10417        &mut self,
10418        max_height_in_lines: u32,
10419        window: &mut Window,
10420        cx: &mut Context<Editor>,
10421    ) -> Option<AnyElement> {
10422        let menu = self.context_menu.borrow();
10423        let menu = menu.as_ref()?;
10424        if !menu.visible() {
10425            return None;
10426        };
10427        self.style
10428            .as_ref()
10429            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10430    }
10431
10432    fn render_context_menu_aside(
10433        &mut self,
10434        max_size: Size<Pixels>,
10435        window: &mut Window,
10436        cx: &mut Context<Editor>,
10437    ) -> Option<AnyElement> {
10438        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10439            if menu.visible() {
10440                menu.render_aside(max_size, window, cx)
10441            } else {
10442                None
10443            }
10444        })
10445    }
10446
10447    fn hide_context_menu(
10448        &mut self,
10449        window: &mut Window,
10450        cx: &mut Context<Self>,
10451    ) -> Option<CodeContextMenu> {
10452        cx.notify();
10453        self.completion_tasks.clear();
10454        let context_menu = self.context_menu.borrow_mut().take();
10455        self.stale_edit_prediction_in_menu.take();
10456        self.update_visible_edit_prediction(window, cx);
10457        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10458            && let Some(completion_provider) = &self.completion_provider
10459        {
10460            completion_provider.selection_changed(None, window, cx);
10461        }
10462        context_menu
10463    }
10464
10465    fn show_snippet_choices(
10466        &mut self,
10467        choices: &Vec<String>,
10468        selection: Range<Anchor>,
10469        cx: &mut Context<Self>,
10470    ) {
10471        let Some((_, buffer, _)) = self
10472            .buffer()
10473            .read(cx)
10474            .excerpt_containing(selection.start, cx)
10475        else {
10476            return;
10477        };
10478        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10479        else {
10480            return;
10481        };
10482        if buffer != end_buffer {
10483            log::error!("expected anchor range to have matching buffer IDs");
10484            return;
10485        }
10486
10487        let id = post_inc(&mut self.next_completion_id);
10488        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10489        let mut context_menu = self.context_menu.borrow_mut();
10490        let old_menu = context_menu.take();
10491        *context_menu = Some(CodeContextMenu::Completions(
10492            CompletionsMenu::new_snippet_choices(
10493                id,
10494                true,
10495                choices,
10496                selection,
10497                buffer,
10498                old_menu.map(|menu| menu.primary_scroll_handle()),
10499                snippet_sort_order,
10500            ),
10501        ));
10502    }
10503
10504    pub fn insert_snippet(
10505        &mut self,
10506        insertion_ranges: &[Range<MultiBufferOffset>],
10507        snippet: Snippet,
10508        window: &mut Window,
10509        cx: &mut Context<Self>,
10510    ) -> Result<()> {
10511        struct Tabstop<T> {
10512            is_end_tabstop: bool,
10513            ranges: Vec<Range<T>>,
10514            choices: Option<Vec<String>>,
10515        }
10516
10517        let tabstops = self.buffer.update(cx, |buffer, cx| {
10518            let snippet_text: Arc<str> = snippet.text.clone().into();
10519            let edits = insertion_ranges
10520                .iter()
10521                .cloned()
10522                .map(|range| (range, snippet_text.clone()));
10523            let autoindent_mode = AutoindentMode::Block {
10524                original_indent_columns: Vec::new(),
10525            };
10526            buffer.edit(edits, Some(autoindent_mode), cx);
10527
10528            let snapshot = &*buffer.read(cx);
10529            let snippet = &snippet;
10530            snippet
10531                .tabstops
10532                .iter()
10533                .map(|tabstop| {
10534                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10535                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10536                    });
10537                    let mut tabstop_ranges = tabstop
10538                        .ranges
10539                        .iter()
10540                        .flat_map(|tabstop_range| {
10541                            let mut delta = 0_isize;
10542                            insertion_ranges.iter().map(move |insertion_range| {
10543                                let insertion_start = insertion_range.start + delta;
10544                                delta += snippet.text.len() as isize
10545                                    - (insertion_range.end - insertion_range.start) as isize;
10546
10547                                let start =
10548                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10549                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10550                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10551                            })
10552                        })
10553                        .collect::<Vec<_>>();
10554                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10555
10556                    Tabstop {
10557                        is_end_tabstop,
10558                        ranges: tabstop_ranges,
10559                        choices: tabstop.choices.clone(),
10560                    }
10561                })
10562                .collect::<Vec<_>>()
10563        });
10564        if let Some(tabstop) = tabstops.first() {
10565            self.change_selections(Default::default(), window, cx, |s| {
10566                // Reverse order so that the first range is the newest created selection.
10567                // Completions will use it and autoscroll will prioritize it.
10568                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10569            });
10570
10571            if let Some(choices) = &tabstop.choices
10572                && let Some(selection) = tabstop.ranges.first()
10573            {
10574                self.show_snippet_choices(choices, selection.clone(), cx)
10575            }
10576
10577            // If we're already at the last tabstop and it's at the end of the snippet,
10578            // we're done, we don't need to keep the state around.
10579            if !tabstop.is_end_tabstop {
10580                let choices = tabstops
10581                    .iter()
10582                    .map(|tabstop| tabstop.choices.clone())
10583                    .collect();
10584
10585                let ranges = tabstops
10586                    .into_iter()
10587                    .map(|tabstop| tabstop.ranges)
10588                    .collect::<Vec<_>>();
10589
10590                self.snippet_stack.push(SnippetState {
10591                    active_index: 0,
10592                    ranges,
10593                    choices,
10594                });
10595            }
10596
10597            // Check whether the just-entered snippet ends with an auto-closable bracket.
10598            if self.autoclose_regions.is_empty() {
10599                let snapshot = self.buffer.read(cx).snapshot(cx);
10600                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10601                    let selection_head = selection.head();
10602                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10603                        continue;
10604                    };
10605
10606                    let mut bracket_pair = None;
10607                    let max_lookup_length = scope
10608                        .brackets()
10609                        .map(|(pair, _)| {
10610                            pair.start
10611                                .as_str()
10612                                .chars()
10613                                .count()
10614                                .max(pair.end.as_str().chars().count())
10615                        })
10616                        .max();
10617                    if let Some(max_lookup_length) = max_lookup_length {
10618                        let next_text = snapshot
10619                            .chars_at(selection_head)
10620                            .take(max_lookup_length)
10621                            .collect::<String>();
10622                        let prev_text = snapshot
10623                            .reversed_chars_at(selection_head)
10624                            .take(max_lookup_length)
10625                            .collect::<String>();
10626
10627                        for (pair, enabled) in scope.brackets() {
10628                            if enabled
10629                                && pair.close
10630                                && prev_text.starts_with(pair.start.as_str())
10631                                && next_text.starts_with(pair.end.as_str())
10632                            {
10633                                bracket_pair = Some(pair.clone());
10634                                break;
10635                            }
10636                        }
10637                    }
10638
10639                    if let Some(pair) = bracket_pair {
10640                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10641                        let autoclose_enabled =
10642                            self.use_autoclose && snapshot_settings.use_autoclose;
10643                        if autoclose_enabled {
10644                            let start = snapshot.anchor_after(selection_head);
10645                            let end = snapshot.anchor_after(selection_head);
10646                            self.autoclose_regions.push(AutocloseRegion {
10647                                selection_id: selection.id,
10648                                range: start..end,
10649                                pair,
10650                            });
10651                        }
10652                    }
10653                }
10654            }
10655        }
10656        Ok(())
10657    }
10658
10659    pub fn move_to_next_snippet_tabstop(
10660        &mut self,
10661        window: &mut Window,
10662        cx: &mut Context<Self>,
10663    ) -> bool {
10664        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10665    }
10666
10667    pub fn move_to_prev_snippet_tabstop(
10668        &mut self,
10669        window: &mut Window,
10670        cx: &mut Context<Self>,
10671    ) -> bool {
10672        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10673    }
10674
10675    pub fn move_to_snippet_tabstop(
10676        &mut self,
10677        bias: Bias,
10678        window: &mut Window,
10679        cx: &mut Context<Self>,
10680    ) -> bool {
10681        if let Some(mut snippet) = self.snippet_stack.pop() {
10682            match bias {
10683                Bias::Left => {
10684                    if snippet.active_index > 0 {
10685                        snippet.active_index -= 1;
10686                    } else {
10687                        self.snippet_stack.push(snippet);
10688                        return false;
10689                    }
10690                }
10691                Bias::Right => {
10692                    if snippet.active_index + 1 < snippet.ranges.len() {
10693                        snippet.active_index += 1;
10694                    } else {
10695                        self.snippet_stack.push(snippet);
10696                        return false;
10697                    }
10698                }
10699            }
10700            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10701                self.change_selections(Default::default(), window, cx, |s| {
10702                    // Reverse order so that the first range is the newest created selection.
10703                    // Completions will use it and autoscroll will prioritize it.
10704                    s.select_ranges(current_ranges.iter().rev().cloned())
10705                });
10706
10707                if let Some(choices) = &snippet.choices[snippet.active_index]
10708                    && let Some(selection) = current_ranges.first()
10709                {
10710                    self.show_snippet_choices(choices, selection.clone(), cx);
10711                }
10712
10713                // If snippet state is not at the last tabstop, push it back on the stack
10714                if snippet.active_index + 1 < snippet.ranges.len() {
10715                    self.snippet_stack.push(snippet);
10716                }
10717                return true;
10718            }
10719        }
10720
10721        false
10722    }
10723
10724    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10725        self.transact(window, cx, |this, window, cx| {
10726            this.select_all(&SelectAll, window, cx);
10727            this.insert("", window, cx);
10728        });
10729    }
10730
10731    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10732        if self.read_only(cx) {
10733            return;
10734        }
10735        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10736        self.transact(window, cx, |this, window, cx| {
10737            this.select_autoclose_pair(window, cx);
10738
10739            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10740
10741            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10742            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10743            for selection in &mut selections {
10744                if selection.is_empty() {
10745                    let old_head = selection.head();
10746                    let mut new_head =
10747                        movement::left(&display_map, old_head.to_display_point(&display_map))
10748                            .to_point(&display_map);
10749                    if let Some((buffer, line_buffer_range)) = display_map
10750                        .buffer_snapshot()
10751                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10752                    {
10753                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10754                        let indent_len = match indent_size.kind {
10755                            IndentKind::Space => {
10756                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10757                            }
10758                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10759                        };
10760                        if old_head.column <= indent_size.len && old_head.column > 0 {
10761                            let indent_len = indent_len.get();
10762                            new_head = cmp::min(
10763                                new_head,
10764                                MultiBufferPoint::new(
10765                                    old_head.row,
10766                                    ((old_head.column - 1) / indent_len) * indent_len,
10767                                ),
10768                            );
10769                        }
10770                    }
10771
10772                    selection.set_head(new_head, SelectionGoal::None);
10773                }
10774            }
10775
10776            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10777            this.insert("", window, cx);
10778            linked_edits.apply_with_left_expansion(cx);
10779            this.refresh_edit_prediction(true, false, window, cx);
10780            refresh_linked_ranges(this, window, cx);
10781        });
10782    }
10783
10784    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10785        if self.read_only(cx) {
10786            return;
10787        }
10788        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10789        self.transact(window, cx, |this, window, cx| {
10790            this.change_selections(Default::default(), window, cx, |s| {
10791                s.move_with(&mut |map, selection| {
10792                    if selection.is_empty() {
10793                        let cursor = movement::right(map, selection.head());
10794                        selection.end = cursor;
10795                        selection.reversed = true;
10796                        selection.goal = SelectionGoal::None;
10797                    }
10798                })
10799            });
10800            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10801            this.insert("", window, cx);
10802            linked_edits.apply(cx);
10803            this.refresh_edit_prediction(true, false, window, cx);
10804            refresh_linked_ranges(this, window, cx);
10805        });
10806    }
10807
10808    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10809        if self.mode.is_single_line() {
10810            cx.propagate();
10811            return;
10812        }
10813
10814        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10815        if self.move_to_prev_snippet_tabstop(window, cx) {
10816            return;
10817        }
10818        self.outdent(&Outdent, window, cx);
10819    }
10820
10821    pub fn next_snippet_tabstop(
10822        &mut self,
10823        _: &NextSnippetTabstop,
10824        window: &mut Window,
10825        cx: &mut Context<Self>,
10826    ) {
10827        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10828            cx.propagate();
10829            return;
10830        }
10831
10832        if self.move_to_next_snippet_tabstop(window, cx) {
10833            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10834            return;
10835        }
10836        cx.propagate();
10837    }
10838
10839    pub fn previous_snippet_tabstop(
10840        &mut self,
10841        _: &PreviousSnippetTabstop,
10842        window: &mut Window,
10843        cx: &mut Context<Self>,
10844    ) {
10845        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10846            cx.propagate();
10847            return;
10848        }
10849
10850        if self.move_to_prev_snippet_tabstop(window, cx) {
10851            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10852            return;
10853        }
10854        cx.propagate();
10855    }
10856
10857    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10858        if self.mode.is_single_line() {
10859            cx.propagate();
10860            return;
10861        }
10862
10863        if self.move_to_next_snippet_tabstop(window, cx) {
10864            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10865            return;
10866        }
10867        if self.read_only(cx) {
10868            return;
10869        }
10870        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10871        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10872        let buffer = self.buffer.read(cx);
10873        let snapshot = buffer.snapshot(cx);
10874        let rows_iter = selections.iter().map(|s| s.head().row);
10875        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10876
10877        let has_some_cursor_in_whitespace = selections
10878            .iter()
10879            .filter(|selection| selection.is_empty())
10880            .any(|selection| {
10881                let cursor = selection.head();
10882                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10883                cursor.column < current_indent.len
10884            });
10885
10886        let mut edits = Vec::new();
10887        let mut prev_edited_row = 0;
10888        let mut row_delta = 0;
10889        for selection in &mut selections {
10890            if selection.start.row != prev_edited_row {
10891                row_delta = 0;
10892            }
10893            prev_edited_row = selection.end.row;
10894
10895            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10896            if selection.is_empty() {
10897                let cursor = selection.head();
10898                let settings = buffer.language_settings_at(cursor, cx);
10899                if settings.indent_list_on_tab {
10900                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10901                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10902                            row_delta = Self::indent_selection(
10903                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10904                            );
10905                            continue;
10906                        }
10907                    }
10908                }
10909            }
10910
10911            // If the selection is non-empty, then increase the indentation of the selected lines.
10912            if !selection.is_empty() {
10913                row_delta =
10914                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10915                continue;
10916            }
10917
10918            let cursor = selection.head();
10919            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10920            if let Some(suggested_indent) =
10921                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10922            {
10923                // Don't do anything if already at suggested indent
10924                // and there is any other cursor which is not
10925                if has_some_cursor_in_whitespace
10926                    && cursor.column == current_indent.len
10927                    && current_indent.len == suggested_indent.len
10928                {
10929                    continue;
10930                }
10931
10932                // Adjust line and move cursor to suggested indent
10933                // if cursor is not at suggested indent
10934                if cursor.column < suggested_indent.len
10935                    && cursor.column <= current_indent.len
10936                    && current_indent.len <= suggested_indent.len
10937                {
10938                    selection.start = Point::new(cursor.row, suggested_indent.len);
10939                    selection.end = selection.start;
10940                    if row_delta == 0 {
10941                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10942                            cursor.row,
10943                            current_indent,
10944                            suggested_indent,
10945                        ));
10946                        row_delta = suggested_indent.len - current_indent.len;
10947                    }
10948                    continue;
10949                }
10950
10951                // If current indent is more than suggested indent
10952                // only move cursor to current indent and skip indent
10953                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10954                    selection.start = Point::new(cursor.row, current_indent.len);
10955                    selection.end = selection.start;
10956                    continue;
10957                }
10958            }
10959
10960            // Otherwise, insert a hard or soft tab.
10961            let settings = buffer.language_settings_at(cursor, cx);
10962            let tab_size = if settings.hard_tabs {
10963                IndentSize::tab()
10964            } else {
10965                let tab_size = settings.tab_size.get();
10966                let indent_remainder = snapshot
10967                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10968                    .flat_map(str::chars)
10969                    .fold(row_delta % tab_size, |counter: u32, c| {
10970                        if c == '\t' {
10971                            0
10972                        } else {
10973                            (counter + 1) % tab_size
10974                        }
10975                    });
10976
10977                let chars_to_next_tab_stop = tab_size - indent_remainder;
10978                IndentSize::spaces(chars_to_next_tab_stop)
10979            };
10980            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10981            selection.end = selection.start;
10982            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10983            row_delta += tab_size.len;
10984        }
10985
10986        self.transact(window, cx, |this, window, cx| {
10987            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10988            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10989            this.refresh_edit_prediction(true, false, window, cx);
10990        });
10991    }
10992
10993    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10994        if self.read_only(cx) {
10995            return;
10996        }
10997        if self.mode.is_single_line() {
10998            cx.propagate();
10999            return;
11000        }
11001
11002        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11003        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11004        let mut prev_edited_row = 0;
11005        let mut row_delta = 0;
11006        let mut edits = Vec::new();
11007        let buffer = self.buffer.read(cx);
11008        let snapshot = buffer.snapshot(cx);
11009        for selection in &mut selections {
11010            if selection.start.row != prev_edited_row {
11011                row_delta = 0;
11012            }
11013            prev_edited_row = selection.end.row;
11014
11015            row_delta =
11016                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11017        }
11018
11019        self.transact(window, cx, |this, window, cx| {
11020            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11021            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11022        });
11023    }
11024
11025    fn indent_selection(
11026        buffer: &MultiBuffer,
11027        snapshot: &MultiBufferSnapshot,
11028        selection: &mut Selection<Point>,
11029        edits: &mut Vec<(Range<Point>, String)>,
11030        delta_for_start_row: u32,
11031        cx: &App,
11032    ) -> u32 {
11033        let settings = buffer.language_settings_at(selection.start, cx);
11034        let tab_size = settings.tab_size.get();
11035        let indent_kind = if settings.hard_tabs {
11036            IndentKind::Tab
11037        } else {
11038            IndentKind::Space
11039        };
11040        let mut start_row = selection.start.row;
11041        let mut end_row = selection.end.row + 1;
11042
11043        // If a selection ends at the beginning of a line, don't indent
11044        // that last line.
11045        if selection.end.column == 0 && selection.end.row > selection.start.row {
11046            end_row -= 1;
11047        }
11048
11049        // Avoid re-indenting a row that has already been indented by a
11050        // previous selection, but still update this selection's column
11051        // to reflect that indentation.
11052        if delta_for_start_row > 0 {
11053            start_row += 1;
11054            selection.start.column += delta_for_start_row;
11055            if selection.end.row == selection.start.row {
11056                selection.end.column += delta_for_start_row;
11057            }
11058        }
11059
11060        let mut delta_for_end_row = 0;
11061        let has_multiple_rows = start_row + 1 != end_row;
11062        for row in start_row..end_row {
11063            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11064            let indent_delta = match (current_indent.kind, indent_kind) {
11065                (IndentKind::Space, IndentKind::Space) => {
11066                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11067                    IndentSize::spaces(columns_to_next_tab_stop)
11068                }
11069                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11070                (_, IndentKind::Tab) => IndentSize::tab(),
11071            };
11072
11073            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11074                0
11075            } else {
11076                selection.start.column
11077            };
11078            let row_start = Point::new(row, start);
11079            edits.push((
11080                row_start..row_start,
11081                indent_delta.chars().collect::<String>(),
11082            ));
11083
11084            // Update this selection's endpoints to reflect the indentation.
11085            if row == selection.start.row {
11086                selection.start.column += indent_delta.len;
11087            }
11088            if row == selection.end.row {
11089                selection.end.column += indent_delta.len;
11090                delta_for_end_row = indent_delta.len;
11091            }
11092        }
11093
11094        if selection.start.row == selection.end.row {
11095            delta_for_start_row + delta_for_end_row
11096        } else {
11097            delta_for_end_row
11098        }
11099    }
11100
11101    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11102        if self.read_only(cx) {
11103            return;
11104        }
11105        if self.mode.is_single_line() {
11106            cx.propagate();
11107            return;
11108        }
11109
11110        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11111        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11112        let selections = self.selections.all::<Point>(&display_map);
11113        let mut deletion_ranges = Vec::new();
11114        let mut last_outdent = None;
11115        {
11116            let buffer = self.buffer.read(cx);
11117            let snapshot = buffer.snapshot(cx);
11118            for selection in &selections {
11119                let settings = buffer.language_settings_at(selection.start, cx);
11120                let tab_size = settings.tab_size.get();
11121                let mut rows = selection.spanned_rows(false, &display_map);
11122
11123                // Avoid re-outdenting a row that has already been outdented by a
11124                // previous selection.
11125                if let Some(last_row) = last_outdent
11126                    && last_row == rows.start
11127                {
11128                    rows.start = rows.start.next_row();
11129                }
11130                let has_multiple_rows = rows.len() > 1;
11131                for row in rows.iter_rows() {
11132                    let indent_size = snapshot.indent_size_for_line(row);
11133                    if indent_size.len > 0 {
11134                        let deletion_len = match indent_size.kind {
11135                            IndentKind::Space => {
11136                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11137                                if columns_to_prev_tab_stop == 0 {
11138                                    tab_size
11139                                } else {
11140                                    columns_to_prev_tab_stop
11141                                }
11142                            }
11143                            IndentKind::Tab => 1,
11144                        };
11145                        let start = if has_multiple_rows
11146                            || deletion_len > selection.start.column
11147                            || indent_size.len < selection.start.column
11148                        {
11149                            0
11150                        } else {
11151                            selection.start.column - deletion_len
11152                        };
11153                        deletion_ranges.push(
11154                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11155                        );
11156                        last_outdent = Some(row);
11157                    }
11158                }
11159            }
11160        }
11161
11162        self.transact(window, cx, |this, window, cx| {
11163            this.buffer.update(cx, |buffer, cx| {
11164                let empty_str: Arc<str> = Arc::default();
11165                buffer.edit(
11166                    deletion_ranges
11167                        .into_iter()
11168                        .map(|range| (range, empty_str.clone())),
11169                    None,
11170                    cx,
11171                );
11172            });
11173            let selections = this
11174                .selections
11175                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11176            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11177        });
11178    }
11179
11180    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11181        if self.read_only(cx) {
11182            return;
11183        }
11184        if self.mode.is_single_line() {
11185            cx.propagate();
11186            return;
11187        }
11188
11189        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11190        let selections = self
11191            .selections
11192            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11193            .into_iter()
11194            .map(|s| s.range());
11195
11196        self.transact(window, cx, |this, window, cx| {
11197            this.buffer.update(cx, |buffer, cx| {
11198                buffer.autoindent_ranges(selections, cx);
11199            });
11200            let selections = this
11201                .selections
11202                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11203            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11204        });
11205    }
11206
11207    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11208        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11209        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11210        let selections = self.selections.all::<Point>(&display_map);
11211
11212        let mut new_cursors = Vec::new();
11213        let mut edit_ranges = Vec::new();
11214        let mut selections = selections.iter().peekable();
11215        while let Some(selection) = selections.next() {
11216            let mut rows = selection.spanned_rows(false, &display_map);
11217
11218            // Accumulate contiguous regions of rows that we want to delete.
11219            while let Some(next_selection) = selections.peek() {
11220                let next_rows = next_selection.spanned_rows(false, &display_map);
11221                if next_rows.start <= rows.end {
11222                    rows.end = next_rows.end;
11223                    selections.next().unwrap();
11224                } else {
11225                    break;
11226                }
11227            }
11228
11229            let buffer = display_map.buffer_snapshot();
11230            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11231            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11232                // If there's a line after the range, delete the \n from the end of the row range
11233                (
11234                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11235                    rows.end,
11236                )
11237            } else {
11238                // If there isn't a line after the range, delete the \n from the line before the
11239                // start of the row range
11240                edit_start = edit_start.saturating_sub_usize(1);
11241                (buffer.len(), rows.start.previous_row())
11242            };
11243
11244            let text_layout_details = self.text_layout_details(window, cx);
11245            let x = display_map.x_for_display_point(
11246                selection.head().to_display_point(&display_map),
11247                &text_layout_details,
11248            );
11249            let row = Point::new(target_row.0, 0)
11250                .to_display_point(&display_map)
11251                .row();
11252            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11253
11254            new_cursors.push((
11255                selection.id,
11256                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11257                SelectionGoal::None,
11258            ));
11259            edit_ranges.push(edit_start..edit_end);
11260        }
11261
11262        self.transact(window, cx, |this, window, cx| {
11263            let buffer = this.buffer.update(cx, |buffer, cx| {
11264                let empty_str: Arc<str> = Arc::default();
11265                buffer.edit(
11266                    edit_ranges
11267                        .into_iter()
11268                        .map(|range| (range, empty_str.clone())),
11269                    None,
11270                    cx,
11271                );
11272                buffer.snapshot(cx)
11273            });
11274            let new_selections = new_cursors
11275                .into_iter()
11276                .map(|(id, cursor, goal)| {
11277                    let cursor = cursor.to_point(&buffer);
11278                    Selection {
11279                        id,
11280                        start: cursor,
11281                        end: cursor,
11282                        reversed: false,
11283                        goal,
11284                    }
11285                })
11286                .collect();
11287
11288            this.change_selections(Default::default(), window, cx, |s| {
11289                s.select(new_selections);
11290            });
11291        });
11292    }
11293
11294    pub fn join_lines_impl(
11295        &mut self,
11296        insert_whitespace: bool,
11297        window: &mut Window,
11298        cx: &mut Context<Self>,
11299    ) {
11300        if self.read_only(cx) {
11301            return;
11302        }
11303        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11304        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11305            let start = MultiBufferRow(selection.start.row);
11306            // Treat single line selections as if they include the next line. Otherwise this action
11307            // would do nothing for single line selections individual cursors.
11308            let end = if selection.start.row == selection.end.row {
11309                MultiBufferRow(selection.start.row + 1)
11310            } else {
11311                MultiBufferRow(selection.end.row)
11312            };
11313
11314            if let Some(last_row_range) = row_ranges.last_mut()
11315                && start <= last_row_range.end
11316            {
11317                last_row_range.end = end;
11318                continue;
11319            }
11320            row_ranges.push(start..end);
11321        }
11322
11323        let snapshot = self.buffer.read(cx).snapshot(cx);
11324        let mut cursor_positions = Vec::new();
11325        for row_range in &row_ranges {
11326            let anchor = snapshot.anchor_before(Point::new(
11327                row_range.end.previous_row().0,
11328                snapshot.line_len(row_range.end.previous_row()),
11329            ));
11330            cursor_positions.push(anchor..anchor);
11331        }
11332
11333        self.transact(window, cx, |this, window, cx| {
11334            for row_range in row_ranges.into_iter().rev() {
11335                for row in row_range.iter_rows().rev() {
11336                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11337                    let next_line_row = row.next_row();
11338                    let indent = snapshot.indent_size_for_line(next_line_row);
11339                    let mut join_start_column = indent.len;
11340
11341                    if let Some(language_scope) =
11342                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11343                    {
11344                        let line_end =
11345                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11346                        let line_text_after_indent = snapshot
11347                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11348                            .collect::<String>();
11349
11350                        if !line_text_after_indent.is_empty() {
11351                            let block_prefix = language_scope
11352                                .block_comment()
11353                                .map(|c| c.prefix.as_ref())
11354                                .filter(|p| !p.is_empty());
11355                            let doc_prefix = language_scope
11356                                .documentation_comment()
11357                                .map(|c| c.prefix.as_ref())
11358                                .filter(|p| !p.is_empty());
11359                            let all_prefixes = language_scope
11360                                .line_comment_prefixes()
11361                                .iter()
11362                                .map(|p| p.as_ref())
11363                                .chain(block_prefix)
11364                                .chain(doc_prefix)
11365                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11366
11367                            let mut longest_prefix_len = None;
11368                            for prefix in all_prefixes {
11369                                let trimmed = prefix.trim_end();
11370                                if line_text_after_indent.starts_with(trimmed) {
11371                                    let candidate_len =
11372                                        if line_text_after_indent.starts_with(prefix) {
11373                                            prefix.len()
11374                                        } else {
11375                                            trimmed.len()
11376                                        };
11377                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11378                                        longest_prefix_len = Some(candidate_len);
11379                                    }
11380                                }
11381                            }
11382
11383                            if let Some(prefix_len) = longest_prefix_len {
11384                                join_start_column =
11385                                    join_start_column.saturating_add(prefix_len as u32);
11386                            }
11387                        }
11388                    }
11389
11390                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11391
11392                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11393                        && insert_whitespace
11394                    {
11395                        " "
11396                    } else {
11397                        ""
11398                    };
11399
11400                    this.buffer.update(cx, |buffer, cx| {
11401                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11402                    });
11403                }
11404            }
11405
11406            this.change_selections(Default::default(), window, cx, |s| {
11407                s.select_anchor_ranges(cursor_positions)
11408            });
11409        });
11410    }
11411
11412    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11413        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11414        self.join_lines_impl(true, window, cx);
11415    }
11416
11417    pub fn sort_lines_case_sensitive(
11418        &mut self,
11419        _: &SortLinesCaseSensitive,
11420        window: &mut Window,
11421        cx: &mut Context<Self>,
11422    ) {
11423        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11424    }
11425
11426    pub fn sort_lines_by_length(
11427        &mut self,
11428        _: &SortLinesByLength,
11429        window: &mut Window,
11430        cx: &mut Context<Self>,
11431    ) {
11432        self.manipulate_immutable_lines(window, cx, |lines| {
11433            lines.sort_by_key(|&line| line.chars().count())
11434        })
11435    }
11436
11437    pub fn sort_lines_case_insensitive(
11438        &mut self,
11439        _: &SortLinesCaseInsensitive,
11440        window: &mut Window,
11441        cx: &mut Context<Self>,
11442    ) {
11443        self.manipulate_immutable_lines(window, cx, |lines| {
11444            lines.sort_by_key(|line| line.to_lowercase())
11445        })
11446    }
11447
11448    pub fn unique_lines_case_insensitive(
11449        &mut self,
11450        _: &UniqueLinesCaseInsensitive,
11451        window: &mut Window,
11452        cx: &mut Context<Self>,
11453    ) {
11454        self.manipulate_immutable_lines(window, cx, |lines| {
11455            let mut seen = HashSet::default();
11456            lines.retain(|line| seen.insert(line.to_lowercase()));
11457        })
11458    }
11459
11460    pub fn unique_lines_case_sensitive(
11461        &mut self,
11462        _: &UniqueLinesCaseSensitive,
11463        window: &mut Window,
11464        cx: &mut Context<Self>,
11465    ) {
11466        self.manipulate_immutable_lines(window, cx, |lines| {
11467            let mut seen = HashSet::default();
11468            lines.retain(|line| seen.insert(*line));
11469        })
11470    }
11471
11472    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11473        let snapshot = self.buffer.read(cx).snapshot(cx);
11474        for selection in self.selections.disjoint_anchors_arc().iter() {
11475            if snapshot
11476                .language_at(selection.start)
11477                .and_then(|lang| lang.config().wrap_characters.as_ref())
11478                .is_some()
11479            {
11480                return true;
11481            }
11482        }
11483        false
11484    }
11485
11486    fn wrap_selections_in_tag(
11487        &mut self,
11488        _: &WrapSelectionsInTag,
11489        window: &mut Window,
11490        cx: &mut Context<Self>,
11491    ) {
11492        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11493
11494        let snapshot = self.buffer.read(cx).snapshot(cx);
11495
11496        let mut edits = Vec::new();
11497        let mut boundaries = Vec::new();
11498
11499        for selection in self
11500            .selections
11501            .all_adjusted(&self.display_snapshot(cx))
11502            .iter()
11503        {
11504            let Some(wrap_config) = snapshot
11505                .language_at(selection.start)
11506                .and_then(|lang| lang.config().wrap_characters.clone())
11507            else {
11508                continue;
11509            };
11510
11511            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11512            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11513
11514            let start_before = snapshot.anchor_before(selection.start);
11515            let end_after = snapshot.anchor_after(selection.end);
11516
11517            edits.push((start_before..start_before, open_tag));
11518            edits.push((end_after..end_after, close_tag));
11519
11520            boundaries.push((
11521                start_before,
11522                end_after,
11523                wrap_config.start_prefix.len(),
11524                wrap_config.end_suffix.len(),
11525            ));
11526        }
11527
11528        if edits.is_empty() {
11529            return;
11530        }
11531
11532        self.transact(window, cx, |this, window, cx| {
11533            let buffer = this.buffer.update(cx, |buffer, cx| {
11534                buffer.edit(edits, None, cx);
11535                buffer.snapshot(cx)
11536            });
11537
11538            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11539            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11540                boundaries.into_iter()
11541            {
11542                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11543                let close_offset = end_after
11544                    .to_offset(&buffer)
11545                    .saturating_sub_usize(end_suffix_len);
11546                new_selections.push(open_offset..open_offset);
11547                new_selections.push(close_offset..close_offset);
11548            }
11549
11550            this.change_selections(Default::default(), window, cx, |s| {
11551                s.select_ranges(new_selections);
11552            });
11553
11554            this.request_autoscroll(Autoscroll::fit(), cx);
11555        });
11556    }
11557
11558    pub fn toggle_read_only(
11559        &mut self,
11560        _: &workspace::ToggleReadOnlyFile,
11561        _: &mut Window,
11562        cx: &mut Context<Self>,
11563    ) {
11564        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11565            buffer.update(cx, |buffer, cx| {
11566                buffer.set_capability(
11567                    match buffer.capability() {
11568                        Capability::ReadWrite => Capability::Read,
11569                        Capability::Read => Capability::ReadWrite,
11570                        Capability::ReadOnly => Capability::ReadOnly,
11571                    },
11572                    cx,
11573                );
11574            })
11575        }
11576    }
11577
11578    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11579        let Some(project) = self.project.clone() else {
11580            return;
11581        };
11582        let task = self.reload(project, window, cx);
11583        self.detach_and_notify_err(task, window, cx);
11584    }
11585
11586    pub fn restore_file(
11587        &mut self,
11588        _: &::git::RestoreFile,
11589        window: &mut Window,
11590        cx: &mut Context<Self>,
11591    ) {
11592        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11593        let mut buffer_ids = HashSet::default();
11594        let snapshot = self.buffer().read(cx).snapshot(cx);
11595        for selection in self
11596            .selections
11597            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11598        {
11599            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11600        }
11601
11602        let buffer = self.buffer().read(cx);
11603        let ranges = buffer_ids
11604            .into_iter()
11605            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11606            .collect::<Vec<_>>();
11607
11608        self.restore_hunks_in_ranges(ranges, window, cx);
11609    }
11610
11611    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11612        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11613        let selections = self
11614            .selections
11615            .all(&self.display_snapshot(cx))
11616            .into_iter()
11617            .map(|s| s.range())
11618            .collect();
11619        self.restore_hunks_in_ranges(selections, window, cx);
11620    }
11621
11622    pub fn restore_hunks_in_ranges(
11623        &mut self,
11624        ranges: Vec<Range<Point>>,
11625        window: &mut Window,
11626        cx: &mut Context<Editor>,
11627    ) {
11628        if self.delegate_stage_and_restore {
11629            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11630            if !hunks.is_empty() {
11631                cx.emit(EditorEvent::RestoreRequested { hunks });
11632            }
11633            return;
11634        }
11635        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11636        self.transact(window, cx, |editor, window, cx| {
11637            editor.restore_diff_hunks(hunks, cx);
11638            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11639                selections.refresh()
11640            });
11641        });
11642    }
11643
11644    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11645        let mut revert_changes = HashMap::default();
11646        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11647        for (buffer_id, hunks) in &chunk_by {
11648            let hunks = hunks.collect::<Vec<_>>();
11649            for hunk in &hunks {
11650                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11651            }
11652            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11653        }
11654        if !revert_changes.is_empty() {
11655            self.buffer().update(cx, |multi_buffer, cx| {
11656                for (buffer_id, changes) in revert_changes {
11657                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11658                        buffer.update(cx, |buffer, cx| {
11659                            buffer.edit(
11660                                changes
11661                                    .into_iter()
11662                                    .map(|(range, text)| (range, text.to_string())),
11663                                None,
11664                                cx,
11665                            );
11666                        });
11667                    }
11668                }
11669            });
11670        }
11671    }
11672
11673    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11674        if let Some(status) = self
11675            .addons
11676            .iter()
11677            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11678        {
11679            return Some(status);
11680        }
11681        self.project
11682            .as_ref()?
11683            .read(cx)
11684            .status_for_buffer_id(buffer_id, cx)
11685    }
11686
11687    pub fn open_active_item_in_terminal(
11688        &mut self,
11689        _: &OpenInTerminal,
11690        window: &mut Window,
11691        cx: &mut Context<Self>,
11692    ) {
11693        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11694            let project_path = buffer.read(cx).project_path(cx)?;
11695            let project = self.project()?.read(cx);
11696            let entry = project.entry_for_path(&project_path, cx)?;
11697            let parent = match &entry.canonical_path {
11698                Some(canonical_path) => canonical_path.to_path_buf(),
11699                None => project.absolute_path(&project_path, cx)?,
11700            }
11701            .parent()?
11702            .to_path_buf();
11703            Some(parent)
11704        }) {
11705            window.dispatch_action(
11706                OpenTerminal {
11707                    working_directory,
11708                    local: false,
11709                }
11710                .boxed_clone(),
11711                cx,
11712            );
11713        }
11714    }
11715
11716    fn set_breakpoint_context_menu(
11717        &mut self,
11718        display_row: DisplayRow,
11719        position: Option<Anchor>,
11720        clicked_point: gpui::Point<Pixels>,
11721        window: &mut Window,
11722        cx: &mut Context<Self>,
11723    ) {
11724        let source = self
11725            .buffer
11726            .read(cx)
11727            .snapshot(cx)
11728            .anchor_before(Point::new(display_row.0, 0u32));
11729
11730        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11731
11732        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11733            self,
11734            source,
11735            clicked_point,
11736            context_menu,
11737            window,
11738            cx,
11739        );
11740    }
11741
11742    fn add_edit_breakpoint_block(
11743        &mut self,
11744        anchor: Anchor,
11745        breakpoint: &Breakpoint,
11746        edit_action: BreakpointPromptEditAction,
11747        window: &mut Window,
11748        cx: &mut Context<Self>,
11749    ) {
11750        let weak_editor = cx.weak_entity();
11751        let bp_prompt = cx.new(|cx| {
11752            BreakpointPromptEditor::new(
11753                weak_editor,
11754                anchor,
11755                breakpoint.clone(),
11756                edit_action,
11757                window,
11758                cx,
11759            )
11760        });
11761
11762        let height = bp_prompt.update(cx, |this, cx| {
11763            this.prompt
11764                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11765        });
11766        let cloned_prompt = bp_prompt.clone();
11767        let blocks = vec![BlockProperties {
11768            style: BlockStyle::Sticky,
11769            placement: BlockPlacement::Above(anchor),
11770            height: Some(height),
11771            render: Arc::new(move |cx| {
11772                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11773                cloned_prompt.clone().into_any_element()
11774            }),
11775            priority: 0,
11776        }];
11777
11778        let focus_handle = bp_prompt.focus_handle(cx);
11779        window.focus(&focus_handle, cx);
11780
11781        let block_ids = self.insert_blocks(blocks, None, cx);
11782        bp_prompt.update(cx, |prompt, _| {
11783            prompt.add_block_ids(block_ids);
11784        });
11785    }
11786
11787    pub(crate) fn breakpoint_at_row(
11788        &self,
11789        row: u32,
11790        window: &mut Window,
11791        cx: &mut Context<Self>,
11792    ) -> Option<(Anchor, Breakpoint)> {
11793        let snapshot = self.snapshot(window, cx);
11794        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11795
11796        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11797    }
11798
11799    pub(crate) fn breakpoint_at_anchor(
11800        &self,
11801        breakpoint_position: Anchor,
11802        snapshot: &EditorSnapshot,
11803        cx: &mut Context<Self>,
11804    ) -> Option<(Anchor, Breakpoint)> {
11805        let buffer = self
11806            .buffer
11807            .read(cx)
11808            .buffer_for_anchor(breakpoint_position, cx)?;
11809
11810        let enclosing_excerpt = breakpoint_position.excerpt_id;
11811        let buffer_snapshot = buffer.read(cx).snapshot();
11812
11813        let row = buffer_snapshot
11814            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11815            .row;
11816
11817        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11818        let anchor_end = snapshot
11819            .buffer_snapshot()
11820            .anchor_after(Point::new(row, line_len));
11821
11822        self.breakpoint_store
11823            .as_ref()?
11824            .read_with(cx, |breakpoint_store, cx| {
11825                breakpoint_store
11826                    .breakpoints(
11827                        &buffer,
11828                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11829                        &buffer_snapshot,
11830                        cx,
11831                    )
11832                    .next()
11833                    .and_then(|(bp, _)| {
11834                        let breakpoint_row = buffer_snapshot
11835                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11836                            .row;
11837
11838                        if breakpoint_row == row {
11839                            snapshot
11840                                .buffer_snapshot()
11841                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11842                                .map(|position| (position, bp.bp.clone()))
11843                        } else {
11844                            None
11845                        }
11846                    })
11847            })
11848    }
11849
11850    pub fn edit_log_breakpoint(
11851        &mut self,
11852        _: &EditLogBreakpoint,
11853        window: &mut Window,
11854        cx: &mut Context<Self>,
11855    ) {
11856        if self.breakpoint_store.is_none() {
11857            return;
11858        }
11859
11860        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11861            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11862                message: None,
11863                state: BreakpointState::Enabled,
11864                condition: None,
11865                hit_condition: None,
11866            });
11867
11868            self.add_edit_breakpoint_block(
11869                anchor,
11870                &breakpoint,
11871                BreakpointPromptEditAction::Log,
11872                window,
11873                cx,
11874            );
11875        }
11876    }
11877
11878    fn breakpoints_at_cursors(
11879        &self,
11880        window: &mut Window,
11881        cx: &mut Context<Self>,
11882    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11883        let snapshot = self.snapshot(window, cx);
11884        let cursors = self
11885            .selections
11886            .disjoint_anchors_arc()
11887            .iter()
11888            .map(|selection| {
11889                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11890
11891                let breakpoint_position = self
11892                    .breakpoint_at_row(cursor_position.row, window, cx)
11893                    .map(|bp| bp.0)
11894                    .unwrap_or_else(|| {
11895                        snapshot
11896                            .display_snapshot
11897                            .buffer_snapshot()
11898                            .anchor_after(Point::new(cursor_position.row, 0))
11899                    });
11900
11901                let breakpoint = self
11902                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11903                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11904
11905                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11906            })
11907            // 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.
11908            .collect::<HashMap<Anchor, _>>();
11909
11910        cursors.into_iter().collect()
11911    }
11912
11913    pub fn enable_breakpoint(
11914        &mut self,
11915        _: &crate::actions::EnableBreakpoint,
11916        window: &mut Window,
11917        cx: &mut Context<Self>,
11918    ) {
11919        if self.breakpoint_store.is_none() {
11920            return;
11921        }
11922
11923        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11924            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11925                continue;
11926            };
11927            self.edit_breakpoint_at_anchor(
11928                anchor,
11929                breakpoint,
11930                BreakpointEditAction::InvertState,
11931                cx,
11932            );
11933        }
11934    }
11935
11936    pub fn disable_breakpoint(
11937        &mut self,
11938        _: &crate::actions::DisableBreakpoint,
11939        window: &mut Window,
11940        cx: &mut Context<Self>,
11941    ) {
11942        if self.breakpoint_store.is_none() {
11943            return;
11944        }
11945
11946        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11947            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11948                continue;
11949            };
11950            self.edit_breakpoint_at_anchor(
11951                anchor,
11952                breakpoint,
11953                BreakpointEditAction::InvertState,
11954                cx,
11955            );
11956        }
11957    }
11958
11959    pub fn toggle_breakpoint(
11960        &mut self,
11961        _: &crate::actions::ToggleBreakpoint,
11962        window: &mut Window,
11963        cx: &mut Context<Self>,
11964    ) {
11965        if self.breakpoint_store.is_none() {
11966            return;
11967        }
11968
11969        let snapshot = self.snapshot(window, cx);
11970        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11971            if self.gutter_breakpoint_indicator.0.is_some() {
11972                let display_row = anchor
11973                    .to_point(snapshot.buffer_snapshot())
11974                    .to_display_point(&snapshot.display_snapshot)
11975                    .row();
11976                self.update_breakpoint_collision_on_toggle(
11977                    display_row,
11978                    &BreakpointEditAction::Toggle,
11979                );
11980            }
11981
11982            if let Some(breakpoint) = breakpoint {
11983                self.edit_breakpoint_at_anchor(
11984                    anchor,
11985                    breakpoint,
11986                    BreakpointEditAction::Toggle,
11987                    cx,
11988                );
11989            } else {
11990                self.edit_breakpoint_at_anchor(
11991                    anchor,
11992                    Breakpoint::new_standard(),
11993                    BreakpointEditAction::Toggle,
11994                    cx,
11995                );
11996            }
11997        }
11998    }
11999
12000    fn update_breakpoint_collision_on_toggle(
12001        &mut self,
12002        display_row: DisplayRow,
12003        edit_action: &BreakpointEditAction,
12004    ) {
12005        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12006            if breakpoint_indicator.display_row == display_row
12007                && matches!(edit_action, BreakpointEditAction::Toggle)
12008            {
12009                breakpoint_indicator.collides_with_existing_breakpoint =
12010                    !breakpoint_indicator.collides_with_existing_breakpoint;
12011            }
12012        }
12013    }
12014
12015    pub fn edit_breakpoint_at_anchor(
12016        &mut self,
12017        breakpoint_position: Anchor,
12018        breakpoint: Breakpoint,
12019        edit_action: BreakpointEditAction,
12020        cx: &mut Context<Self>,
12021    ) {
12022        let Some(breakpoint_store) = &self.breakpoint_store else {
12023            return;
12024        };
12025
12026        let Some(buffer) = self
12027            .buffer
12028            .read(cx)
12029            .buffer_for_anchor(breakpoint_position, cx)
12030        else {
12031            return;
12032        };
12033
12034        breakpoint_store.update(cx, |breakpoint_store, cx| {
12035            breakpoint_store.toggle_breakpoint(
12036                buffer,
12037                BreakpointWithPosition {
12038                    position: breakpoint_position.text_anchor,
12039                    bp: breakpoint,
12040                },
12041                edit_action,
12042                cx,
12043            );
12044        });
12045
12046        cx.notify();
12047    }
12048
12049    #[cfg(any(test, feature = "test-support"))]
12050    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12051        self.breakpoint_store.clone()
12052    }
12053
12054    pub fn prepare_restore_change(
12055        &self,
12056        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12057        hunk: &MultiBufferDiffHunk,
12058        cx: &mut App,
12059    ) -> Option<()> {
12060        if hunk.is_created_file() {
12061            return None;
12062        }
12063        let buffer = self.buffer.read(cx);
12064        let diff = buffer.diff_for(hunk.buffer_id)?;
12065        let buffer = buffer.buffer(hunk.buffer_id)?;
12066        let buffer = buffer.read(cx);
12067        let original_text = diff
12068            .read(cx)
12069            .base_text(cx)
12070            .as_rope()
12071            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12072        let buffer_snapshot = buffer.snapshot();
12073        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12074        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12075            probe
12076                .0
12077                .start
12078                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12079                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12080        }) {
12081            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12082            Some(())
12083        } else {
12084            None
12085        }
12086    }
12087
12088    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12089        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12090    }
12091
12092    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12093        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12094    }
12095
12096    pub fn rotate_selections_forward(
12097        &mut self,
12098        _: &RotateSelectionsForward,
12099        window: &mut Window,
12100        cx: &mut Context<Self>,
12101    ) {
12102        self.rotate_selections(window, cx, false)
12103    }
12104
12105    pub fn rotate_selections_backward(
12106        &mut self,
12107        _: &RotateSelectionsBackward,
12108        window: &mut Window,
12109        cx: &mut Context<Self>,
12110    ) {
12111        self.rotate_selections(window, cx, true)
12112    }
12113
12114    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12115        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12116        let display_snapshot = self.display_snapshot(cx);
12117        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12118
12119        if selections.len() < 2 {
12120            return;
12121        }
12122
12123        let (edits, new_selections) = {
12124            let buffer = self.buffer.read(cx).read(cx);
12125            let has_selections = selections.iter().any(|s| !s.is_empty());
12126            if has_selections {
12127                let mut selected_texts: Vec<String> = selections
12128                    .iter()
12129                    .map(|selection| {
12130                        buffer
12131                            .text_for_range(selection.start..selection.end)
12132                            .collect()
12133                    })
12134                    .collect();
12135
12136                if reverse {
12137                    selected_texts.rotate_left(1);
12138                } else {
12139                    selected_texts.rotate_right(1);
12140                }
12141
12142                let mut offset_delta: i64 = 0;
12143                let mut new_selections = Vec::new();
12144                let edits: Vec<_> = selections
12145                    .iter()
12146                    .zip(selected_texts.iter())
12147                    .map(|(selection, new_text)| {
12148                        let old_len = (selection.end.0 - selection.start.0) as i64;
12149                        let new_len = new_text.len() as i64;
12150                        let adjusted_start =
12151                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12152                        let adjusted_end =
12153                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12154
12155                        new_selections.push(Selection {
12156                            id: selection.id,
12157                            start: adjusted_start,
12158                            end: adjusted_end,
12159                            reversed: selection.reversed,
12160                            goal: selection.goal,
12161                        });
12162
12163                        offset_delta += new_len - old_len;
12164                        (selection.start..selection.end, new_text.clone())
12165                    })
12166                    .collect();
12167                (edits, new_selections)
12168            } else {
12169                let mut all_rows: Vec<u32> = selections
12170                    .iter()
12171                    .map(|selection| buffer.offset_to_point(selection.start).row)
12172                    .collect();
12173                all_rows.sort_unstable();
12174                all_rows.dedup();
12175
12176                if all_rows.len() < 2 {
12177                    return;
12178                }
12179
12180                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12181                    .iter()
12182                    .map(|&row| {
12183                        let start = Point::new(row, 0);
12184                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12185                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12186                    })
12187                    .collect();
12188
12189                let mut line_texts: Vec<String> = line_ranges
12190                    .iter()
12191                    .map(|range| buffer.text_for_range(range.clone()).collect())
12192                    .collect();
12193
12194                if reverse {
12195                    line_texts.rotate_left(1);
12196                } else {
12197                    line_texts.rotate_right(1);
12198                }
12199
12200                let edits = line_ranges
12201                    .iter()
12202                    .zip(line_texts.iter())
12203                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12204                    .collect();
12205
12206                let num_rows = all_rows.len();
12207                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12208                    .iter()
12209                    .enumerate()
12210                    .map(|(i, &row)| (row, i))
12211                    .collect();
12212
12213                // Compute new line start offsets after rotation (handles CRLF)
12214                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12215                let first_line_start = line_ranges[0].start.0;
12216                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12217                for text in line_texts.iter().take(num_rows - 1) {
12218                    let prev_start = *new_line_starts.last().unwrap();
12219                    new_line_starts.push(prev_start + text.len() + newline_len);
12220                }
12221
12222                let new_selections = selections
12223                    .iter()
12224                    .map(|selection| {
12225                        let point = buffer.offset_to_point(selection.start);
12226                        let old_index = row_to_index[&point.row];
12227                        let new_index = if reverse {
12228                            (old_index + num_rows - 1) % num_rows
12229                        } else {
12230                            (old_index + 1) % num_rows
12231                        };
12232                        let new_offset =
12233                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12234                        Selection {
12235                            id: selection.id,
12236                            start: new_offset,
12237                            end: new_offset,
12238                            reversed: selection.reversed,
12239                            goal: selection.goal,
12240                        }
12241                    })
12242                    .collect();
12243
12244                (edits, new_selections)
12245            }
12246        };
12247
12248        self.transact(window, cx, |this, window, cx| {
12249            this.buffer.update(cx, |buffer, cx| {
12250                buffer.edit(edits, None, cx);
12251            });
12252            this.change_selections(Default::default(), window, cx, |s| {
12253                s.select(new_selections);
12254            });
12255        });
12256    }
12257
12258    fn manipulate_lines<M>(
12259        &mut self,
12260        window: &mut Window,
12261        cx: &mut Context<Self>,
12262        mut manipulate: M,
12263    ) where
12264        M: FnMut(&str) -> LineManipulationResult,
12265    {
12266        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12267
12268        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12269        let buffer = self.buffer.read(cx).snapshot(cx);
12270
12271        let mut edits = Vec::new();
12272
12273        let selections = self.selections.all::<Point>(&display_map);
12274        let mut selections = selections.iter().peekable();
12275        let mut contiguous_row_selections = Vec::new();
12276        let mut new_selections = Vec::new();
12277        let mut added_lines = 0;
12278        let mut removed_lines = 0;
12279
12280        while let Some(selection) = selections.next() {
12281            let (start_row, end_row) = consume_contiguous_rows(
12282                &mut contiguous_row_selections,
12283                selection,
12284                &display_map,
12285                &mut selections,
12286            );
12287
12288            let start_point = Point::new(start_row.0, 0);
12289            let end_point = Point::new(
12290                end_row.previous_row().0,
12291                buffer.line_len(end_row.previous_row()),
12292            );
12293            let text = buffer
12294                .text_for_range(start_point..end_point)
12295                .collect::<String>();
12296
12297            let LineManipulationResult {
12298                new_text,
12299                line_count_before,
12300                line_count_after,
12301            } = manipulate(&text);
12302
12303            edits.push((start_point..end_point, new_text));
12304
12305            // Selections must change based on added and removed line count
12306            let start_row =
12307                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12308            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12309            new_selections.push(Selection {
12310                id: selection.id,
12311                start: start_row,
12312                end: end_row,
12313                goal: SelectionGoal::None,
12314                reversed: selection.reversed,
12315            });
12316
12317            if line_count_after > line_count_before {
12318                added_lines += line_count_after - line_count_before;
12319            } else if line_count_before > line_count_after {
12320                removed_lines += line_count_before - line_count_after;
12321            }
12322        }
12323
12324        self.transact(window, cx, |this, window, cx| {
12325            let buffer = this.buffer.update(cx, |buffer, cx| {
12326                buffer.edit(edits, None, cx);
12327                buffer.snapshot(cx)
12328            });
12329
12330            // Recalculate offsets on newly edited buffer
12331            let new_selections = new_selections
12332                .iter()
12333                .map(|s| {
12334                    let start_point = Point::new(s.start.0, 0);
12335                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12336                    Selection {
12337                        id: s.id,
12338                        start: buffer.point_to_offset(start_point),
12339                        end: buffer.point_to_offset(end_point),
12340                        goal: s.goal,
12341                        reversed: s.reversed,
12342                    }
12343                })
12344                .collect();
12345
12346            this.change_selections(Default::default(), window, cx, |s| {
12347                s.select(new_selections);
12348            });
12349
12350            this.request_autoscroll(Autoscroll::fit(), cx);
12351        });
12352    }
12353
12354    fn manipulate_immutable_lines<Fn>(
12355        &mut self,
12356        window: &mut Window,
12357        cx: &mut Context<Self>,
12358        mut callback: Fn,
12359    ) where
12360        Fn: FnMut(&mut Vec<&str>),
12361    {
12362        self.manipulate_lines(window, cx, |text| {
12363            let mut lines: Vec<&str> = text.split('\n').collect();
12364            let line_count_before = lines.len();
12365
12366            callback(&mut lines);
12367
12368            LineManipulationResult {
12369                new_text: lines.join("\n"),
12370                line_count_before,
12371                line_count_after: lines.len(),
12372            }
12373        });
12374    }
12375
12376    fn manipulate_mutable_lines<Fn>(
12377        &mut self,
12378        window: &mut Window,
12379        cx: &mut Context<Self>,
12380        mut callback: Fn,
12381    ) where
12382        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12383    {
12384        self.manipulate_lines(window, cx, |text| {
12385            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12386            let line_count_before = lines.len();
12387
12388            callback(&mut lines);
12389
12390            LineManipulationResult {
12391                new_text: lines.join("\n"),
12392                line_count_before,
12393                line_count_after: lines.len(),
12394            }
12395        });
12396    }
12397
12398    pub fn convert_indentation_to_spaces(
12399        &mut self,
12400        _: &ConvertIndentationToSpaces,
12401        window: &mut Window,
12402        cx: &mut Context<Self>,
12403    ) {
12404        let settings = self.buffer.read(cx).language_settings(cx);
12405        let tab_size = settings.tab_size.get() as usize;
12406
12407        self.manipulate_mutable_lines(window, cx, |lines| {
12408            // Allocates a reasonably sized scratch buffer once for the whole loop
12409            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12410            // Avoids recomputing spaces that could be inserted many times
12411            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12412                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12413                .collect();
12414
12415            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12416                let mut chars = line.as_ref().chars();
12417                let mut col = 0;
12418                let mut changed = false;
12419
12420                for ch in chars.by_ref() {
12421                    match ch {
12422                        ' ' => {
12423                            reindented_line.push(' ');
12424                            col += 1;
12425                        }
12426                        '\t' => {
12427                            // \t are converted to spaces depending on the current column
12428                            let spaces_len = tab_size - (col % tab_size);
12429                            reindented_line.extend(&space_cache[spaces_len - 1]);
12430                            col += spaces_len;
12431                            changed = true;
12432                        }
12433                        _ => {
12434                            // If we dont append before break, the character is consumed
12435                            reindented_line.push(ch);
12436                            break;
12437                        }
12438                    }
12439                }
12440
12441                if !changed {
12442                    reindented_line.clear();
12443                    continue;
12444                }
12445                // Append the rest of the line and replace old reference with new one
12446                reindented_line.extend(chars);
12447                *line = Cow::Owned(reindented_line.clone());
12448                reindented_line.clear();
12449            }
12450        });
12451    }
12452
12453    pub fn convert_indentation_to_tabs(
12454        &mut self,
12455        _: &ConvertIndentationToTabs,
12456        window: &mut Window,
12457        cx: &mut Context<Self>,
12458    ) {
12459        let settings = self.buffer.read(cx).language_settings(cx);
12460        let tab_size = settings.tab_size.get() as usize;
12461
12462        self.manipulate_mutable_lines(window, cx, |lines| {
12463            // Allocates a reasonably sized buffer once for the whole loop
12464            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12465            // Avoids recomputing spaces that could be inserted many times
12466            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12467                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12468                .collect();
12469
12470            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12471                let mut chars = line.chars();
12472                let mut spaces_count = 0;
12473                let mut first_non_indent_char = None;
12474                let mut changed = false;
12475
12476                for ch in chars.by_ref() {
12477                    match ch {
12478                        ' ' => {
12479                            // Keep track of spaces. Append \t when we reach tab_size
12480                            spaces_count += 1;
12481                            changed = true;
12482                            if spaces_count == tab_size {
12483                                reindented_line.push('\t');
12484                                spaces_count = 0;
12485                            }
12486                        }
12487                        '\t' => {
12488                            reindented_line.push('\t');
12489                            spaces_count = 0;
12490                        }
12491                        _ => {
12492                            // Dont append it yet, we might have remaining spaces
12493                            first_non_indent_char = Some(ch);
12494                            break;
12495                        }
12496                    }
12497                }
12498
12499                if !changed {
12500                    reindented_line.clear();
12501                    continue;
12502                }
12503                // Remaining spaces that didn't make a full tab stop
12504                if spaces_count > 0 {
12505                    reindented_line.extend(&space_cache[spaces_count - 1]);
12506                }
12507                // If we consume an extra character that was not indentation, add it back
12508                if let Some(extra_char) = first_non_indent_char {
12509                    reindented_line.push(extra_char);
12510                }
12511                // Append the rest of the line and replace old reference with new one
12512                reindented_line.extend(chars);
12513                *line = Cow::Owned(reindented_line.clone());
12514                reindented_line.clear();
12515            }
12516        });
12517    }
12518
12519    pub fn convert_to_upper_case(
12520        &mut self,
12521        _: &ConvertToUpperCase,
12522        window: &mut Window,
12523        cx: &mut Context<Self>,
12524    ) {
12525        self.manipulate_text(window, cx, |text| text.to_uppercase())
12526    }
12527
12528    pub fn convert_to_lower_case(
12529        &mut self,
12530        _: &ConvertToLowerCase,
12531        window: &mut Window,
12532        cx: &mut Context<Self>,
12533    ) {
12534        self.manipulate_text(window, cx, |text| text.to_lowercase())
12535    }
12536
12537    pub fn convert_to_title_case(
12538        &mut self,
12539        _: &ConvertToTitleCase,
12540        window: &mut Window,
12541        cx: &mut Context<Self>,
12542    ) {
12543        self.manipulate_text(window, cx, |text| {
12544            text.split('\n')
12545                .map(|line| line.to_case(Case::Title))
12546                .join("\n")
12547        })
12548    }
12549
12550    pub fn convert_to_snake_case(
12551        &mut self,
12552        _: &ConvertToSnakeCase,
12553        window: &mut Window,
12554        cx: &mut Context<Self>,
12555    ) {
12556        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12557    }
12558
12559    pub fn convert_to_kebab_case(
12560        &mut self,
12561        _: &ConvertToKebabCase,
12562        window: &mut Window,
12563        cx: &mut Context<Self>,
12564    ) {
12565        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12566    }
12567
12568    pub fn convert_to_upper_camel_case(
12569        &mut self,
12570        _: &ConvertToUpperCamelCase,
12571        window: &mut Window,
12572        cx: &mut Context<Self>,
12573    ) {
12574        self.manipulate_text(window, cx, |text| {
12575            text.split('\n')
12576                .map(|line| line.to_case(Case::UpperCamel))
12577                .join("\n")
12578        })
12579    }
12580
12581    pub fn convert_to_lower_camel_case(
12582        &mut self,
12583        _: &ConvertToLowerCamelCase,
12584        window: &mut Window,
12585        cx: &mut Context<Self>,
12586    ) {
12587        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12588    }
12589
12590    pub fn convert_to_opposite_case(
12591        &mut self,
12592        _: &ConvertToOppositeCase,
12593        window: &mut Window,
12594        cx: &mut Context<Self>,
12595    ) {
12596        self.manipulate_text(window, cx, |text| {
12597            text.chars()
12598                .fold(String::with_capacity(text.len()), |mut t, c| {
12599                    if c.is_uppercase() {
12600                        t.extend(c.to_lowercase());
12601                    } else {
12602                        t.extend(c.to_uppercase());
12603                    }
12604                    t
12605                })
12606        })
12607    }
12608
12609    pub fn convert_to_sentence_case(
12610        &mut self,
12611        _: &ConvertToSentenceCase,
12612        window: &mut Window,
12613        cx: &mut Context<Self>,
12614    ) {
12615        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12616    }
12617
12618    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12619        self.manipulate_text(window, cx, |text| {
12620            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12621            if has_upper_case_characters {
12622                text.to_lowercase()
12623            } else {
12624                text.to_uppercase()
12625            }
12626        })
12627    }
12628
12629    pub fn convert_to_rot13(
12630        &mut self,
12631        _: &ConvertToRot13,
12632        window: &mut Window,
12633        cx: &mut Context<Self>,
12634    ) {
12635        self.manipulate_text(window, cx, |text| {
12636            text.chars()
12637                .map(|c| match c {
12638                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12639                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12640                    _ => c,
12641                })
12642                .collect()
12643        })
12644    }
12645
12646    pub fn convert_to_rot47(
12647        &mut self,
12648        _: &ConvertToRot47,
12649        window: &mut Window,
12650        cx: &mut Context<Self>,
12651    ) {
12652        self.manipulate_text(window, cx, |text| {
12653            text.chars()
12654                .map(|c| {
12655                    let code_point = c as u32;
12656                    if code_point >= 33 && code_point <= 126 {
12657                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12658                    }
12659                    c
12660                })
12661                .collect()
12662        })
12663    }
12664
12665    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12666    where
12667        Fn: FnMut(&str) -> String,
12668    {
12669        let buffer = self.buffer.read(cx).snapshot(cx);
12670
12671        let mut new_selections = Vec::new();
12672        let mut edits = Vec::new();
12673        let mut selection_adjustment = 0isize;
12674
12675        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12676            let selection_is_empty = selection.is_empty();
12677
12678            let (start, end) = if selection_is_empty {
12679                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12680                (word_range.start, word_range.end)
12681            } else {
12682                (
12683                    buffer.point_to_offset(selection.start),
12684                    buffer.point_to_offset(selection.end),
12685                )
12686            };
12687
12688            let text = buffer.text_for_range(start..end).collect::<String>();
12689            let old_length = text.len() as isize;
12690            let text = callback(&text);
12691
12692            new_selections.push(Selection {
12693                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12694                end: MultiBufferOffset(
12695                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12696                ),
12697                goal: SelectionGoal::None,
12698                id: selection.id,
12699                reversed: selection.reversed,
12700            });
12701
12702            selection_adjustment += old_length - text.len() as isize;
12703
12704            edits.push((start..end, text));
12705        }
12706
12707        self.transact(window, cx, |this, window, cx| {
12708            this.buffer.update(cx, |buffer, cx| {
12709                buffer.edit(edits, None, cx);
12710            });
12711
12712            this.change_selections(Default::default(), window, cx, |s| {
12713                s.select(new_selections);
12714            });
12715
12716            this.request_autoscroll(Autoscroll::fit(), cx);
12717        });
12718    }
12719
12720    pub fn move_selection_on_drop(
12721        &mut self,
12722        selection: &Selection<Anchor>,
12723        target: DisplayPoint,
12724        is_cut: bool,
12725        window: &mut Window,
12726        cx: &mut Context<Self>,
12727    ) {
12728        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12729        let buffer = display_map.buffer_snapshot();
12730        let mut edits = Vec::new();
12731        let insert_point = display_map
12732            .clip_point(target, Bias::Left)
12733            .to_point(&display_map);
12734        let text = buffer
12735            .text_for_range(selection.start..selection.end)
12736            .collect::<String>();
12737        if is_cut {
12738            edits.push(((selection.start..selection.end), String::new()));
12739        }
12740        let insert_anchor = buffer.anchor_before(insert_point);
12741        edits.push(((insert_anchor..insert_anchor), text));
12742        let last_edit_start = insert_anchor.bias_left(buffer);
12743        let last_edit_end = insert_anchor.bias_right(buffer);
12744        self.transact(window, cx, |this, window, cx| {
12745            this.buffer.update(cx, |buffer, cx| {
12746                buffer.edit(edits, None, cx);
12747            });
12748            this.change_selections(Default::default(), window, cx, |s| {
12749                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12750            });
12751        });
12752    }
12753
12754    pub fn clear_selection_drag_state(&mut self) {
12755        self.selection_drag_state = SelectionDragState::None;
12756    }
12757
12758    pub fn duplicate(
12759        &mut self,
12760        upwards: bool,
12761        whole_lines: bool,
12762        window: &mut Window,
12763        cx: &mut Context<Self>,
12764    ) {
12765        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12766
12767        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12768        let buffer = display_map.buffer_snapshot();
12769        let selections = self.selections.all::<Point>(&display_map);
12770
12771        let mut edits = Vec::new();
12772        let mut selections_iter = selections.iter().peekable();
12773        while let Some(selection) = selections_iter.next() {
12774            let mut rows = selection.spanned_rows(false, &display_map);
12775            // duplicate line-wise
12776            if whole_lines || selection.start == selection.end {
12777                // Avoid duplicating the same lines twice.
12778                while let Some(next_selection) = selections_iter.peek() {
12779                    let next_rows = next_selection.spanned_rows(false, &display_map);
12780                    if next_rows.start < rows.end {
12781                        rows.end = next_rows.end;
12782                        selections_iter.next().unwrap();
12783                    } else {
12784                        break;
12785                    }
12786                }
12787
12788                // Copy the text from the selected row region and splice it either at the start
12789                // or end of the region.
12790                let start = Point::new(rows.start.0, 0);
12791                let end = Point::new(
12792                    rows.end.previous_row().0,
12793                    buffer.line_len(rows.end.previous_row()),
12794                );
12795
12796                let mut text = buffer.text_for_range(start..end).collect::<String>();
12797
12798                let insert_location = if upwards {
12799                    // When duplicating upward, we need to insert before the current line.
12800                    // If we're on the last line and it doesn't end with a newline,
12801                    // we need to add a newline before the duplicated content.
12802                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12803                        && buffer.max_point().column > 0
12804                        && !text.ends_with('\n');
12805
12806                    if needs_leading_newline {
12807                        text.insert(0, '\n');
12808                        end
12809                    } else {
12810                        text.push('\n');
12811                        Point::new(rows.start.0, 0)
12812                    }
12813                } else {
12814                    text.push('\n');
12815                    start
12816                };
12817                edits.push((insert_location..insert_location, text));
12818            } else {
12819                // duplicate character-wise
12820                let start = selection.start;
12821                let end = selection.end;
12822                let text = buffer.text_for_range(start..end).collect::<String>();
12823                edits.push((selection.end..selection.end, text));
12824            }
12825        }
12826
12827        self.transact(window, cx, |this, window, cx| {
12828            this.buffer.update(cx, |buffer, cx| {
12829                buffer.edit(edits, None, cx);
12830            });
12831
12832            // When duplicating upward with whole lines, move the cursor to the duplicated line
12833            if upwards && whole_lines {
12834                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12835
12836                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12837                    let mut new_ranges = Vec::new();
12838                    let selections = s.all::<Point>(&display_map);
12839                    let mut selections_iter = selections.iter().peekable();
12840
12841                    while let Some(first_selection) = selections_iter.next() {
12842                        // Group contiguous selections together to find the total row span
12843                        let mut group_selections = vec![first_selection];
12844                        let mut rows = first_selection.spanned_rows(false, &display_map);
12845
12846                        while let Some(next_selection) = selections_iter.peek() {
12847                            let next_rows = next_selection.spanned_rows(false, &display_map);
12848                            if next_rows.start < rows.end {
12849                                rows.end = next_rows.end;
12850                                group_selections.push(selections_iter.next().unwrap());
12851                            } else {
12852                                break;
12853                            }
12854                        }
12855
12856                        let row_count = rows.end.0 - rows.start.0;
12857
12858                        // Move all selections in this group up by the total number of duplicated rows
12859                        for selection in group_selections {
12860                            let new_start = Point::new(
12861                                selection.start.row.saturating_sub(row_count),
12862                                selection.start.column,
12863                            );
12864
12865                            let new_end = Point::new(
12866                                selection.end.row.saturating_sub(row_count),
12867                                selection.end.column,
12868                            );
12869
12870                            new_ranges.push(new_start..new_end);
12871                        }
12872                    }
12873
12874                    s.select_ranges(new_ranges);
12875                });
12876            }
12877
12878            this.request_autoscroll(Autoscroll::fit(), cx);
12879        });
12880    }
12881
12882    pub fn duplicate_line_up(
12883        &mut self,
12884        _: &DuplicateLineUp,
12885        window: &mut Window,
12886        cx: &mut Context<Self>,
12887    ) {
12888        self.duplicate(true, true, window, cx);
12889    }
12890
12891    pub fn duplicate_line_down(
12892        &mut self,
12893        _: &DuplicateLineDown,
12894        window: &mut Window,
12895        cx: &mut Context<Self>,
12896    ) {
12897        self.duplicate(false, true, window, cx);
12898    }
12899
12900    pub fn duplicate_selection(
12901        &mut self,
12902        _: &DuplicateSelection,
12903        window: &mut Window,
12904        cx: &mut Context<Self>,
12905    ) {
12906        self.duplicate(false, false, window, cx);
12907    }
12908
12909    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12910        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12911        if self.mode.is_single_line() {
12912            cx.propagate();
12913            return;
12914        }
12915
12916        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12917        let buffer = self.buffer.read(cx).snapshot(cx);
12918
12919        let mut edits = Vec::new();
12920        let mut unfold_ranges = Vec::new();
12921        let mut refold_creases = Vec::new();
12922
12923        let selections = self.selections.all::<Point>(&display_map);
12924        let mut selections = selections.iter().peekable();
12925        let mut contiguous_row_selections = Vec::new();
12926        let mut new_selections = Vec::new();
12927
12928        while let Some(selection) = selections.next() {
12929            // Find all the selections that span a contiguous row range
12930            let (start_row, end_row) = consume_contiguous_rows(
12931                &mut contiguous_row_selections,
12932                selection,
12933                &display_map,
12934                &mut selections,
12935            );
12936
12937            // Move the text spanned by the row range to be before the line preceding the row range
12938            if start_row.0 > 0 {
12939                let range_to_move = Point::new(
12940                    start_row.previous_row().0,
12941                    buffer.line_len(start_row.previous_row()),
12942                )
12943                    ..Point::new(
12944                        end_row.previous_row().0,
12945                        buffer.line_len(end_row.previous_row()),
12946                    );
12947                let insertion_point = display_map
12948                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12949                    .0;
12950
12951                // Don't move lines across excerpts
12952                if buffer
12953                    .excerpt_containing(insertion_point..range_to_move.end)
12954                    .is_some()
12955                {
12956                    let text = buffer
12957                        .text_for_range(range_to_move.clone())
12958                        .flat_map(|s| s.chars())
12959                        .skip(1)
12960                        .chain(['\n'])
12961                        .collect::<String>();
12962
12963                    edits.push((
12964                        buffer.anchor_after(range_to_move.start)
12965                            ..buffer.anchor_before(range_to_move.end),
12966                        String::new(),
12967                    ));
12968                    let insertion_anchor = buffer.anchor_after(insertion_point);
12969                    edits.push((insertion_anchor..insertion_anchor, text));
12970
12971                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12972
12973                    // Move selections up
12974                    new_selections.extend(contiguous_row_selections.drain(..).map(
12975                        |mut selection| {
12976                            selection.start.row -= row_delta;
12977                            selection.end.row -= row_delta;
12978                            selection
12979                        },
12980                    ));
12981
12982                    // Move folds up
12983                    unfold_ranges.push(range_to_move.clone());
12984                    for fold in display_map.folds_in_range(
12985                        buffer.anchor_before(range_to_move.start)
12986                            ..buffer.anchor_after(range_to_move.end),
12987                    ) {
12988                        let mut start = fold.range.start.to_point(&buffer);
12989                        let mut end = fold.range.end.to_point(&buffer);
12990                        start.row -= row_delta;
12991                        end.row -= row_delta;
12992                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12993                    }
12994                }
12995            }
12996
12997            // If we didn't move line(s), preserve the existing selections
12998            new_selections.append(&mut contiguous_row_selections);
12999        }
13000
13001        self.transact(window, cx, |this, window, cx| {
13002            this.unfold_ranges(&unfold_ranges, true, true, cx);
13003            this.buffer.update(cx, |buffer, cx| {
13004                for (range, text) in edits {
13005                    buffer.edit([(range, text)], None, cx);
13006                }
13007            });
13008            this.fold_creases(refold_creases, true, window, cx);
13009            this.change_selections(Default::default(), window, cx, |s| {
13010                s.select(new_selections);
13011            })
13012        });
13013    }
13014
13015    pub fn move_line_down(
13016        &mut self,
13017        _: &MoveLineDown,
13018        window: &mut Window,
13019        cx: &mut Context<Self>,
13020    ) {
13021        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13022        if self.mode.is_single_line() {
13023            cx.propagate();
13024            return;
13025        }
13026
13027        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13028        let buffer = self.buffer.read(cx).snapshot(cx);
13029
13030        let mut edits = Vec::new();
13031        let mut unfold_ranges = Vec::new();
13032        let mut refold_creases = Vec::new();
13033
13034        let selections = self.selections.all::<Point>(&display_map);
13035        let mut selections = selections.iter().peekable();
13036        let mut contiguous_row_selections = Vec::new();
13037        let mut new_selections = Vec::new();
13038
13039        while let Some(selection) = selections.next() {
13040            // Find all the selections that span a contiguous row range
13041            let (start_row, end_row) = consume_contiguous_rows(
13042                &mut contiguous_row_selections,
13043                selection,
13044                &display_map,
13045                &mut selections,
13046            );
13047
13048            // Move the text spanned by the row range to be after the last line of the row range
13049            if end_row.0 <= buffer.max_point().row {
13050                let range_to_move =
13051                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13052                let insertion_point = display_map
13053                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13054                    .0;
13055
13056                // Don't move lines across excerpt boundaries
13057                if buffer
13058                    .excerpt_containing(range_to_move.start..insertion_point)
13059                    .is_some()
13060                {
13061                    let mut text = String::from("\n");
13062                    text.extend(buffer.text_for_range(range_to_move.clone()));
13063                    text.pop(); // Drop trailing newline
13064                    edits.push((
13065                        buffer.anchor_after(range_to_move.start)
13066                            ..buffer.anchor_before(range_to_move.end),
13067                        String::new(),
13068                    ));
13069                    let insertion_anchor = buffer.anchor_after(insertion_point);
13070                    edits.push((insertion_anchor..insertion_anchor, text));
13071
13072                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13073
13074                    // Move selections down
13075                    new_selections.extend(contiguous_row_selections.drain(..).map(
13076                        |mut selection| {
13077                            selection.start.row += row_delta;
13078                            selection.end.row += row_delta;
13079                            selection
13080                        },
13081                    ));
13082
13083                    // Move folds down
13084                    unfold_ranges.push(range_to_move.clone());
13085                    for fold in display_map.folds_in_range(
13086                        buffer.anchor_before(range_to_move.start)
13087                            ..buffer.anchor_after(range_to_move.end),
13088                    ) {
13089                        let mut start = fold.range.start.to_point(&buffer);
13090                        let mut end = fold.range.end.to_point(&buffer);
13091                        start.row += row_delta;
13092                        end.row += row_delta;
13093                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13094                    }
13095                }
13096            }
13097
13098            // If we didn't move line(s), preserve the existing selections
13099            new_selections.append(&mut contiguous_row_selections);
13100        }
13101
13102        self.transact(window, cx, |this, window, cx| {
13103            this.unfold_ranges(&unfold_ranges, true, true, cx);
13104            this.buffer.update(cx, |buffer, cx| {
13105                for (range, text) in edits {
13106                    buffer.edit([(range, text)], None, cx);
13107                }
13108            });
13109            this.fold_creases(refold_creases, true, window, cx);
13110            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13111        });
13112    }
13113
13114    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13115        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13116        let text_layout_details = &self.text_layout_details(window, cx);
13117        self.transact(window, cx, |this, window, cx| {
13118            let edits = this.change_selections(Default::default(), window, cx, |s| {
13119                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13120                s.move_with(&mut |display_map, selection| {
13121                    if !selection.is_empty() {
13122                        return;
13123                    }
13124
13125                    let mut head = selection.head();
13126                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13127                    if head.column() == display_map.line_len(head.row()) {
13128                        transpose_offset = display_map
13129                            .buffer_snapshot()
13130                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13131                    }
13132
13133                    if transpose_offset == MultiBufferOffset(0) {
13134                        return;
13135                    }
13136
13137                    *head.column_mut() += 1;
13138                    head = display_map.clip_point(head, Bias::Right);
13139                    let goal = SelectionGoal::HorizontalPosition(
13140                        display_map
13141                            .x_for_display_point(head, text_layout_details)
13142                            .into(),
13143                    );
13144                    selection.collapse_to(head, goal);
13145
13146                    let transpose_start = display_map
13147                        .buffer_snapshot()
13148                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13149                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13150                        let transpose_end = display_map
13151                            .buffer_snapshot()
13152                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13153                        if let Some(ch) = display_map
13154                            .buffer_snapshot()
13155                            .chars_at(transpose_start)
13156                            .next()
13157                        {
13158                            edits.push((transpose_start..transpose_offset, String::new()));
13159                            edits.push((transpose_end..transpose_end, ch.to_string()));
13160                        }
13161                    }
13162                });
13163                edits
13164            });
13165            this.buffer
13166                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13167            let selections = this
13168                .selections
13169                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13170            this.change_selections(Default::default(), window, cx, |s| {
13171                s.select(selections);
13172            });
13173        });
13174    }
13175
13176    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13177        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13178        if self.mode.is_single_line() {
13179            cx.propagate();
13180            return;
13181        }
13182
13183        self.rewrap_impl(RewrapOptions::default(), cx)
13184    }
13185
13186    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13187        let buffer = self.buffer.read(cx).snapshot(cx);
13188        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13189
13190        #[derive(Clone, Debug, PartialEq)]
13191        enum CommentFormat {
13192            /// single line comment, with prefix for line
13193            Line(String),
13194            /// single line within a block comment, with prefix for line
13195            BlockLine(String),
13196            /// a single line of a block comment that includes the initial delimiter
13197            BlockCommentWithStart(BlockCommentConfig),
13198            /// a single line of a block comment that includes the ending delimiter
13199            BlockCommentWithEnd(BlockCommentConfig),
13200        }
13201
13202        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13203        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13204            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13205                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13206                .peekable();
13207
13208            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13209                row
13210            } else {
13211                return Vec::new();
13212            };
13213
13214            let language_settings = buffer.language_settings_at(selection.head(), cx);
13215            let language_scope = buffer.language_scope_at(selection.head());
13216
13217            let indent_and_prefix_for_row =
13218                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13219                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13220                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13221                        &language_scope
13222                    {
13223                        let indent_end = Point::new(row, indent.len);
13224                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13225                        let line_text_after_indent = buffer
13226                            .text_for_range(indent_end..line_end)
13227                            .collect::<String>();
13228
13229                        let is_within_comment_override = buffer
13230                            .language_scope_at(indent_end)
13231                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13232                        let comment_delimiters = if is_within_comment_override {
13233                            // we are within a comment syntax node, but we don't
13234                            // yet know what kind of comment: block, doc or line
13235                            match (
13236                                language_scope.documentation_comment(),
13237                                language_scope.block_comment(),
13238                            ) {
13239                                (Some(config), _) | (_, Some(config))
13240                                    if buffer.contains_str_at(indent_end, &config.start) =>
13241                                {
13242                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13243                                }
13244                                (Some(config), _) | (_, Some(config))
13245                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13246                                {
13247                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13248                                }
13249                                (Some(config), _) | (_, Some(config))
13250                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13251                                {
13252                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13253                                }
13254                                (_, _) => language_scope
13255                                    .line_comment_prefixes()
13256                                    .iter()
13257                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13258                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13259                            }
13260                        } else {
13261                            // we not in an overridden comment node, but we may
13262                            // be within a non-overridden line comment node
13263                            language_scope
13264                                .line_comment_prefixes()
13265                                .iter()
13266                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13267                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13268                        };
13269
13270                        let rewrap_prefix = language_scope
13271                            .rewrap_prefixes()
13272                            .iter()
13273                            .find_map(|prefix_regex| {
13274                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13275                                    if mat.start() == 0 {
13276                                        Some(mat.as_str().to_string())
13277                                    } else {
13278                                        None
13279                                    }
13280                                })
13281                            })
13282                            .flatten();
13283                        (comment_delimiters, rewrap_prefix)
13284                    } else {
13285                        (None, None)
13286                    };
13287                    (indent, comment_prefix, rewrap_prefix)
13288                };
13289
13290            let mut ranges = Vec::new();
13291            let from_empty_selection = selection.is_empty();
13292
13293            let mut current_range_start = first_row;
13294            let mut prev_row = first_row;
13295            let (
13296                mut current_range_indent,
13297                mut current_range_comment_delimiters,
13298                mut current_range_rewrap_prefix,
13299            ) = indent_and_prefix_for_row(first_row);
13300
13301            for row in non_blank_rows_iter.skip(1) {
13302                let has_paragraph_break = row > prev_row + 1;
13303
13304                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13305                    indent_and_prefix_for_row(row);
13306
13307                let has_indent_change = row_indent != current_range_indent;
13308                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13309
13310                let has_boundary_change = has_comment_change
13311                    || row_rewrap_prefix.is_some()
13312                    || (has_indent_change && current_range_comment_delimiters.is_some());
13313
13314                if has_paragraph_break || has_boundary_change {
13315                    ranges.push((
13316                        language_settings.clone(),
13317                        Point::new(current_range_start, 0)
13318                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13319                        current_range_indent,
13320                        current_range_comment_delimiters.clone(),
13321                        current_range_rewrap_prefix.clone(),
13322                        from_empty_selection,
13323                    ));
13324                    current_range_start = row;
13325                    current_range_indent = row_indent;
13326                    current_range_comment_delimiters = row_comment_delimiters;
13327                    current_range_rewrap_prefix = row_rewrap_prefix;
13328                }
13329                prev_row = row;
13330            }
13331
13332            ranges.push((
13333                language_settings.clone(),
13334                Point::new(current_range_start, 0)
13335                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13336                current_range_indent,
13337                current_range_comment_delimiters,
13338                current_range_rewrap_prefix,
13339                from_empty_selection,
13340            ));
13341
13342            ranges
13343        });
13344
13345        let mut edits = Vec::new();
13346        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13347
13348        for (
13349            language_settings,
13350            wrap_range,
13351            mut indent_size,
13352            comment_prefix,
13353            rewrap_prefix,
13354            from_empty_selection,
13355        ) in wrap_ranges
13356        {
13357            let mut start_row = wrap_range.start.row;
13358            let mut end_row = wrap_range.end.row;
13359
13360            // Skip selections that overlap with a range that has already been rewrapped.
13361            let selection_range = start_row..end_row;
13362            if rewrapped_row_ranges
13363                .iter()
13364                .any(|range| range.overlaps(&selection_range))
13365            {
13366                continue;
13367            }
13368
13369            let tab_size = language_settings.tab_size;
13370
13371            let (line_prefix, inside_comment) = match &comment_prefix {
13372                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13373                    (Some(prefix.as_str()), true)
13374                }
13375                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13376                    (Some(prefix.as_ref()), true)
13377                }
13378                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13379                    start: _,
13380                    end: _,
13381                    prefix,
13382                    tab_size,
13383                })) => {
13384                    indent_size.len += tab_size;
13385                    (Some(prefix.as_ref()), true)
13386                }
13387                None => (None, false),
13388            };
13389            let indent_prefix = indent_size.chars().collect::<String>();
13390            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13391
13392            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13393                RewrapBehavior::InComments => inside_comment,
13394                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13395                RewrapBehavior::Anywhere => true,
13396            };
13397
13398            let should_rewrap = options.override_language_settings
13399                || allow_rewrap_based_on_language
13400                || self.hard_wrap.is_some();
13401            if !should_rewrap {
13402                continue;
13403            }
13404
13405            if from_empty_selection {
13406                'expand_upwards: while start_row > 0 {
13407                    let prev_row = start_row - 1;
13408                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13409                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13410                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13411                    {
13412                        start_row = prev_row;
13413                    } else {
13414                        break 'expand_upwards;
13415                    }
13416                }
13417
13418                'expand_downwards: while end_row < buffer.max_point().row {
13419                    let next_row = end_row + 1;
13420                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13421                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13422                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13423                    {
13424                        end_row = next_row;
13425                    } else {
13426                        break 'expand_downwards;
13427                    }
13428                }
13429            }
13430
13431            let start = Point::new(start_row, 0);
13432            let start_offset = ToOffset::to_offset(&start, &buffer);
13433            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13434            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13435            let mut first_line_delimiter = None;
13436            let mut last_line_delimiter = None;
13437            let Some(lines_without_prefixes) = selection_text
13438                .lines()
13439                .enumerate()
13440                .map(|(ix, line)| {
13441                    let line_trimmed = line.trim_start();
13442                    if rewrap_prefix.is_some() && ix > 0 {
13443                        Ok(line_trimmed)
13444                    } else if let Some(
13445                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13446                            start,
13447                            prefix,
13448                            end,
13449                            tab_size,
13450                        })
13451                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13452                            start,
13453                            prefix,
13454                            end,
13455                            tab_size,
13456                        }),
13457                    ) = &comment_prefix
13458                    {
13459                        let line_trimmed = line_trimmed
13460                            .strip_prefix(start.as_ref())
13461                            .map(|s| {
13462                                let mut indent_size = indent_size;
13463                                indent_size.len -= tab_size;
13464                                let indent_prefix: String = indent_size.chars().collect();
13465                                first_line_delimiter = Some((indent_prefix, start));
13466                                s.trim_start()
13467                            })
13468                            .unwrap_or(line_trimmed);
13469                        let line_trimmed = line_trimmed
13470                            .strip_suffix(end.as_ref())
13471                            .map(|s| {
13472                                last_line_delimiter = Some(end);
13473                                s.trim_end()
13474                            })
13475                            .unwrap_or(line_trimmed);
13476                        let line_trimmed = line_trimmed
13477                            .strip_prefix(prefix.as_ref())
13478                            .unwrap_or(line_trimmed);
13479                        Ok(line_trimmed)
13480                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13481                        line_trimmed.strip_prefix(prefix).with_context(|| {
13482                            format!("line did not start with prefix {prefix:?}: {line:?}")
13483                        })
13484                    } else {
13485                        line_trimmed
13486                            .strip_prefix(&line_prefix.trim_start())
13487                            .with_context(|| {
13488                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13489                            })
13490                    }
13491                })
13492                .collect::<Result<Vec<_>, _>>()
13493                .log_err()
13494            else {
13495                continue;
13496            };
13497
13498            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13499                buffer
13500                    .language_settings_at(Point::new(start_row, 0), cx)
13501                    .preferred_line_length as usize
13502            });
13503
13504            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13505                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13506            } else {
13507                line_prefix.clone()
13508            };
13509
13510            let wrapped_text = {
13511                let mut wrapped_text = wrap_with_prefix(
13512                    line_prefix,
13513                    subsequent_lines_prefix,
13514                    lines_without_prefixes.join("\n"),
13515                    wrap_column,
13516                    tab_size,
13517                    options.preserve_existing_whitespace,
13518                );
13519
13520                if let Some((indent, delimiter)) = first_line_delimiter {
13521                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13522                }
13523                if let Some(last_line) = last_line_delimiter {
13524                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13525                }
13526
13527                wrapped_text
13528            };
13529
13530            // TODO: should always use char-based diff while still supporting cursor behavior that
13531            // matches vim.
13532            let mut diff_options = DiffOptions::default();
13533            if options.override_language_settings {
13534                diff_options.max_word_diff_len = 0;
13535                diff_options.max_word_diff_line_count = 0;
13536            } else {
13537                diff_options.max_word_diff_len = usize::MAX;
13538                diff_options.max_word_diff_line_count = usize::MAX;
13539            }
13540
13541            for (old_range, new_text) in
13542                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13543            {
13544                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13545                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13546                edits.push((edit_start..edit_end, new_text));
13547            }
13548
13549            rewrapped_row_ranges.push(start_row..=end_row);
13550        }
13551
13552        self.buffer
13553            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13554    }
13555
13556    pub fn cut_common(
13557        &mut self,
13558        cut_no_selection_line: bool,
13559        window: &mut Window,
13560        cx: &mut Context<Self>,
13561    ) -> ClipboardItem {
13562        let mut text = String::new();
13563        let buffer = self.buffer.read(cx).snapshot(cx);
13564        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13565        let mut clipboard_selections = Vec::with_capacity(selections.len());
13566        {
13567            let max_point = buffer.max_point();
13568            let mut is_first = true;
13569            let mut prev_selection_was_entire_line = false;
13570            for selection in &mut selections {
13571                let is_entire_line =
13572                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13573                if is_entire_line {
13574                    selection.start = Point::new(selection.start.row, 0);
13575                    if !selection.is_empty() && selection.end.column == 0 {
13576                        selection.end = cmp::min(max_point, selection.end);
13577                    } else {
13578                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13579                    }
13580                    selection.goal = SelectionGoal::None;
13581                }
13582                if is_first {
13583                    is_first = false;
13584                } else if !prev_selection_was_entire_line {
13585                    text += "\n";
13586                }
13587                prev_selection_was_entire_line = is_entire_line;
13588                let mut len = 0;
13589                for chunk in buffer.text_for_range(selection.start..selection.end) {
13590                    text.push_str(chunk);
13591                    len += chunk.len();
13592                }
13593
13594                clipboard_selections.push(ClipboardSelection::for_buffer(
13595                    len,
13596                    is_entire_line,
13597                    selection.range(),
13598                    &buffer,
13599                    self.project.as_ref(),
13600                    cx,
13601                ));
13602            }
13603        }
13604
13605        self.transact(window, cx, |this, window, cx| {
13606            this.change_selections(Default::default(), window, cx, |s| {
13607                s.select(selections);
13608            });
13609            this.insert("", window, cx);
13610        });
13611        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13612    }
13613
13614    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13615        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13616        let item = self.cut_common(true, window, cx);
13617        cx.write_to_clipboard(item);
13618    }
13619
13620    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13621        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13622        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13623            s.move_with(&mut |snapshot, sel| {
13624                if sel.is_empty() {
13625                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13626                }
13627                if sel.is_empty() {
13628                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13629                }
13630            });
13631        });
13632        let item = self.cut_common(false, window, cx);
13633        cx.set_global(KillRing(item))
13634    }
13635
13636    pub fn kill_ring_yank(
13637        &mut self,
13638        _: &KillRingYank,
13639        window: &mut Window,
13640        cx: &mut Context<Self>,
13641    ) {
13642        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13643        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13644            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13645                (kill_ring.text().to_string(), kill_ring.metadata_json())
13646            } else {
13647                return;
13648            }
13649        } else {
13650            return;
13651        };
13652        self.do_paste(&text, metadata, false, window, cx);
13653    }
13654
13655    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13656        self.do_copy(true, cx);
13657    }
13658
13659    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13660        self.do_copy(false, cx);
13661    }
13662
13663    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13664        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13665        let buffer = self.buffer.read(cx).read(cx);
13666        let mut text = String::new();
13667        let mut clipboard_selections = Vec::with_capacity(selections.len());
13668
13669        let max_point = buffer.max_point();
13670        let mut is_first = true;
13671        for selection in &selections {
13672            let mut start = selection.start;
13673            let mut end = selection.end;
13674            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13675            let mut add_trailing_newline = false;
13676            if is_entire_line {
13677                start = Point::new(start.row, 0);
13678                let next_line_start = Point::new(end.row + 1, 0);
13679                if next_line_start <= max_point {
13680                    end = next_line_start;
13681                } else {
13682                    // We're on the last line without a trailing newline.
13683                    // Copy to the end of the line and add a newline afterwards.
13684                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13685                    add_trailing_newline = true;
13686                }
13687            }
13688
13689            let mut trimmed_selections = Vec::new();
13690            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13691                let row = MultiBufferRow(start.row);
13692                let first_indent = buffer.indent_size_for_line(row);
13693                if first_indent.len == 0 || start.column > first_indent.len {
13694                    trimmed_selections.push(start..end);
13695                } else {
13696                    trimmed_selections.push(
13697                        Point::new(row.0, first_indent.len)
13698                            ..Point::new(row.0, buffer.line_len(row)),
13699                    );
13700                    for row in start.row + 1..=end.row {
13701                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13702                        if row == end.row {
13703                            line_len = end.column;
13704                        }
13705                        if line_len == 0 {
13706                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13707                            continue;
13708                        }
13709                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13710                        if row_indent_size.len >= first_indent.len {
13711                            trimmed_selections
13712                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13713                        } else {
13714                            trimmed_selections.clear();
13715                            trimmed_selections.push(start..end);
13716                            break;
13717                        }
13718                    }
13719                }
13720            } else {
13721                trimmed_selections.push(start..end);
13722            }
13723
13724            let is_multiline_trim = trimmed_selections.len() > 1;
13725            let mut selection_len: usize = 0;
13726            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13727
13728            for trimmed_range in trimmed_selections {
13729                if is_first {
13730                    is_first = false;
13731                } else if is_multiline_trim || !prev_selection_was_entire_line {
13732                    text.push('\n');
13733                    if is_multiline_trim {
13734                        selection_len += 1;
13735                    }
13736                }
13737                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13738                    text.push_str(chunk);
13739                    selection_len += chunk.len();
13740                }
13741                if add_trailing_newline {
13742                    text.push('\n');
13743                    selection_len += 1;
13744                }
13745            }
13746
13747            clipboard_selections.push(ClipboardSelection::for_buffer(
13748                selection_len,
13749                is_entire_line,
13750                start..end,
13751                &buffer,
13752                self.project.as_ref(),
13753                cx,
13754            ));
13755        }
13756
13757        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13758            text,
13759            clipboard_selections,
13760        ));
13761    }
13762
13763    pub fn do_paste(
13764        &mut self,
13765        text: &String,
13766        clipboard_selections: Option<Vec<ClipboardSelection>>,
13767        handle_entire_lines: bool,
13768        window: &mut Window,
13769        cx: &mut Context<Self>,
13770    ) {
13771        if self.read_only(cx) {
13772            return;
13773        }
13774
13775        let clipboard_text = Cow::Borrowed(text.as_str());
13776
13777        self.transact(window, cx, |this, window, cx| {
13778            let had_active_edit_prediction = this.has_active_edit_prediction();
13779            let display_map = this.display_snapshot(cx);
13780            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13781            let cursor_offset = this
13782                .selections
13783                .last::<MultiBufferOffset>(&display_map)
13784                .head();
13785
13786            if let Some(mut clipboard_selections) = clipboard_selections {
13787                let all_selections_were_entire_line =
13788                    clipboard_selections.iter().all(|s| s.is_entire_line);
13789                let first_selection_indent_column =
13790                    clipboard_selections.first().map(|s| s.first_line_indent);
13791                if clipboard_selections.len() != old_selections.len() {
13792                    clipboard_selections.drain(..);
13793                }
13794                let mut auto_indent_on_paste = true;
13795
13796                this.buffer.update(cx, |buffer, cx| {
13797                    let snapshot = buffer.read(cx);
13798                    auto_indent_on_paste = snapshot
13799                        .language_settings_at(cursor_offset, cx)
13800                        .auto_indent_on_paste;
13801
13802                    let mut start_offset = 0;
13803                    let mut edits = Vec::new();
13804                    let mut original_indent_columns = Vec::new();
13805                    for (ix, selection) in old_selections.iter().enumerate() {
13806                        let to_insert;
13807                        let entire_line;
13808                        let original_indent_column;
13809                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13810                            let end_offset = start_offset + clipboard_selection.len;
13811                            to_insert = &clipboard_text[start_offset..end_offset];
13812                            entire_line = clipboard_selection.is_entire_line;
13813                            start_offset = if entire_line {
13814                                end_offset
13815                            } else {
13816                                end_offset + 1
13817                            };
13818                            original_indent_column = Some(clipboard_selection.first_line_indent);
13819                        } else {
13820                            to_insert = &*clipboard_text;
13821                            entire_line = all_selections_were_entire_line;
13822                            original_indent_column = first_selection_indent_column
13823                        }
13824
13825                        let (range, to_insert) =
13826                            if selection.is_empty() && handle_entire_lines && entire_line {
13827                                // If the corresponding selection was empty when this slice of the
13828                                // clipboard text was written, then the entire line containing the
13829                                // selection was copied. If this selection is also currently empty,
13830                                // then paste the line before the current line of the buffer.
13831                                let column = selection.start.to_point(&snapshot).column as usize;
13832                                let line_start = selection.start - column;
13833                                (line_start..line_start, Cow::Borrowed(to_insert))
13834                            } else {
13835                                let language = snapshot.language_at(selection.head());
13836                                let range = selection.range();
13837                                if let Some(language) = language
13838                                    && language.name() == "Markdown"
13839                                {
13840                                    edit_for_markdown_paste(
13841                                        &snapshot,
13842                                        range,
13843                                        to_insert,
13844                                        url::Url::parse(to_insert).ok(),
13845                                    )
13846                                } else {
13847                                    (range, Cow::Borrowed(to_insert))
13848                                }
13849                            };
13850
13851                        edits.push((range, to_insert));
13852                        original_indent_columns.push(original_indent_column);
13853                    }
13854                    drop(snapshot);
13855
13856                    buffer.edit(
13857                        edits,
13858                        if auto_indent_on_paste {
13859                            Some(AutoindentMode::Block {
13860                                original_indent_columns,
13861                            })
13862                        } else {
13863                            None
13864                        },
13865                        cx,
13866                    );
13867                });
13868
13869                let selections = this
13870                    .selections
13871                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13872                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13873            } else {
13874                let url = url::Url::parse(&clipboard_text).ok();
13875
13876                let auto_indent_mode = if !clipboard_text.is_empty() {
13877                    Some(AutoindentMode::Block {
13878                        original_indent_columns: Vec::new(),
13879                    })
13880                } else {
13881                    None
13882                };
13883
13884                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13885                    let snapshot = buffer.snapshot(cx);
13886
13887                    let anchors = old_selections
13888                        .iter()
13889                        .map(|s| {
13890                            let anchor = snapshot.anchor_after(s.head());
13891                            s.map(|_| anchor)
13892                        })
13893                        .collect::<Vec<_>>();
13894
13895                    let mut edits = Vec::new();
13896
13897                    // When pasting text without metadata (e.g. copied from an
13898                    // external editor using multiple cursors) and the number of
13899                    // lines matches the number of selections, distribute one
13900                    // line per cursor instead of pasting the whole text at each.
13901                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
13902                    let distribute_lines =
13903                        old_selections.len() > 1 && lines.len() == old_selections.len();
13904
13905                    for (ix, selection) in old_selections.iter().enumerate() {
13906                        let language = snapshot.language_at(selection.head());
13907                        let range = selection.range();
13908
13909                        let text_for_cursor: &str = if distribute_lines {
13910                            lines[ix]
13911                        } else {
13912                            &clipboard_text
13913                        };
13914
13915                        let (edit_range, edit_text) = if let Some(language) = language
13916                            && language.name() == "Markdown"
13917                        {
13918                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
13919                        } else {
13920                            (range, Cow::Borrowed(text_for_cursor))
13921                        };
13922
13923                        edits.push((edit_range, edit_text));
13924                    }
13925
13926                    drop(snapshot);
13927                    buffer.edit(edits, auto_indent_mode, cx);
13928
13929                    anchors
13930                });
13931
13932                this.change_selections(Default::default(), window, cx, |s| {
13933                    s.select_anchors(selection_anchors);
13934                });
13935            }
13936
13937            //   🤔                 |    ..     | show_in_menu |
13938            // | ..                  |   true        true
13939            // | had_edit_prediction |   false       true
13940
13941            let trigger_in_words =
13942                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13943
13944            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13945        });
13946    }
13947
13948    pub fn diff_clipboard_with_selection(
13949        &mut self,
13950        _: &DiffClipboardWithSelection,
13951        window: &mut Window,
13952        cx: &mut Context<Self>,
13953    ) {
13954        let selections = self
13955            .selections
13956            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13957
13958        if selections.is_empty() {
13959            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13960            return;
13961        };
13962
13963        let clipboard_text = match cx.read_from_clipboard() {
13964            Some(item) => match item.entries().first() {
13965                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13966                _ => None,
13967            },
13968            None => None,
13969        };
13970
13971        let Some(clipboard_text) = clipboard_text else {
13972            log::warn!("Clipboard doesn't contain text.");
13973            return;
13974        };
13975
13976        window.dispatch_action(
13977            Box::new(DiffClipboardWithSelectionData {
13978                clipboard_text,
13979                editor: cx.entity(),
13980            }),
13981            cx,
13982        );
13983    }
13984
13985    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13986        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13987        if let Some(item) = cx.read_from_clipboard() {
13988            let entries = item.entries();
13989
13990            match entries.first() {
13991                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13992                // of all the pasted entries.
13993                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13994                    .do_paste(
13995                        clipboard_string.text(),
13996                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13997                        true,
13998                        window,
13999                        cx,
14000                    ),
14001                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14002            }
14003        }
14004    }
14005
14006    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14007        if self.read_only(cx) {
14008            return;
14009        }
14010
14011        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14012
14013        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14014            if let Some((selections, _)) =
14015                self.selection_history.transaction(transaction_id).cloned()
14016            {
14017                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14018                    s.select_anchors(selections.to_vec());
14019                });
14020            } else {
14021                log::error!(
14022                    "No entry in selection_history found for undo. \
14023                     This may correspond to a bug where undo does not update the selection. \
14024                     If this is occurring, please add details to \
14025                     https://github.com/zed-industries/zed/issues/22692"
14026                );
14027            }
14028            self.request_autoscroll(Autoscroll::fit(), cx);
14029            self.unmark_text(window, cx);
14030            self.refresh_edit_prediction(true, false, window, cx);
14031            cx.emit(EditorEvent::Edited { transaction_id });
14032            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14033        }
14034    }
14035
14036    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14037        if self.read_only(cx) {
14038            return;
14039        }
14040
14041        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14042
14043        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14044            if let Some((_, Some(selections))) =
14045                self.selection_history.transaction(transaction_id).cloned()
14046            {
14047                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14048                    s.select_anchors(selections.to_vec());
14049                });
14050            } else {
14051                log::error!(
14052                    "No entry in selection_history found for redo. \
14053                     This may correspond to a bug where undo does not update the selection. \
14054                     If this is occurring, please add details to \
14055                     https://github.com/zed-industries/zed/issues/22692"
14056                );
14057            }
14058            self.request_autoscroll(Autoscroll::fit(), cx);
14059            self.unmark_text(window, cx);
14060            self.refresh_edit_prediction(true, false, window, cx);
14061            cx.emit(EditorEvent::Edited { transaction_id });
14062        }
14063    }
14064
14065    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14066        self.buffer
14067            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14068    }
14069
14070    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14071        self.buffer
14072            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14073    }
14074
14075    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14076        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14077        self.change_selections(Default::default(), window, cx, |s| {
14078            s.move_with(&mut |map, selection| {
14079                let cursor = if selection.is_empty() {
14080                    movement::left(map, selection.start)
14081                } else {
14082                    selection.start
14083                };
14084                selection.collapse_to(cursor, SelectionGoal::None);
14085            });
14086        })
14087    }
14088
14089    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14090        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14091        self.change_selections(Default::default(), window, cx, |s| {
14092            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14093        })
14094    }
14095
14096    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14097        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14098        self.change_selections(Default::default(), window, cx, |s| {
14099            s.move_with(&mut |map, selection| {
14100                let cursor = if selection.is_empty() {
14101                    movement::right(map, selection.end)
14102                } else {
14103                    selection.end
14104                };
14105                selection.collapse_to(cursor, SelectionGoal::None)
14106            });
14107        })
14108    }
14109
14110    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14111        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14112        self.change_selections(Default::default(), window, cx, |s| {
14113            s.move_heads_with(&mut |map, head, _| {
14114                (movement::right(map, head), SelectionGoal::None)
14115            });
14116        });
14117    }
14118
14119    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14120        if self.take_rename(true, window, cx).is_some() {
14121            return;
14122        }
14123
14124        if self.mode.is_single_line() {
14125            cx.propagate();
14126            return;
14127        }
14128
14129        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14130
14131        let text_layout_details = &self.text_layout_details(window, cx);
14132        let selection_count = self.selections.count();
14133        let first_selection = self.selections.first_anchor();
14134
14135        self.change_selections(Default::default(), window, cx, |s| {
14136            s.move_with(&mut |map, selection| {
14137                if !selection.is_empty() {
14138                    selection.goal = SelectionGoal::None;
14139                }
14140                let (cursor, goal) = movement::up(
14141                    map,
14142                    selection.start,
14143                    selection.goal,
14144                    false,
14145                    text_layout_details,
14146                );
14147                selection.collapse_to(cursor, goal);
14148            });
14149        });
14150
14151        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14152        {
14153            cx.propagate();
14154        }
14155    }
14156
14157    pub fn move_up_by_lines(
14158        &mut self,
14159        action: &MoveUpByLines,
14160        window: &mut Window,
14161        cx: &mut Context<Self>,
14162    ) {
14163        if self.take_rename(true, window, cx).is_some() {
14164            return;
14165        }
14166
14167        if self.mode.is_single_line() {
14168            cx.propagate();
14169            return;
14170        }
14171
14172        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14173
14174        let text_layout_details = &self.text_layout_details(window, cx);
14175
14176        self.change_selections(Default::default(), window, cx, |s| {
14177            s.move_with(&mut |map, selection| {
14178                if !selection.is_empty() {
14179                    selection.goal = SelectionGoal::None;
14180                }
14181                let (cursor, goal) = movement::up_by_rows(
14182                    map,
14183                    selection.start,
14184                    action.lines,
14185                    selection.goal,
14186                    false,
14187                    text_layout_details,
14188                );
14189                selection.collapse_to(cursor, goal);
14190            });
14191        })
14192    }
14193
14194    pub fn move_down_by_lines(
14195        &mut self,
14196        action: &MoveDownByLines,
14197        window: &mut Window,
14198        cx: &mut Context<Self>,
14199    ) {
14200        if self.take_rename(true, window, cx).is_some() {
14201            return;
14202        }
14203
14204        if self.mode.is_single_line() {
14205            cx.propagate();
14206            return;
14207        }
14208
14209        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14210
14211        let text_layout_details = &self.text_layout_details(window, cx);
14212
14213        self.change_selections(Default::default(), window, cx, |s| {
14214            s.move_with(&mut |map, selection| {
14215                if !selection.is_empty() {
14216                    selection.goal = SelectionGoal::None;
14217                }
14218                let (cursor, goal) = movement::down_by_rows(
14219                    map,
14220                    selection.start,
14221                    action.lines,
14222                    selection.goal,
14223                    false,
14224                    text_layout_details,
14225                );
14226                selection.collapse_to(cursor, goal);
14227            });
14228        })
14229    }
14230
14231    pub fn select_down_by_lines(
14232        &mut self,
14233        action: &SelectDownByLines,
14234        window: &mut Window,
14235        cx: &mut Context<Self>,
14236    ) {
14237        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14238        let text_layout_details = &self.text_layout_details(window, cx);
14239        self.change_selections(Default::default(), window, cx, |s| {
14240            s.move_heads_with(&mut |map, head, goal| {
14241                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14242            })
14243        })
14244    }
14245
14246    pub fn select_up_by_lines(
14247        &mut self,
14248        action: &SelectUpByLines,
14249        window: &mut Window,
14250        cx: &mut Context<Self>,
14251    ) {
14252        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14253        let text_layout_details = &self.text_layout_details(window, cx);
14254        self.change_selections(Default::default(), window, cx, |s| {
14255            s.move_heads_with(&mut |map, head, goal| {
14256                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14257            })
14258        })
14259    }
14260
14261    pub fn select_page_up(
14262        &mut self,
14263        _: &SelectPageUp,
14264        window: &mut Window,
14265        cx: &mut Context<Self>,
14266    ) {
14267        let Some(row_count) = self.visible_row_count() else {
14268            return;
14269        };
14270
14271        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14272
14273        let text_layout_details = &self.text_layout_details(window, cx);
14274
14275        self.change_selections(Default::default(), window, cx, |s| {
14276            s.move_heads_with(&mut |map, head, goal| {
14277                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14278            })
14279        })
14280    }
14281
14282    pub fn move_page_up(
14283        &mut self,
14284        action: &MovePageUp,
14285        window: &mut Window,
14286        cx: &mut Context<Self>,
14287    ) {
14288        if self.take_rename(true, window, cx).is_some() {
14289            return;
14290        }
14291
14292        if self
14293            .context_menu
14294            .borrow_mut()
14295            .as_mut()
14296            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14297            .unwrap_or(false)
14298        {
14299            return;
14300        }
14301
14302        if matches!(self.mode, EditorMode::SingleLine) {
14303            cx.propagate();
14304            return;
14305        }
14306
14307        let Some(row_count) = self.visible_row_count() else {
14308            return;
14309        };
14310
14311        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14312
14313        let effects = if action.center_cursor {
14314            SelectionEffects::scroll(Autoscroll::center())
14315        } else {
14316            SelectionEffects::default()
14317        };
14318
14319        let text_layout_details = &self.text_layout_details(window, cx);
14320
14321        self.change_selections(effects, window, cx, |s| {
14322            s.move_with(&mut |map, selection| {
14323                if !selection.is_empty() {
14324                    selection.goal = SelectionGoal::None;
14325                }
14326                let (cursor, goal) = movement::up_by_rows(
14327                    map,
14328                    selection.end,
14329                    row_count,
14330                    selection.goal,
14331                    false,
14332                    text_layout_details,
14333                );
14334                selection.collapse_to(cursor, goal);
14335            });
14336        });
14337    }
14338
14339    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14340        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14341        let text_layout_details = &self.text_layout_details(window, cx);
14342        self.change_selections(Default::default(), window, cx, |s| {
14343            s.move_heads_with(&mut |map, head, goal| {
14344                movement::up(map, head, goal, false, text_layout_details)
14345            })
14346        })
14347    }
14348
14349    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14350        self.take_rename(true, window, cx);
14351
14352        if self.mode.is_single_line() {
14353            cx.propagate();
14354            return;
14355        }
14356
14357        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14358
14359        let text_layout_details = &self.text_layout_details(window, cx);
14360        let selection_count = self.selections.count();
14361        let first_selection = self.selections.first_anchor();
14362
14363        self.change_selections(Default::default(), window, cx, |s| {
14364            s.move_with(&mut |map, selection| {
14365                if !selection.is_empty() {
14366                    selection.goal = SelectionGoal::None;
14367                }
14368                let (cursor, goal) = movement::down(
14369                    map,
14370                    selection.end,
14371                    selection.goal,
14372                    false,
14373                    text_layout_details,
14374                );
14375                selection.collapse_to(cursor, goal);
14376            });
14377        });
14378
14379        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14380        {
14381            cx.propagate();
14382        }
14383    }
14384
14385    pub fn select_page_down(
14386        &mut self,
14387        _: &SelectPageDown,
14388        window: &mut Window,
14389        cx: &mut Context<Self>,
14390    ) {
14391        let Some(row_count) = self.visible_row_count() else {
14392            return;
14393        };
14394
14395        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14396
14397        let text_layout_details = &self.text_layout_details(window, cx);
14398
14399        self.change_selections(Default::default(), window, cx, |s| {
14400            s.move_heads_with(&mut |map, head, goal| {
14401                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14402            })
14403        })
14404    }
14405
14406    pub fn move_page_down(
14407        &mut self,
14408        action: &MovePageDown,
14409        window: &mut Window,
14410        cx: &mut Context<Self>,
14411    ) {
14412        if self.take_rename(true, window, cx).is_some() {
14413            return;
14414        }
14415
14416        if self
14417            .context_menu
14418            .borrow_mut()
14419            .as_mut()
14420            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14421            .unwrap_or(false)
14422        {
14423            return;
14424        }
14425
14426        if matches!(self.mode, EditorMode::SingleLine) {
14427            cx.propagate();
14428            return;
14429        }
14430
14431        let Some(row_count) = self.visible_row_count() else {
14432            return;
14433        };
14434
14435        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14436
14437        let effects = if action.center_cursor {
14438            SelectionEffects::scroll(Autoscroll::center())
14439        } else {
14440            SelectionEffects::default()
14441        };
14442
14443        let text_layout_details = &self.text_layout_details(window, cx);
14444        self.change_selections(effects, window, cx, |s| {
14445            s.move_with(&mut |map, selection| {
14446                if !selection.is_empty() {
14447                    selection.goal = SelectionGoal::None;
14448                }
14449                let (cursor, goal) = movement::down_by_rows(
14450                    map,
14451                    selection.end,
14452                    row_count,
14453                    selection.goal,
14454                    false,
14455                    text_layout_details,
14456                );
14457                selection.collapse_to(cursor, goal);
14458            });
14459        });
14460    }
14461
14462    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14463        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14464        let text_layout_details = &self.text_layout_details(window, cx);
14465        self.change_selections(Default::default(), window, cx, |s| {
14466            s.move_heads_with(&mut |map, head, goal| {
14467                movement::down(map, head, goal, false, text_layout_details)
14468            })
14469        });
14470    }
14471
14472    pub fn context_menu_first(
14473        &mut self,
14474        _: &ContextMenuFirst,
14475        window: &mut Window,
14476        cx: &mut Context<Self>,
14477    ) {
14478        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14479            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14480        }
14481    }
14482
14483    pub fn context_menu_prev(
14484        &mut self,
14485        _: &ContextMenuPrevious,
14486        window: &mut Window,
14487        cx: &mut Context<Self>,
14488    ) {
14489        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14490            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14491        }
14492    }
14493
14494    pub fn context_menu_next(
14495        &mut self,
14496        _: &ContextMenuNext,
14497        window: &mut Window,
14498        cx: &mut Context<Self>,
14499    ) {
14500        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14501            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14502        }
14503    }
14504
14505    pub fn context_menu_last(
14506        &mut self,
14507        _: &ContextMenuLast,
14508        window: &mut Window,
14509        cx: &mut Context<Self>,
14510    ) {
14511        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14512            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14513        }
14514    }
14515
14516    pub fn signature_help_prev(
14517        &mut self,
14518        _: &SignatureHelpPrevious,
14519        _: &mut Window,
14520        cx: &mut Context<Self>,
14521    ) {
14522        if let Some(popover) = self.signature_help_state.popover_mut() {
14523            if popover.current_signature == 0 {
14524                popover.current_signature = popover.signatures.len() - 1;
14525            } else {
14526                popover.current_signature -= 1;
14527            }
14528            cx.notify();
14529        }
14530    }
14531
14532    pub fn signature_help_next(
14533        &mut self,
14534        _: &SignatureHelpNext,
14535        _: &mut Window,
14536        cx: &mut Context<Self>,
14537    ) {
14538        if let Some(popover) = self.signature_help_state.popover_mut() {
14539            if popover.current_signature + 1 == popover.signatures.len() {
14540                popover.current_signature = 0;
14541            } else {
14542                popover.current_signature += 1;
14543            }
14544            cx.notify();
14545        }
14546    }
14547
14548    pub fn move_to_previous_word_start(
14549        &mut self,
14550        _: &MoveToPreviousWordStart,
14551        window: &mut Window,
14552        cx: &mut Context<Self>,
14553    ) {
14554        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14555        self.change_selections(Default::default(), window, cx, |s| {
14556            s.move_cursors_with(&mut |map, head, _| {
14557                (
14558                    movement::previous_word_start(map, head),
14559                    SelectionGoal::None,
14560                )
14561            });
14562        })
14563    }
14564
14565    pub fn move_to_previous_subword_start(
14566        &mut self,
14567        _: &MoveToPreviousSubwordStart,
14568        window: &mut Window,
14569        cx: &mut Context<Self>,
14570    ) {
14571        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14572        self.change_selections(Default::default(), window, cx, |s| {
14573            s.move_cursors_with(&mut |map, head, _| {
14574                (
14575                    movement::previous_subword_start(map, head),
14576                    SelectionGoal::None,
14577                )
14578            });
14579        })
14580    }
14581
14582    pub fn select_to_previous_word_start(
14583        &mut self,
14584        _: &SelectToPreviousWordStart,
14585        window: &mut Window,
14586        cx: &mut Context<Self>,
14587    ) {
14588        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14589        self.change_selections(Default::default(), window, cx, |s| {
14590            s.move_heads_with(&mut |map, head, _| {
14591                (
14592                    movement::previous_word_start(map, head),
14593                    SelectionGoal::None,
14594                )
14595            });
14596        })
14597    }
14598
14599    pub fn select_to_previous_subword_start(
14600        &mut self,
14601        _: &SelectToPreviousSubwordStart,
14602        window: &mut Window,
14603        cx: &mut Context<Self>,
14604    ) {
14605        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14606        self.change_selections(Default::default(), window, cx, |s| {
14607            s.move_heads_with(&mut |map, head, _| {
14608                (
14609                    movement::previous_subword_start(map, head),
14610                    SelectionGoal::None,
14611                )
14612            });
14613        })
14614    }
14615
14616    pub fn delete_to_previous_word_start(
14617        &mut self,
14618        action: &DeleteToPreviousWordStart,
14619        window: &mut Window,
14620        cx: &mut Context<Self>,
14621    ) {
14622        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14623        self.transact(window, cx, |this, window, cx| {
14624            this.select_autoclose_pair(window, cx);
14625            this.change_selections(Default::default(), window, cx, |s| {
14626                s.move_with(&mut |map, selection| {
14627                    if selection.is_empty() {
14628                        let mut cursor = if action.ignore_newlines {
14629                            movement::previous_word_start(map, selection.head())
14630                        } else {
14631                            movement::previous_word_start_or_newline(map, selection.head())
14632                        };
14633                        cursor = movement::adjust_greedy_deletion(
14634                            map,
14635                            selection.head(),
14636                            cursor,
14637                            action.ignore_brackets,
14638                        );
14639                        selection.set_head(cursor, SelectionGoal::None);
14640                    }
14641                });
14642            });
14643            this.insert("", window, cx);
14644        });
14645    }
14646
14647    pub fn delete_to_previous_subword_start(
14648        &mut self,
14649        action: &DeleteToPreviousSubwordStart,
14650        window: &mut Window,
14651        cx: &mut Context<Self>,
14652    ) {
14653        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14654        self.transact(window, cx, |this, window, cx| {
14655            this.select_autoclose_pair(window, cx);
14656            this.change_selections(Default::default(), window, cx, |s| {
14657                s.move_with(&mut |map, selection| {
14658                    if selection.is_empty() {
14659                        let mut cursor = if action.ignore_newlines {
14660                            movement::previous_subword_start(map, selection.head())
14661                        } else {
14662                            movement::previous_subword_start_or_newline(map, selection.head())
14663                        };
14664                        cursor = movement::adjust_greedy_deletion(
14665                            map,
14666                            selection.head(),
14667                            cursor,
14668                            action.ignore_brackets,
14669                        );
14670                        selection.set_head(cursor, SelectionGoal::None);
14671                    }
14672                });
14673            });
14674            this.insert("", window, cx);
14675        });
14676    }
14677
14678    pub fn move_to_next_word_end(
14679        &mut self,
14680        _: &MoveToNextWordEnd,
14681        window: &mut Window,
14682        cx: &mut Context<Self>,
14683    ) {
14684        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14685        self.change_selections(Default::default(), window, cx, |s| {
14686            s.move_cursors_with(&mut |map, head, _| {
14687                (movement::next_word_end(map, head), SelectionGoal::None)
14688            });
14689        })
14690    }
14691
14692    pub fn move_to_next_subword_end(
14693        &mut self,
14694        _: &MoveToNextSubwordEnd,
14695        window: &mut Window,
14696        cx: &mut Context<Self>,
14697    ) {
14698        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14699        self.change_selections(Default::default(), window, cx, |s| {
14700            s.move_cursors_with(&mut |map, head, _| {
14701                (movement::next_subword_end(map, head), SelectionGoal::None)
14702            });
14703        })
14704    }
14705
14706    pub fn select_to_next_word_end(
14707        &mut self,
14708        _: &SelectToNextWordEnd,
14709        window: &mut Window,
14710        cx: &mut Context<Self>,
14711    ) {
14712        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14713        self.change_selections(Default::default(), window, cx, |s| {
14714            s.move_heads_with(&mut |map, head, _| {
14715                (movement::next_word_end(map, head), SelectionGoal::None)
14716            });
14717        })
14718    }
14719
14720    pub fn select_to_next_subword_end(
14721        &mut self,
14722        _: &SelectToNextSubwordEnd,
14723        window: &mut Window,
14724        cx: &mut Context<Self>,
14725    ) {
14726        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14727        self.change_selections(Default::default(), window, cx, |s| {
14728            s.move_heads_with(&mut |map, head, _| {
14729                (movement::next_subword_end(map, head), SelectionGoal::None)
14730            });
14731        })
14732    }
14733
14734    pub fn delete_to_next_word_end(
14735        &mut self,
14736        action: &DeleteToNextWordEnd,
14737        window: &mut Window,
14738        cx: &mut Context<Self>,
14739    ) {
14740        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14741        self.transact(window, cx, |this, window, cx| {
14742            this.change_selections(Default::default(), window, cx, |s| {
14743                s.move_with(&mut |map, selection| {
14744                    if selection.is_empty() {
14745                        let mut cursor = if action.ignore_newlines {
14746                            movement::next_word_end(map, selection.head())
14747                        } else {
14748                            movement::next_word_end_or_newline(map, selection.head())
14749                        };
14750                        cursor = movement::adjust_greedy_deletion(
14751                            map,
14752                            selection.head(),
14753                            cursor,
14754                            action.ignore_brackets,
14755                        );
14756                        selection.set_head(cursor, SelectionGoal::None);
14757                    }
14758                });
14759            });
14760            this.insert("", window, cx);
14761        });
14762    }
14763
14764    pub fn delete_to_next_subword_end(
14765        &mut self,
14766        action: &DeleteToNextSubwordEnd,
14767        window: &mut Window,
14768        cx: &mut Context<Self>,
14769    ) {
14770        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14771        self.transact(window, cx, |this, window, cx| {
14772            this.change_selections(Default::default(), window, cx, |s| {
14773                s.move_with(&mut |map, selection| {
14774                    if selection.is_empty() {
14775                        let mut cursor = if action.ignore_newlines {
14776                            movement::next_subword_end(map, selection.head())
14777                        } else {
14778                            movement::next_subword_end_or_newline(map, selection.head())
14779                        };
14780                        cursor = movement::adjust_greedy_deletion(
14781                            map,
14782                            selection.head(),
14783                            cursor,
14784                            action.ignore_brackets,
14785                        );
14786                        selection.set_head(cursor, SelectionGoal::None);
14787                    }
14788                });
14789            });
14790            this.insert("", window, cx);
14791        });
14792    }
14793
14794    pub fn move_to_beginning_of_line(
14795        &mut self,
14796        action: &MoveToBeginningOfLine,
14797        window: &mut Window,
14798        cx: &mut Context<Self>,
14799    ) {
14800        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14801        self.change_selections(Default::default(), window, cx, |s| {
14802            s.move_cursors_with(&mut |map, head, _| {
14803                (
14804                    movement::indented_line_beginning(
14805                        map,
14806                        head,
14807                        action.stop_at_soft_wraps,
14808                        action.stop_at_indent,
14809                    ),
14810                    SelectionGoal::None,
14811                )
14812            });
14813        })
14814    }
14815
14816    pub fn select_to_beginning_of_line(
14817        &mut self,
14818        action: &SelectToBeginningOfLine,
14819        window: &mut Window,
14820        cx: &mut Context<Self>,
14821    ) {
14822        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14823        self.change_selections(Default::default(), window, cx, |s| {
14824            s.move_heads_with(&mut |map, head, _| {
14825                (
14826                    movement::indented_line_beginning(
14827                        map,
14828                        head,
14829                        action.stop_at_soft_wraps,
14830                        action.stop_at_indent,
14831                    ),
14832                    SelectionGoal::None,
14833                )
14834            });
14835        });
14836    }
14837
14838    pub fn delete_to_beginning_of_line(
14839        &mut self,
14840        action: &DeleteToBeginningOfLine,
14841        window: &mut Window,
14842        cx: &mut Context<Self>,
14843    ) {
14844        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14845        self.transact(window, cx, |this, window, cx| {
14846            this.change_selections(Default::default(), window, cx, |s| {
14847                s.move_with(&mut |_, selection| {
14848                    selection.reversed = true;
14849                });
14850            });
14851
14852            this.select_to_beginning_of_line(
14853                &SelectToBeginningOfLine {
14854                    stop_at_soft_wraps: false,
14855                    stop_at_indent: action.stop_at_indent,
14856                },
14857                window,
14858                cx,
14859            );
14860            this.backspace(&Backspace, window, cx);
14861        });
14862    }
14863
14864    pub fn move_to_end_of_line(
14865        &mut self,
14866        action: &MoveToEndOfLine,
14867        window: &mut Window,
14868        cx: &mut Context<Self>,
14869    ) {
14870        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14871        self.change_selections(Default::default(), window, cx, |s| {
14872            s.move_cursors_with(&mut |map, head, _| {
14873                (
14874                    movement::line_end(map, head, action.stop_at_soft_wraps),
14875                    SelectionGoal::None,
14876                )
14877            });
14878        })
14879    }
14880
14881    pub fn select_to_end_of_line(
14882        &mut self,
14883        action: &SelectToEndOfLine,
14884        window: &mut Window,
14885        cx: &mut Context<Self>,
14886    ) {
14887        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14888        self.change_selections(Default::default(), window, cx, |s| {
14889            s.move_heads_with(&mut |map, head, _| {
14890                (
14891                    movement::line_end(map, head, action.stop_at_soft_wraps),
14892                    SelectionGoal::None,
14893                )
14894            });
14895        })
14896    }
14897
14898    pub fn delete_to_end_of_line(
14899        &mut self,
14900        _: &DeleteToEndOfLine,
14901        window: &mut Window,
14902        cx: &mut Context<Self>,
14903    ) {
14904        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14905        self.transact(window, cx, |this, window, cx| {
14906            this.select_to_end_of_line(
14907                &SelectToEndOfLine {
14908                    stop_at_soft_wraps: false,
14909                },
14910                window,
14911                cx,
14912            );
14913            this.delete(&Delete, window, cx);
14914        });
14915    }
14916
14917    pub fn cut_to_end_of_line(
14918        &mut self,
14919        action: &CutToEndOfLine,
14920        window: &mut Window,
14921        cx: &mut Context<Self>,
14922    ) {
14923        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14924        self.transact(window, cx, |this, window, cx| {
14925            this.select_to_end_of_line(
14926                &SelectToEndOfLine {
14927                    stop_at_soft_wraps: false,
14928                },
14929                window,
14930                cx,
14931            );
14932            if !action.stop_at_newlines {
14933                this.change_selections(Default::default(), window, cx, |s| {
14934                    s.move_with(&mut |_, sel| {
14935                        if sel.is_empty() {
14936                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14937                        }
14938                    });
14939                });
14940            }
14941            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14942            let item = this.cut_common(false, window, cx);
14943            cx.write_to_clipboard(item);
14944        });
14945    }
14946
14947    pub fn move_to_start_of_paragraph(
14948        &mut self,
14949        _: &MoveToStartOfParagraph,
14950        window: &mut Window,
14951        cx: &mut Context<Self>,
14952    ) {
14953        if matches!(self.mode, EditorMode::SingleLine) {
14954            cx.propagate();
14955            return;
14956        }
14957        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14958        self.change_selections(Default::default(), window, cx, |s| {
14959            s.move_with(&mut |map, selection| {
14960                selection.collapse_to(
14961                    movement::start_of_paragraph(map, selection.head(), 1),
14962                    SelectionGoal::None,
14963                )
14964            });
14965        })
14966    }
14967
14968    pub fn move_to_end_of_paragraph(
14969        &mut self,
14970        _: &MoveToEndOfParagraph,
14971        window: &mut Window,
14972        cx: &mut Context<Self>,
14973    ) {
14974        if matches!(self.mode, EditorMode::SingleLine) {
14975            cx.propagate();
14976            return;
14977        }
14978        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14979        self.change_selections(Default::default(), window, cx, |s| {
14980            s.move_with(&mut |map, selection| {
14981                selection.collapse_to(
14982                    movement::end_of_paragraph(map, selection.head(), 1),
14983                    SelectionGoal::None,
14984                )
14985            });
14986        })
14987    }
14988
14989    pub fn select_to_start_of_paragraph(
14990        &mut self,
14991        _: &SelectToStartOfParagraph,
14992        window: &mut Window,
14993        cx: &mut Context<Self>,
14994    ) {
14995        if matches!(self.mode, EditorMode::SingleLine) {
14996            cx.propagate();
14997            return;
14998        }
14999        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15000        self.change_selections(Default::default(), window, cx, |s| {
15001            s.move_heads_with(&mut |map, head, _| {
15002                (
15003                    movement::start_of_paragraph(map, head, 1),
15004                    SelectionGoal::None,
15005                )
15006            });
15007        })
15008    }
15009
15010    pub fn select_to_end_of_paragraph(
15011        &mut self,
15012        _: &SelectToEndOfParagraph,
15013        window: &mut Window,
15014        cx: &mut Context<Self>,
15015    ) {
15016        if matches!(self.mode, EditorMode::SingleLine) {
15017            cx.propagate();
15018            return;
15019        }
15020        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15021        self.change_selections(Default::default(), window, cx, |s| {
15022            s.move_heads_with(&mut |map, head, _| {
15023                (
15024                    movement::end_of_paragraph(map, head, 1),
15025                    SelectionGoal::None,
15026                )
15027            });
15028        })
15029    }
15030
15031    pub fn move_to_start_of_excerpt(
15032        &mut self,
15033        _: &MoveToStartOfExcerpt,
15034        window: &mut Window,
15035        cx: &mut Context<Self>,
15036    ) {
15037        if matches!(self.mode, EditorMode::SingleLine) {
15038            cx.propagate();
15039            return;
15040        }
15041        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15042        self.change_selections(Default::default(), window, cx, |s| {
15043            s.move_with(&mut |map, selection| {
15044                selection.collapse_to(
15045                    movement::start_of_excerpt(
15046                        map,
15047                        selection.head(),
15048                        workspace::searchable::Direction::Prev,
15049                    ),
15050                    SelectionGoal::None,
15051                )
15052            });
15053        })
15054    }
15055
15056    pub fn move_to_start_of_next_excerpt(
15057        &mut self,
15058        _: &MoveToStartOfNextExcerpt,
15059        window: &mut Window,
15060        cx: &mut Context<Self>,
15061    ) {
15062        if matches!(self.mode, EditorMode::SingleLine) {
15063            cx.propagate();
15064            return;
15065        }
15066
15067        self.change_selections(Default::default(), window, cx, |s| {
15068            s.move_with(&mut |map, selection| {
15069                selection.collapse_to(
15070                    movement::start_of_excerpt(
15071                        map,
15072                        selection.head(),
15073                        workspace::searchable::Direction::Next,
15074                    ),
15075                    SelectionGoal::None,
15076                )
15077            });
15078        })
15079    }
15080
15081    pub fn move_to_end_of_excerpt(
15082        &mut self,
15083        _: &MoveToEndOfExcerpt,
15084        window: &mut Window,
15085        cx: &mut Context<Self>,
15086    ) {
15087        if matches!(self.mode, EditorMode::SingleLine) {
15088            cx.propagate();
15089            return;
15090        }
15091        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15092        self.change_selections(Default::default(), window, cx, |s| {
15093            s.move_with(&mut |map, selection| {
15094                selection.collapse_to(
15095                    movement::end_of_excerpt(
15096                        map,
15097                        selection.head(),
15098                        workspace::searchable::Direction::Next,
15099                    ),
15100                    SelectionGoal::None,
15101                )
15102            });
15103        })
15104    }
15105
15106    pub fn move_to_end_of_previous_excerpt(
15107        &mut self,
15108        _: &MoveToEndOfPreviousExcerpt,
15109        window: &mut Window,
15110        cx: &mut Context<Self>,
15111    ) {
15112        if matches!(self.mode, EditorMode::SingleLine) {
15113            cx.propagate();
15114            return;
15115        }
15116        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15117        self.change_selections(Default::default(), window, cx, |s| {
15118            s.move_with(&mut |map, selection| {
15119                selection.collapse_to(
15120                    movement::end_of_excerpt(
15121                        map,
15122                        selection.head(),
15123                        workspace::searchable::Direction::Prev,
15124                    ),
15125                    SelectionGoal::None,
15126                )
15127            });
15128        })
15129    }
15130
15131    pub fn select_to_start_of_excerpt(
15132        &mut self,
15133        _: &SelectToStartOfExcerpt,
15134        window: &mut Window,
15135        cx: &mut Context<Self>,
15136    ) {
15137        if matches!(self.mode, EditorMode::SingleLine) {
15138            cx.propagate();
15139            return;
15140        }
15141        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15142        self.change_selections(Default::default(), window, cx, |s| {
15143            s.move_heads_with(&mut |map, head, _| {
15144                (
15145                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15146                    SelectionGoal::None,
15147                )
15148            });
15149        })
15150    }
15151
15152    pub fn select_to_start_of_next_excerpt(
15153        &mut self,
15154        _: &SelectToStartOfNextExcerpt,
15155        window: &mut Window,
15156        cx: &mut Context<Self>,
15157    ) {
15158        if matches!(self.mode, EditorMode::SingleLine) {
15159            cx.propagate();
15160            return;
15161        }
15162        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15163        self.change_selections(Default::default(), window, cx, |s| {
15164            s.move_heads_with(&mut |map, head, _| {
15165                (
15166                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15167                    SelectionGoal::None,
15168                )
15169            });
15170        })
15171    }
15172
15173    pub fn select_to_end_of_excerpt(
15174        &mut self,
15175        _: &SelectToEndOfExcerpt,
15176        window: &mut Window,
15177        cx: &mut Context<Self>,
15178    ) {
15179        if matches!(self.mode, EditorMode::SingleLine) {
15180            cx.propagate();
15181            return;
15182        }
15183        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15184        self.change_selections(Default::default(), window, cx, |s| {
15185            s.move_heads_with(&mut |map, head, _| {
15186                (
15187                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15188                    SelectionGoal::None,
15189                )
15190            });
15191        })
15192    }
15193
15194    pub fn select_to_end_of_previous_excerpt(
15195        &mut self,
15196        _: &SelectToEndOfPreviousExcerpt,
15197        window: &mut Window,
15198        cx: &mut Context<Self>,
15199    ) {
15200        if matches!(self.mode, EditorMode::SingleLine) {
15201            cx.propagate();
15202            return;
15203        }
15204        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15205        self.change_selections(Default::default(), window, cx, |s| {
15206            s.move_heads_with(&mut |map, head, _| {
15207                (
15208                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15209                    SelectionGoal::None,
15210                )
15211            });
15212        })
15213    }
15214
15215    pub fn move_to_beginning(
15216        &mut self,
15217        _: &MoveToBeginning,
15218        window: &mut Window,
15219        cx: &mut Context<Self>,
15220    ) {
15221        if matches!(self.mode, EditorMode::SingleLine) {
15222            cx.propagate();
15223            return;
15224        }
15225        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15226        self.change_selections(Default::default(), window, cx, |s| {
15227            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15228        });
15229    }
15230
15231    pub fn select_to_beginning(
15232        &mut self,
15233        _: &SelectToBeginning,
15234        window: &mut Window,
15235        cx: &mut Context<Self>,
15236    ) {
15237        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15238        selection.set_head(Point::zero(), SelectionGoal::None);
15239        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15240        self.change_selections(Default::default(), window, cx, |s| {
15241            s.select(vec![selection]);
15242        });
15243    }
15244
15245    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15246        if matches!(self.mode, EditorMode::SingleLine) {
15247            cx.propagate();
15248            return;
15249        }
15250        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15251        let cursor = self.buffer.read(cx).read(cx).len();
15252        self.change_selections(Default::default(), window, cx, |s| {
15253            s.select_ranges(vec![cursor..cursor])
15254        });
15255    }
15256
15257    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15258        self.nav_history = nav_history;
15259    }
15260
15261    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15262        self.nav_history.as_ref()
15263    }
15264
15265    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15266        self.push_to_nav_history(
15267            self.selections.newest_anchor().head(),
15268            None,
15269            false,
15270            true,
15271            cx,
15272        );
15273    }
15274
15275    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15276        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15277        let buffer = self.buffer.read(cx).read(cx);
15278        let cursor_position = cursor_anchor.to_point(&buffer);
15279        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15280        let scroll_top_row = scroll_anchor.top_row(&buffer);
15281        drop(buffer);
15282
15283        NavigationData {
15284            cursor_anchor,
15285            cursor_position,
15286            scroll_anchor,
15287            scroll_top_row,
15288        }
15289    }
15290
15291    fn navigation_entry(
15292        &self,
15293        cursor_anchor: Anchor,
15294        cx: &mut Context<Self>,
15295    ) -> Option<NavigationEntry> {
15296        let Some(history) = self.nav_history.clone() else {
15297            return None;
15298        };
15299        let data = self.navigation_data(cursor_anchor, cx);
15300        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15301    }
15302
15303    fn push_to_nav_history(
15304        &mut self,
15305        cursor_anchor: Anchor,
15306        new_position: Option<Point>,
15307        is_deactivate: bool,
15308        always: bool,
15309        cx: &mut Context<Self>,
15310    ) {
15311        let data = self.navigation_data(cursor_anchor, cx);
15312        if let Some(nav_history) = self.nav_history.as_mut() {
15313            if let Some(new_position) = new_position {
15314                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15315                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15316                    return;
15317                }
15318            }
15319
15320            nav_history.push(Some(data), cx);
15321            cx.emit(EditorEvent::PushedToNavHistory {
15322                anchor: cursor_anchor,
15323                is_deactivate,
15324            })
15325        }
15326    }
15327
15328    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15329        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15330        let buffer = self.buffer.read(cx).snapshot(cx);
15331        let mut selection = self
15332            .selections
15333            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15334        selection.set_head(buffer.len(), SelectionGoal::None);
15335        self.change_selections(Default::default(), window, cx, |s| {
15336            s.select(vec![selection]);
15337        });
15338    }
15339
15340    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15341        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15342        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15343            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15344        });
15345    }
15346
15347    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15348        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15349        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15350        let mut selections = self.selections.all::<Point>(&display_map);
15351        let max_point = display_map.buffer_snapshot().max_point();
15352        for selection in &mut selections {
15353            let rows = selection.spanned_rows(true, &display_map);
15354            selection.start = Point::new(rows.start.0, 0);
15355            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15356            selection.reversed = false;
15357        }
15358        self.change_selections(Default::default(), window, cx, |s| {
15359            s.select(selections);
15360        });
15361    }
15362
15363    pub fn split_selection_into_lines(
15364        &mut self,
15365        action: &SplitSelectionIntoLines,
15366        window: &mut Window,
15367        cx: &mut Context<Self>,
15368    ) {
15369        let selections = self
15370            .selections
15371            .all::<Point>(&self.display_snapshot(cx))
15372            .into_iter()
15373            .map(|selection| selection.start..selection.end)
15374            .collect::<Vec<_>>();
15375        self.unfold_ranges(&selections, true, true, cx);
15376
15377        let mut new_selection_ranges = Vec::new();
15378        {
15379            let buffer = self.buffer.read(cx).read(cx);
15380            for selection in selections {
15381                for row in selection.start.row..selection.end.row {
15382                    let line_start = Point::new(row, 0);
15383                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15384
15385                    if action.keep_selections {
15386                        // Keep the selection range for each line
15387                        let selection_start = if row == selection.start.row {
15388                            selection.start
15389                        } else {
15390                            line_start
15391                        };
15392                        new_selection_ranges.push(selection_start..line_end);
15393                    } else {
15394                        // Collapse to cursor at end of line
15395                        new_selection_ranges.push(line_end..line_end);
15396                    }
15397                }
15398
15399                let is_multiline_selection = selection.start.row != selection.end.row;
15400                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15401                // so this action feels more ergonomic when paired with other selection operations
15402                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15403                if !should_skip_last {
15404                    if action.keep_selections {
15405                        if is_multiline_selection {
15406                            let line_start = Point::new(selection.end.row, 0);
15407                            new_selection_ranges.push(line_start..selection.end);
15408                        } else {
15409                            new_selection_ranges.push(selection.start..selection.end);
15410                        }
15411                    } else {
15412                        new_selection_ranges.push(selection.end..selection.end);
15413                    }
15414                }
15415            }
15416        }
15417        self.change_selections(Default::default(), window, cx, |s| {
15418            s.select_ranges(new_selection_ranges);
15419        });
15420    }
15421
15422    pub fn add_selection_above(
15423        &mut self,
15424        action: &AddSelectionAbove,
15425        window: &mut Window,
15426        cx: &mut Context<Self>,
15427    ) {
15428        self.add_selection(true, action.skip_soft_wrap, window, cx);
15429    }
15430
15431    pub fn add_selection_below(
15432        &mut self,
15433        action: &AddSelectionBelow,
15434        window: &mut Window,
15435        cx: &mut Context<Self>,
15436    ) {
15437        self.add_selection(false, action.skip_soft_wrap, window, cx);
15438    }
15439
15440    fn add_selection(
15441        &mut self,
15442        above: bool,
15443        skip_soft_wrap: bool,
15444        window: &mut Window,
15445        cx: &mut Context<Self>,
15446    ) {
15447        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15448
15449        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15450        let all_selections = self.selections.all::<Point>(&display_map);
15451        let text_layout_details = self.text_layout_details(window, cx);
15452
15453        let (mut columnar_selections, new_selections_to_columnarize) = {
15454            if let Some(state) = self.add_selections_state.as_ref() {
15455                let columnar_selection_ids: HashSet<_> = state
15456                    .groups
15457                    .iter()
15458                    .flat_map(|group| group.stack.iter())
15459                    .copied()
15460                    .collect();
15461
15462                all_selections
15463                    .into_iter()
15464                    .partition(|s| columnar_selection_ids.contains(&s.id))
15465            } else {
15466                (Vec::new(), all_selections)
15467            }
15468        };
15469
15470        let mut state = self
15471            .add_selections_state
15472            .take()
15473            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15474
15475        for selection in new_selections_to_columnarize {
15476            let range = selection.display_range(&display_map).sorted();
15477            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15478            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15479            let positions = start_x.min(end_x)..start_x.max(end_x);
15480            let mut stack = Vec::new();
15481            for row in range.start.row().0..=range.end.row().0 {
15482                if let Some(selection) = self.selections.build_columnar_selection(
15483                    &display_map,
15484                    DisplayRow(row),
15485                    &positions,
15486                    selection.reversed,
15487                    &text_layout_details,
15488                ) {
15489                    stack.push(selection.id);
15490                    columnar_selections.push(selection);
15491                }
15492            }
15493            if !stack.is_empty() {
15494                if above {
15495                    stack.reverse();
15496                }
15497                state.groups.push(AddSelectionsGroup { above, stack });
15498            }
15499        }
15500
15501        let mut final_selections = Vec::new();
15502        let end_row = if above {
15503            DisplayRow(0)
15504        } else {
15505            display_map.max_point().row()
15506        };
15507
15508        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15509        // positions to place new selections, so we need to keep track of the
15510        // column range of the oldest selection in each group, because
15511        // intermediate selections may have been clamped to shorter lines.
15512        // selections may have been clamped to shorter lines.
15513        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15514            let mut map = HashMap::default();
15515            for group in state.groups.iter() {
15516                if let Some(oldest_id) = group.stack.first() {
15517                    if let Some(oldest_selection) =
15518                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15519                    {
15520                        let start_col = oldest_selection.start.column;
15521                        let end_col = oldest_selection.end.column;
15522                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15523                        for id in &group.stack {
15524                            map.insert(*id, goal_columns.clone());
15525                        }
15526                    }
15527                }
15528            }
15529            map
15530        } else {
15531            HashMap::default()
15532        };
15533
15534        let mut last_added_item_per_group = HashMap::default();
15535        for group in state.groups.iter_mut() {
15536            if let Some(last_id) = group.stack.last() {
15537                last_added_item_per_group.insert(*last_id, group);
15538            }
15539        }
15540
15541        for selection in columnar_selections {
15542            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15543                if above == group.above {
15544                    let range = selection.display_range(&display_map).sorted();
15545                    debug_assert_eq!(range.start.row(), range.end.row());
15546                    let row = range.start.row();
15547                    let positions =
15548                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15549                            Pixels::from(start)..Pixels::from(end)
15550                        } else {
15551                            let start_x =
15552                                display_map.x_for_display_point(range.start, &text_layout_details);
15553                            let end_x =
15554                                display_map.x_for_display_point(range.end, &text_layout_details);
15555                            start_x.min(end_x)..start_x.max(end_x)
15556                        };
15557
15558                    let maybe_new_selection = if skip_soft_wrap {
15559                        let goal_columns = goal_columns_by_selection_id
15560                            .remove(&selection.id)
15561                            .unwrap_or_else(|| {
15562                                let start_col = selection.start.column;
15563                                let end_col = selection.end.column;
15564                                start_col.min(end_col)..start_col.max(end_col)
15565                            });
15566                        self.selections.find_next_columnar_selection_by_buffer_row(
15567                            &display_map,
15568                            row,
15569                            end_row,
15570                            above,
15571                            &goal_columns,
15572                            selection.reversed,
15573                            &text_layout_details,
15574                        )
15575                    } else {
15576                        self.selections.find_next_columnar_selection_by_display_row(
15577                            &display_map,
15578                            row,
15579                            end_row,
15580                            above,
15581                            &positions,
15582                            selection.reversed,
15583                            &text_layout_details,
15584                        )
15585                    };
15586
15587                    if let Some(new_selection) = maybe_new_selection {
15588                        group.stack.push(new_selection.id);
15589                        if above {
15590                            final_selections.push(new_selection);
15591                            final_selections.push(selection);
15592                        } else {
15593                            final_selections.push(selection);
15594                            final_selections.push(new_selection);
15595                        }
15596                    } else {
15597                        final_selections.push(selection);
15598                    }
15599                } else {
15600                    group.stack.pop();
15601                }
15602            } else {
15603                final_selections.push(selection);
15604            }
15605        }
15606
15607        self.change_selections(Default::default(), window, cx, |s| {
15608            s.select(final_selections);
15609        });
15610
15611        let final_selection_ids: HashSet<_> = self
15612            .selections
15613            .all::<Point>(&display_map)
15614            .iter()
15615            .map(|s| s.id)
15616            .collect();
15617        state.groups.retain_mut(|group| {
15618            // selections might get merged above so we remove invalid items from stacks
15619            group.stack.retain(|id| final_selection_ids.contains(id));
15620
15621            // single selection in stack can be treated as initial state
15622            group.stack.len() > 1
15623        });
15624
15625        if !state.groups.is_empty() {
15626            self.add_selections_state = Some(state);
15627        }
15628    }
15629
15630    pub fn insert_snippet_at_selections(
15631        &mut self,
15632        action: &InsertSnippet,
15633        window: &mut Window,
15634        cx: &mut Context<Self>,
15635    ) {
15636        self.try_insert_snippet_at_selections(action, window, cx)
15637            .log_err();
15638    }
15639
15640    fn try_insert_snippet_at_selections(
15641        &mut self,
15642        action: &InsertSnippet,
15643        window: &mut Window,
15644        cx: &mut Context<Self>,
15645    ) -> Result<()> {
15646        let insertion_ranges = self
15647            .selections
15648            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15649            .into_iter()
15650            .map(|selection| selection.range())
15651            .collect_vec();
15652
15653        let snippet = if let Some(snippet_body) = &action.snippet {
15654            if action.language.is_none() && action.name.is_none() {
15655                Snippet::parse(snippet_body)?
15656            } else {
15657                bail!("`snippet` is mutually exclusive with `language` and `name`")
15658            }
15659        } else if let Some(name) = &action.name {
15660            let project = self.project().context("no project")?;
15661            let snippet_store = project.read(cx).snippets().read(cx);
15662            let snippet = snippet_store
15663                .snippets_for(action.language.clone(), cx)
15664                .into_iter()
15665                .find(|snippet| snippet.name == *name)
15666                .context("snippet not found")?;
15667            Snippet::parse(&snippet.body)?
15668        } else {
15669            // todo(andrew): open modal to select snippet
15670            bail!("`name` or `snippet` is required")
15671        };
15672
15673        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15674    }
15675
15676    fn select_match_ranges(
15677        &mut self,
15678        range: Range<MultiBufferOffset>,
15679        reversed: bool,
15680        replace_newest: bool,
15681        auto_scroll: Option<Autoscroll>,
15682        window: &mut Window,
15683        cx: &mut Context<Editor>,
15684    ) {
15685        self.unfold_ranges(
15686            std::slice::from_ref(&range),
15687            false,
15688            auto_scroll.is_some(),
15689            cx,
15690        );
15691        let effects = if let Some(scroll) = auto_scroll {
15692            SelectionEffects::scroll(scroll)
15693        } else {
15694            SelectionEffects::no_scroll()
15695        };
15696        self.change_selections(effects, window, cx, |s| {
15697            if replace_newest {
15698                s.delete(s.newest_anchor().id);
15699            }
15700            if reversed {
15701                s.insert_range(range.end..range.start);
15702            } else {
15703                s.insert_range(range);
15704            }
15705        });
15706    }
15707
15708    pub fn select_next_match_internal(
15709        &mut self,
15710        display_map: &DisplaySnapshot,
15711        replace_newest: bool,
15712        autoscroll: Option<Autoscroll>,
15713        window: &mut Window,
15714        cx: &mut Context<Self>,
15715    ) -> Result<()> {
15716        let buffer = display_map.buffer_snapshot();
15717        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15718        if let Some(mut select_next_state) = self.select_next_state.take() {
15719            let query = &select_next_state.query;
15720            if !select_next_state.done {
15721                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15722                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15723                let mut next_selected_range = None;
15724
15725                let bytes_after_last_selection =
15726                    buffer.bytes_in_range(last_selection.end..buffer.len());
15727                let bytes_before_first_selection =
15728                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15729                let query_matches = query
15730                    .stream_find_iter(bytes_after_last_selection)
15731                    .map(|result| (last_selection.end, result))
15732                    .chain(
15733                        query
15734                            .stream_find_iter(bytes_before_first_selection)
15735                            .map(|result| (MultiBufferOffset(0), result)),
15736                    );
15737
15738                for (start_offset, query_match) in query_matches {
15739                    let query_match = query_match.unwrap(); // can only fail due to I/O
15740                    let offset_range =
15741                        start_offset + query_match.start()..start_offset + query_match.end();
15742
15743                    if !select_next_state.wordwise
15744                        || (!buffer.is_inside_word(offset_range.start, None)
15745                            && !buffer.is_inside_word(offset_range.end, None))
15746                    {
15747                        let idx = selections
15748                            .partition_point(|selection| selection.end <= offset_range.start);
15749                        let overlaps = selections
15750                            .get(idx)
15751                            .map_or(false, |selection| selection.start < offset_range.end);
15752
15753                        if !overlaps {
15754                            next_selected_range = Some(offset_range);
15755                            break;
15756                        }
15757                    }
15758                }
15759
15760                if let Some(next_selected_range) = next_selected_range {
15761                    self.select_match_ranges(
15762                        next_selected_range,
15763                        last_selection.reversed,
15764                        replace_newest,
15765                        autoscroll,
15766                        window,
15767                        cx,
15768                    );
15769                } else {
15770                    select_next_state.done = true;
15771                }
15772            }
15773
15774            self.select_next_state = Some(select_next_state);
15775        } else {
15776            let mut only_carets = true;
15777            let mut same_text_selected = true;
15778            let mut selected_text = None;
15779
15780            let mut selections_iter = selections.iter().peekable();
15781            while let Some(selection) = selections_iter.next() {
15782                if selection.start != selection.end {
15783                    only_carets = false;
15784                }
15785
15786                if same_text_selected {
15787                    if selected_text.is_none() {
15788                        selected_text =
15789                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15790                    }
15791
15792                    if let Some(next_selection) = selections_iter.peek() {
15793                        if next_selection.len() == selection.len() {
15794                            let next_selected_text = buffer
15795                                .text_for_range(next_selection.range())
15796                                .collect::<String>();
15797                            if Some(next_selected_text) != selected_text {
15798                                same_text_selected = false;
15799                                selected_text = None;
15800                            }
15801                        } else {
15802                            same_text_selected = false;
15803                            selected_text = None;
15804                        }
15805                    }
15806                }
15807            }
15808
15809            if only_carets {
15810                for selection in &mut selections {
15811                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15812                    selection.start = word_range.start;
15813                    selection.end = word_range.end;
15814                    selection.goal = SelectionGoal::None;
15815                    selection.reversed = false;
15816                    self.select_match_ranges(
15817                        selection.start..selection.end,
15818                        selection.reversed,
15819                        replace_newest,
15820                        autoscroll,
15821                        window,
15822                        cx,
15823                    );
15824                }
15825
15826                if selections.len() == 1 {
15827                    let selection = selections
15828                        .last()
15829                        .expect("ensured that there's only one selection");
15830                    let query = buffer
15831                        .text_for_range(selection.start..selection.end)
15832                        .collect::<String>();
15833                    let is_empty = query.is_empty();
15834                    let select_state = SelectNextState {
15835                        query: self.build_query(&[query], cx)?,
15836                        wordwise: true,
15837                        done: is_empty,
15838                    };
15839                    self.select_next_state = Some(select_state);
15840                } else {
15841                    self.select_next_state = None;
15842                }
15843            } else if let Some(selected_text) = selected_text {
15844                self.select_next_state = Some(SelectNextState {
15845                    query: self.build_query(&[selected_text], cx)?,
15846                    wordwise: false,
15847                    done: false,
15848                });
15849                self.select_next_match_internal(
15850                    display_map,
15851                    replace_newest,
15852                    autoscroll,
15853                    window,
15854                    cx,
15855                )?;
15856            }
15857        }
15858        Ok(())
15859    }
15860
15861    pub fn select_all_matches(
15862        &mut self,
15863        _action: &SelectAllMatches,
15864        window: &mut Window,
15865        cx: &mut Context<Self>,
15866    ) -> Result<()> {
15867        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15868
15869        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15870
15871        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15872        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15873        else {
15874            return Ok(());
15875        };
15876
15877        let mut new_selections = Vec::new();
15878
15879        let reversed = self
15880            .selections
15881            .oldest::<MultiBufferOffset>(&display_map)
15882            .reversed;
15883        let buffer = display_map.buffer_snapshot();
15884        let query_matches = select_next_state
15885            .query
15886            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15887
15888        for query_match in query_matches.into_iter() {
15889            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15890            let offset_range = if reversed {
15891                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15892            } else {
15893                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15894            };
15895
15896            if !select_next_state.wordwise
15897                || (!buffer.is_inside_word(offset_range.start, None)
15898                    && !buffer.is_inside_word(offset_range.end, None))
15899            {
15900                new_selections.push(offset_range.start..offset_range.end);
15901            }
15902        }
15903
15904        select_next_state.done = true;
15905
15906        if new_selections.is_empty() {
15907            log::error!("bug: new_selections is empty in select_all_matches");
15908            return Ok(());
15909        }
15910
15911        self.unfold_ranges(&new_selections, false, false, cx);
15912        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15913            selections.select_ranges(new_selections)
15914        });
15915
15916        Ok(())
15917    }
15918
15919    pub fn select_next(
15920        &mut self,
15921        action: &SelectNext,
15922        window: &mut Window,
15923        cx: &mut Context<Self>,
15924    ) -> Result<()> {
15925        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15926        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15927        self.select_next_match_internal(
15928            &display_map,
15929            action.replace_newest,
15930            Some(Autoscroll::newest()),
15931            window,
15932            cx,
15933        )
15934    }
15935
15936    pub fn select_previous(
15937        &mut self,
15938        action: &SelectPrevious,
15939        window: &mut Window,
15940        cx: &mut Context<Self>,
15941    ) -> Result<()> {
15942        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15943        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15944        let buffer = display_map.buffer_snapshot();
15945        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15946        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15947            let query = &select_prev_state.query;
15948            if !select_prev_state.done {
15949                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15950                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15951                let mut next_selected_range = None;
15952                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15953                let bytes_before_last_selection =
15954                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15955                let bytes_after_first_selection =
15956                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15957                let query_matches = query
15958                    .stream_find_iter(bytes_before_last_selection)
15959                    .map(|result| (last_selection.start, result))
15960                    .chain(
15961                        query
15962                            .stream_find_iter(bytes_after_first_selection)
15963                            .map(|result| (buffer.len(), result)),
15964                    );
15965                for (end_offset, query_match) in query_matches {
15966                    let query_match = query_match.unwrap(); // can only fail due to I/O
15967                    let offset_range =
15968                        end_offset - query_match.end()..end_offset - query_match.start();
15969
15970                    if !select_prev_state.wordwise
15971                        || (!buffer.is_inside_word(offset_range.start, None)
15972                            && !buffer.is_inside_word(offset_range.end, None))
15973                    {
15974                        next_selected_range = Some(offset_range);
15975                        break;
15976                    }
15977                }
15978
15979                if let Some(next_selected_range) = next_selected_range {
15980                    self.select_match_ranges(
15981                        next_selected_range,
15982                        last_selection.reversed,
15983                        action.replace_newest,
15984                        Some(Autoscroll::newest()),
15985                        window,
15986                        cx,
15987                    );
15988                } else {
15989                    select_prev_state.done = true;
15990                }
15991            }
15992
15993            self.select_prev_state = Some(select_prev_state);
15994        } else {
15995            let mut only_carets = true;
15996            let mut same_text_selected = true;
15997            let mut selected_text = None;
15998
15999            let mut selections_iter = selections.iter().peekable();
16000            while let Some(selection) = selections_iter.next() {
16001                if selection.start != selection.end {
16002                    only_carets = false;
16003                }
16004
16005                if same_text_selected {
16006                    if selected_text.is_none() {
16007                        selected_text =
16008                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16009                    }
16010
16011                    if let Some(next_selection) = selections_iter.peek() {
16012                        if next_selection.len() == selection.len() {
16013                            let next_selected_text = buffer
16014                                .text_for_range(next_selection.range())
16015                                .collect::<String>();
16016                            if Some(next_selected_text) != selected_text {
16017                                same_text_selected = false;
16018                                selected_text = None;
16019                            }
16020                        } else {
16021                            same_text_selected = false;
16022                            selected_text = None;
16023                        }
16024                    }
16025                }
16026            }
16027
16028            if only_carets {
16029                for selection in &mut selections {
16030                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16031                    selection.start = word_range.start;
16032                    selection.end = word_range.end;
16033                    selection.goal = SelectionGoal::None;
16034                    selection.reversed = false;
16035                    self.select_match_ranges(
16036                        selection.start..selection.end,
16037                        selection.reversed,
16038                        action.replace_newest,
16039                        Some(Autoscroll::newest()),
16040                        window,
16041                        cx,
16042                    );
16043                }
16044                if selections.len() == 1 {
16045                    let selection = selections
16046                        .last()
16047                        .expect("ensured that there's only one selection");
16048                    let query = buffer
16049                        .text_for_range(selection.start..selection.end)
16050                        .collect::<String>();
16051                    let is_empty = query.is_empty();
16052                    let select_state = SelectNextState {
16053                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16054                        wordwise: true,
16055                        done: is_empty,
16056                    };
16057                    self.select_prev_state = Some(select_state);
16058                } else {
16059                    self.select_prev_state = None;
16060                }
16061            } else if let Some(selected_text) = selected_text {
16062                self.select_prev_state = Some(SelectNextState {
16063                    query: self
16064                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16065                    wordwise: false,
16066                    done: false,
16067                });
16068                self.select_previous(action, window, cx)?;
16069            }
16070        }
16071        Ok(())
16072    }
16073
16074    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16075    /// setting the case sensitivity based on the global
16076    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16077    /// editor's settings.
16078    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16079    where
16080        I: IntoIterator<Item = P>,
16081        P: AsRef<[u8]>,
16082    {
16083        let case_sensitive = self
16084            .select_next_is_case_sensitive
16085            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16086
16087        let mut builder = AhoCorasickBuilder::new();
16088        builder.ascii_case_insensitive(!case_sensitive);
16089        builder.build(patterns)
16090    }
16091
16092    pub fn find_next_match(
16093        &mut self,
16094        _: &FindNextMatch,
16095        window: &mut Window,
16096        cx: &mut Context<Self>,
16097    ) -> Result<()> {
16098        let selections = self.selections.disjoint_anchors_arc();
16099        match selections.first() {
16100            Some(first) if selections.len() >= 2 => {
16101                self.change_selections(Default::default(), window, cx, |s| {
16102                    s.select_ranges([first.range()]);
16103                });
16104            }
16105            _ => self.select_next(
16106                &SelectNext {
16107                    replace_newest: true,
16108                },
16109                window,
16110                cx,
16111            )?,
16112        }
16113        Ok(())
16114    }
16115
16116    pub fn find_previous_match(
16117        &mut self,
16118        _: &FindPreviousMatch,
16119        window: &mut Window,
16120        cx: &mut Context<Self>,
16121    ) -> Result<()> {
16122        let selections = self.selections.disjoint_anchors_arc();
16123        match selections.last() {
16124            Some(last) if selections.len() >= 2 => {
16125                self.change_selections(Default::default(), window, cx, |s| {
16126                    s.select_ranges([last.range()]);
16127                });
16128            }
16129            _ => self.select_previous(
16130                &SelectPrevious {
16131                    replace_newest: true,
16132                },
16133                window,
16134                cx,
16135            )?,
16136        }
16137        Ok(())
16138    }
16139
16140    pub fn toggle_comments(
16141        &mut self,
16142        action: &ToggleComments,
16143        window: &mut Window,
16144        cx: &mut Context<Self>,
16145    ) {
16146        if self.read_only(cx) {
16147            return;
16148        }
16149        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16150        let text_layout_details = &self.text_layout_details(window, cx);
16151        self.transact(window, cx, |this, window, cx| {
16152            let mut selections = this
16153                .selections
16154                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16155            let mut edits = Vec::new();
16156            let mut selection_edit_ranges = Vec::new();
16157            let mut last_toggled_row = None;
16158            let snapshot = this.buffer.read(cx).read(cx);
16159            let empty_str: Arc<str> = Arc::default();
16160            let mut suffixes_inserted = Vec::new();
16161            let ignore_indent = action.ignore_indent;
16162
16163            fn comment_prefix_range(
16164                snapshot: &MultiBufferSnapshot,
16165                row: MultiBufferRow,
16166                comment_prefix: &str,
16167                comment_prefix_whitespace: &str,
16168                ignore_indent: bool,
16169            ) -> Range<Point> {
16170                let indent_size = if ignore_indent {
16171                    0
16172                } else {
16173                    snapshot.indent_size_for_line(row).len
16174                };
16175
16176                let start = Point::new(row.0, indent_size);
16177
16178                let mut line_bytes = snapshot
16179                    .bytes_in_range(start..snapshot.max_point())
16180                    .flatten()
16181                    .copied();
16182
16183                // If this line currently begins with the line comment prefix, then record
16184                // the range containing the prefix.
16185                if line_bytes
16186                    .by_ref()
16187                    .take(comment_prefix.len())
16188                    .eq(comment_prefix.bytes())
16189                {
16190                    // Include any whitespace that matches the comment prefix.
16191                    let matching_whitespace_len = line_bytes
16192                        .zip(comment_prefix_whitespace.bytes())
16193                        .take_while(|(a, b)| a == b)
16194                        .count() as u32;
16195                    let end = Point::new(
16196                        start.row,
16197                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16198                    );
16199                    start..end
16200                } else {
16201                    start..start
16202                }
16203            }
16204
16205            fn comment_suffix_range(
16206                snapshot: &MultiBufferSnapshot,
16207                row: MultiBufferRow,
16208                comment_suffix: &str,
16209                comment_suffix_has_leading_space: bool,
16210            ) -> Range<Point> {
16211                let end = Point::new(row.0, snapshot.line_len(row));
16212                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16213
16214                let mut line_end_bytes = snapshot
16215                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16216                    .flatten()
16217                    .copied();
16218
16219                let leading_space_len = if suffix_start_column > 0
16220                    && line_end_bytes.next() == Some(b' ')
16221                    && comment_suffix_has_leading_space
16222                {
16223                    1
16224                } else {
16225                    0
16226                };
16227
16228                // If this line currently begins with the line comment prefix, then record
16229                // the range containing the prefix.
16230                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16231                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16232                    start..end
16233                } else {
16234                    end..end
16235                }
16236            }
16237
16238            // TODO: Handle selections that cross excerpts
16239            for selection in &mut selections {
16240                let start_column = snapshot
16241                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16242                    .len;
16243                let language = if let Some(language) =
16244                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16245                {
16246                    language
16247                } else {
16248                    continue;
16249                };
16250
16251                selection_edit_ranges.clear();
16252
16253                // If multiple selections contain a given row, avoid processing that
16254                // row more than once.
16255                let mut start_row = MultiBufferRow(selection.start.row);
16256                if last_toggled_row == Some(start_row) {
16257                    start_row = start_row.next_row();
16258                }
16259                let end_row =
16260                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16261                        MultiBufferRow(selection.end.row - 1)
16262                    } else {
16263                        MultiBufferRow(selection.end.row)
16264                    };
16265                last_toggled_row = Some(end_row);
16266
16267                if start_row > end_row {
16268                    continue;
16269                }
16270
16271                // If the language has line comments, toggle those.
16272                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16273
16274                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16275                if ignore_indent {
16276                    full_comment_prefixes = full_comment_prefixes
16277                        .into_iter()
16278                        .map(|s| Arc::from(s.trim_end()))
16279                        .collect();
16280                }
16281
16282                if !full_comment_prefixes.is_empty() {
16283                    let first_prefix = full_comment_prefixes
16284                        .first()
16285                        .expect("prefixes is non-empty");
16286                    let prefix_trimmed_lengths = full_comment_prefixes
16287                        .iter()
16288                        .map(|p| p.trim_end_matches(' ').len())
16289                        .collect::<SmallVec<[usize; 4]>>();
16290
16291                    let mut all_selection_lines_are_comments = true;
16292
16293                    for row in start_row.0..=end_row.0 {
16294                        let row = MultiBufferRow(row);
16295                        if start_row < end_row && snapshot.is_line_blank(row) {
16296                            continue;
16297                        }
16298
16299                        let prefix_range = full_comment_prefixes
16300                            .iter()
16301                            .zip(prefix_trimmed_lengths.iter().copied())
16302                            .map(|(prefix, trimmed_prefix_len)| {
16303                                comment_prefix_range(
16304                                    snapshot.deref(),
16305                                    row,
16306                                    &prefix[..trimmed_prefix_len],
16307                                    &prefix[trimmed_prefix_len..],
16308                                    ignore_indent,
16309                                )
16310                            })
16311                            .max_by_key(|range| range.end.column - range.start.column)
16312                            .expect("prefixes is non-empty");
16313
16314                        if prefix_range.is_empty() {
16315                            all_selection_lines_are_comments = false;
16316                        }
16317
16318                        selection_edit_ranges.push(prefix_range);
16319                    }
16320
16321                    if all_selection_lines_are_comments {
16322                        edits.extend(
16323                            selection_edit_ranges
16324                                .iter()
16325                                .cloned()
16326                                .map(|range| (range, empty_str.clone())),
16327                        );
16328                    } else {
16329                        let min_column = selection_edit_ranges
16330                            .iter()
16331                            .map(|range| range.start.column)
16332                            .min()
16333                            .unwrap_or(0);
16334                        edits.extend(selection_edit_ranges.iter().map(|range| {
16335                            let position = Point::new(range.start.row, min_column);
16336                            (position..position, first_prefix.clone())
16337                        }));
16338                    }
16339                } else if let Some(BlockCommentConfig {
16340                    start: full_comment_prefix,
16341                    end: comment_suffix,
16342                    ..
16343                }) = language.block_comment()
16344                {
16345                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16346                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16347                    let prefix_range = comment_prefix_range(
16348                        snapshot.deref(),
16349                        start_row,
16350                        comment_prefix,
16351                        comment_prefix_whitespace,
16352                        ignore_indent,
16353                    );
16354                    let suffix_range = comment_suffix_range(
16355                        snapshot.deref(),
16356                        end_row,
16357                        comment_suffix.trim_start_matches(' '),
16358                        comment_suffix.starts_with(' '),
16359                    );
16360
16361                    if prefix_range.is_empty() || suffix_range.is_empty() {
16362                        edits.push((
16363                            prefix_range.start..prefix_range.start,
16364                            full_comment_prefix.clone(),
16365                        ));
16366                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16367                        suffixes_inserted.push((end_row, comment_suffix.len()));
16368                    } else {
16369                        edits.push((prefix_range, empty_str.clone()));
16370                        edits.push((suffix_range, empty_str.clone()));
16371                    }
16372                } else {
16373                    continue;
16374                }
16375            }
16376
16377            drop(snapshot);
16378            this.buffer.update(cx, |buffer, cx| {
16379                buffer.edit(edits, None, cx);
16380            });
16381
16382            // Adjust selections so that they end before any comment suffixes that
16383            // were inserted.
16384            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16385            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16386            let snapshot = this.buffer.read(cx).read(cx);
16387            for selection in &mut selections {
16388                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16389                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16390                        Ordering::Less => {
16391                            suffixes_inserted.next();
16392                            continue;
16393                        }
16394                        Ordering::Greater => break,
16395                        Ordering::Equal => {
16396                            if selection.end.column == snapshot.line_len(row) {
16397                                if selection.is_empty() {
16398                                    selection.start.column -= suffix_len as u32;
16399                                }
16400                                selection.end.column -= suffix_len as u32;
16401                            }
16402                            break;
16403                        }
16404                    }
16405                }
16406            }
16407
16408            drop(snapshot);
16409            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16410
16411            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16412            let selections_on_single_row = selections.windows(2).all(|selections| {
16413                selections[0].start.row == selections[1].start.row
16414                    && selections[0].end.row == selections[1].end.row
16415                    && selections[0].start.row == selections[0].end.row
16416            });
16417            let selections_selecting = selections
16418                .iter()
16419                .any(|selection| selection.start != selection.end);
16420            let advance_downwards = action.advance_downwards
16421                && selections_on_single_row
16422                && !selections_selecting
16423                && !matches!(this.mode, EditorMode::SingleLine);
16424
16425            if advance_downwards {
16426                let snapshot = this.buffer.read(cx).snapshot(cx);
16427
16428                this.change_selections(Default::default(), window, cx, |s| {
16429                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16430                        let mut point = display_point.to_point(display_snapshot);
16431                        point.row += 1;
16432                        point = snapshot.clip_point(point, Bias::Left);
16433                        let display_point = point.to_display_point(display_snapshot);
16434                        let goal = SelectionGoal::HorizontalPosition(
16435                            display_snapshot
16436                                .x_for_display_point(display_point, text_layout_details)
16437                                .into(),
16438                        );
16439                        (display_point, goal)
16440                    })
16441                });
16442            }
16443        });
16444    }
16445
16446    pub fn select_enclosing_symbol(
16447        &mut self,
16448        _: &SelectEnclosingSymbol,
16449        window: &mut Window,
16450        cx: &mut Context<Self>,
16451    ) {
16452        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16453
16454        let buffer = self.buffer.read(cx).snapshot(cx);
16455        let old_selections = self
16456            .selections
16457            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16458            .into_boxed_slice();
16459
16460        fn update_selection(
16461            selection: &Selection<MultiBufferOffset>,
16462            buffer_snap: &MultiBufferSnapshot,
16463        ) -> Option<Selection<MultiBufferOffset>> {
16464            let cursor = selection.head();
16465            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16466            for symbol in symbols.iter().rev() {
16467                let start = symbol.range.start.to_offset(buffer_snap);
16468                let end = symbol.range.end.to_offset(buffer_snap);
16469                let new_range = start..end;
16470                if start < selection.start || end > selection.end {
16471                    return Some(Selection {
16472                        id: selection.id,
16473                        start: new_range.start,
16474                        end: new_range.end,
16475                        goal: SelectionGoal::None,
16476                        reversed: selection.reversed,
16477                    });
16478                }
16479            }
16480            None
16481        }
16482
16483        let mut selected_larger_symbol = false;
16484        let new_selections = old_selections
16485            .iter()
16486            .map(|selection| match update_selection(selection, &buffer) {
16487                Some(new_selection) => {
16488                    if new_selection.range() != selection.range() {
16489                        selected_larger_symbol = true;
16490                    }
16491                    new_selection
16492                }
16493                None => selection.clone(),
16494            })
16495            .collect::<Vec<_>>();
16496
16497        if selected_larger_symbol {
16498            self.change_selections(Default::default(), window, cx, |s| {
16499                s.select(new_selections);
16500            });
16501        }
16502    }
16503
16504    pub fn select_larger_syntax_node(
16505        &mut self,
16506        _: &SelectLargerSyntaxNode,
16507        window: &mut Window,
16508        cx: &mut Context<Self>,
16509    ) {
16510        let Some(visible_row_count) = self.visible_row_count() else {
16511            return;
16512        };
16513        let old_selections: Box<[_]> = self
16514            .selections
16515            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16516            .into();
16517        if old_selections.is_empty() {
16518            return;
16519        }
16520
16521        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16522
16523        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16524        let buffer = self.buffer.read(cx).snapshot(cx);
16525
16526        let mut selected_larger_node = false;
16527        let mut new_selections = old_selections
16528            .iter()
16529            .map(|selection| {
16530                let old_range = selection.start..selection.end;
16531
16532                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16533                    // manually select word at selection
16534                    if ["string_content", "inline"].contains(&node.kind()) {
16535                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16536                        // ignore if word is already selected
16537                        if !word_range.is_empty() && old_range != word_range {
16538                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16539                            // only select word if start and end point belongs to same word
16540                            if word_range == last_word_range {
16541                                selected_larger_node = true;
16542                                return Selection {
16543                                    id: selection.id,
16544                                    start: word_range.start,
16545                                    end: word_range.end,
16546                                    goal: SelectionGoal::None,
16547                                    reversed: selection.reversed,
16548                                };
16549                            }
16550                        }
16551                    }
16552                }
16553
16554                let mut new_range = old_range.clone();
16555                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16556                    new_range = range;
16557                    if !node.is_named() {
16558                        continue;
16559                    }
16560                    if !display_map.intersects_fold(new_range.start)
16561                        && !display_map.intersects_fold(new_range.end)
16562                    {
16563                        break;
16564                    }
16565                }
16566
16567                selected_larger_node |= new_range != old_range;
16568                Selection {
16569                    id: selection.id,
16570                    start: new_range.start,
16571                    end: new_range.end,
16572                    goal: SelectionGoal::None,
16573                    reversed: selection.reversed,
16574                }
16575            })
16576            .collect::<Vec<_>>();
16577
16578        if !selected_larger_node {
16579            return; // don't put this call in the history
16580        }
16581
16582        // scroll based on transformation done to the last selection created by the user
16583        let (last_old, last_new) = old_selections
16584            .last()
16585            .zip(new_selections.last().cloned())
16586            .expect("old_selections isn't empty");
16587
16588        // revert selection
16589        let is_selection_reversed = {
16590            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16591            new_selections.last_mut().expect("checked above").reversed =
16592                should_newest_selection_be_reversed;
16593            should_newest_selection_be_reversed
16594        };
16595
16596        if selected_larger_node {
16597            self.select_syntax_node_history.disable_clearing = true;
16598            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16599                s.select(new_selections.clone());
16600            });
16601            self.select_syntax_node_history.disable_clearing = false;
16602        }
16603
16604        let start_row = last_new.start.to_display_point(&display_map).row().0;
16605        let end_row = last_new.end.to_display_point(&display_map).row().0;
16606        let selection_height = end_row - start_row + 1;
16607        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16608
16609        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16610        let scroll_behavior = if fits_on_the_screen {
16611            self.request_autoscroll(Autoscroll::fit(), cx);
16612            SelectSyntaxNodeScrollBehavior::FitSelection
16613        } else if is_selection_reversed {
16614            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16615            SelectSyntaxNodeScrollBehavior::CursorTop
16616        } else {
16617            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16618            SelectSyntaxNodeScrollBehavior::CursorBottom
16619        };
16620
16621        let old_selections: Box<[Selection<Anchor>]> = old_selections
16622            .iter()
16623            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16624            .collect();
16625        self.select_syntax_node_history.push((
16626            old_selections,
16627            scroll_behavior,
16628            is_selection_reversed,
16629        ));
16630    }
16631
16632    pub fn select_smaller_syntax_node(
16633        &mut self,
16634        _: &SelectSmallerSyntaxNode,
16635        window: &mut Window,
16636        cx: &mut Context<Self>,
16637    ) {
16638        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16639
16640        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16641            self.select_syntax_node_history.pop()
16642        {
16643            if let Some(selection) = selections.last_mut() {
16644                selection.reversed = is_selection_reversed;
16645            }
16646
16647            let snapshot = self.buffer.read(cx).snapshot(cx);
16648            let selections: Vec<Selection<MultiBufferOffset>> = selections
16649                .iter()
16650                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16651                .collect();
16652
16653            self.select_syntax_node_history.disable_clearing = true;
16654            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16655                s.select(selections);
16656            });
16657            self.select_syntax_node_history.disable_clearing = false;
16658
16659            match scroll_behavior {
16660                SelectSyntaxNodeScrollBehavior::CursorTop => {
16661                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16662                }
16663                SelectSyntaxNodeScrollBehavior::FitSelection => {
16664                    self.request_autoscroll(Autoscroll::fit(), cx);
16665                }
16666                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16667                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16668                }
16669            }
16670        }
16671    }
16672
16673    pub fn unwrap_syntax_node(
16674        &mut self,
16675        _: &UnwrapSyntaxNode,
16676        window: &mut Window,
16677        cx: &mut Context<Self>,
16678    ) {
16679        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16680
16681        let buffer = self.buffer.read(cx).snapshot(cx);
16682        let selections = self
16683            .selections
16684            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16685            .into_iter()
16686            // subtracting the offset requires sorting
16687            .sorted_by_key(|i| i.start);
16688
16689        let full_edits = selections
16690            .into_iter()
16691            .filter_map(|selection| {
16692                let child = if selection.is_empty()
16693                    && let Some((_, ancestor_range)) =
16694                        buffer.syntax_ancestor(selection.start..selection.end)
16695                {
16696                    ancestor_range
16697                } else {
16698                    selection.range()
16699                };
16700
16701                let mut parent = child.clone();
16702                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16703                    parent = ancestor_range;
16704                    if parent.start < child.start || parent.end > child.end {
16705                        break;
16706                    }
16707                }
16708
16709                if parent == child {
16710                    return None;
16711                }
16712                let text = buffer.text_for_range(child).collect::<String>();
16713                Some((selection.id, parent, text))
16714            })
16715            .collect::<Vec<_>>();
16716        if full_edits.is_empty() {
16717            return;
16718        }
16719
16720        self.transact(window, cx, |this, window, cx| {
16721            this.buffer.update(cx, |buffer, cx| {
16722                buffer.edit(
16723                    full_edits
16724                        .iter()
16725                        .map(|(_, p, t)| (p.clone(), t.clone()))
16726                        .collect::<Vec<_>>(),
16727                    None,
16728                    cx,
16729                );
16730            });
16731            this.change_selections(Default::default(), window, cx, |s| {
16732                let mut offset = 0;
16733                let mut selections = vec![];
16734                for (id, parent, text) in full_edits {
16735                    let start = parent.start - offset;
16736                    offset += (parent.end - parent.start) - text.len();
16737                    selections.push(Selection {
16738                        id,
16739                        start,
16740                        end: start + text.len(),
16741                        reversed: false,
16742                        goal: Default::default(),
16743                    });
16744                }
16745                s.select(selections);
16746            });
16747        });
16748    }
16749
16750    pub fn select_next_syntax_node(
16751        &mut self,
16752        _: &SelectNextSyntaxNode,
16753        window: &mut Window,
16754        cx: &mut Context<Self>,
16755    ) {
16756        let old_selections: Box<[_]> = self
16757            .selections
16758            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16759            .into();
16760        if old_selections.is_empty() {
16761            return;
16762        }
16763
16764        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16765
16766        let buffer = self.buffer.read(cx).snapshot(cx);
16767        let mut selected_sibling = false;
16768
16769        let new_selections = old_selections
16770            .iter()
16771            .map(|selection| {
16772                let old_range = selection.start..selection.end;
16773
16774                let old_range =
16775                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16776                let excerpt = buffer.excerpt_containing(old_range.clone());
16777
16778                if let Some(mut excerpt) = excerpt
16779                    && let Some(node) = excerpt
16780                        .buffer()
16781                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16782                {
16783                    let new_range = excerpt.map_range_from_buffer(
16784                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16785                    );
16786                    selected_sibling = true;
16787                    Selection {
16788                        id: selection.id,
16789                        start: new_range.start,
16790                        end: new_range.end,
16791                        goal: SelectionGoal::None,
16792                        reversed: selection.reversed,
16793                    }
16794                } else {
16795                    selection.clone()
16796                }
16797            })
16798            .collect::<Vec<_>>();
16799
16800        if selected_sibling {
16801            self.change_selections(
16802                SelectionEffects::scroll(Autoscroll::fit()),
16803                window,
16804                cx,
16805                |s| {
16806                    s.select(new_selections);
16807                },
16808            );
16809        }
16810    }
16811
16812    pub fn select_prev_syntax_node(
16813        &mut self,
16814        _: &SelectPreviousSyntaxNode,
16815        window: &mut Window,
16816        cx: &mut Context<Self>,
16817    ) {
16818        let old_selections: Box<[_]> = self
16819            .selections
16820            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16821            .into();
16822        if old_selections.is_empty() {
16823            return;
16824        }
16825
16826        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16827
16828        let buffer = self.buffer.read(cx).snapshot(cx);
16829        let mut selected_sibling = false;
16830
16831        let new_selections = old_selections
16832            .iter()
16833            .map(|selection| {
16834                let old_range = selection.start..selection.end;
16835                let old_range =
16836                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16837                let excerpt = buffer.excerpt_containing(old_range.clone());
16838
16839                if let Some(mut excerpt) = excerpt
16840                    && let Some(node) = excerpt
16841                        .buffer()
16842                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16843                {
16844                    let new_range = excerpt.map_range_from_buffer(
16845                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16846                    );
16847                    selected_sibling = true;
16848                    Selection {
16849                        id: selection.id,
16850                        start: new_range.start,
16851                        end: new_range.end,
16852                        goal: SelectionGoal::None,
16853                        reversed: selection.reversed,
16854                    }
16855                } else {
16856                    selection.clone()
16857                }
16858            })
16859            .collect::<Vec<_>>();
16860
16861        if selected_sibling {
16862            self.change_selections(
16863                SelectionEffects::scroll(Autoscroll::fit()),
16864                window,
16865                cx,
16866                |s| {
16867                    s.select(new_selections);
16868                },
16869            );
16870        }
16871    }
16872
16873    pub fn move_to_start_of_larger_syntax_node(
16874        &mut self,
16875        _: &MoveToStartOfLargerSyntaxNode,
16876        window: &mut Window,
16877        cx: &mut Context<Self>,
16878    ) {
16879        self.move_cursors_to_syntax_nodes(window, cx, false);
16880    }
16881
16882    pub fn move_to_end_of_larger_syntax_node(
16883        &mut self,
16884        _: &MoveToEndOfLargerSyntaxNode,
16885        window: &mut Window,
16886        cx: &mut Context<Self>,
16887    ) {
16888        self.move_cursors_to_syntax_nodes(window, cx, true);
16889    }
16890
16891    fn find_syntax_node_boundary(
16892        &self,
16893        selection_pos: MultiBufferOffset,
16894        move_to_end: bool,
16895        display_map: &DisplaySnapshot,
16896        buffer: &MultiBufferSnapshot,
16897    ) -> MultiBufferOffset {
16898        let old_range = selection_pos..selection_pos;
16899        let mut new_pos = selection_pos;
16900        let mut search_range = old_range;
16901        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16902            search_range = range.clone();
16903            if !node.is_named()
16904                || display_map.intersects_fold(range.start)
16905                || display_map.intersects_fold(range.end)
16906                // If cursor is already at the end of the syntax node, continue searching
16907                || (move_to_end && range.end == selection_pos)
16908                // If cursor is already at the start of the syntax node, continue searching
16909                || (!move_to_end && range.start == selection_pos)
16910            {
16911                continue;
16912            }
16913
16914            // If we found a string_content node, find the largest parent that is still string_content
16915            // Enables us to skip to the end of strings without taking multiple steps inside the string
16916            let (_, final_range) = if node.kind() == "string_content" {
16917                let mut current_node = node;
16918                let mut current_range = range;
16919                while let Some((parent, parent_range)) =
16920                    buffer.syntax_ancestor(current_range.clone())
16921                {
16922                    if parent.kind() == "string_content" {
16923                        current_node = parent;
16924                        current_range = parent_range;
16925                    } else {
16926                        break;
16927                    }
16928                }
16929
16930                (current_node, current_range)
16931            } else {
16932                (node, range)
16933            };
16934
16935            new_pos = if move_to_end {
16936                final_range.end
16937            } else {
16938                final_range.start
16939            };
16940
16941            break;
16942        }
16943
16944        new_pos
16945    }
16946
16947    fn move_cursors_to_syntax_nodes(
16948        &mut self,
16949        window: &mut Window,
16950        cx: &mut Context<Self>,
16951        move_to_end: bool,
16952    ) -> bool {
16953        let old_selections: Box<[_]> = self
16954            .selections
16955            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16956            .into();
16957        if old_selections.is_empty() {
16958            return false;
16959        }
16960
16961        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16962
16963        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16964        let buffer = self.buffer.read(cx).snapshot(cx);
16965
16966        let mut any_cursor_moved = false;
16967        let new_selections = old_selections
16968            .iter()
16969            .map(|selection| {
16970                if !selection.is_empty() {
16971                    return selection.clone();
16972                }
16973
16974                let selection_pos = selection.head();
16975                let new_pos = self.find_syntax_node_boundary(
16976                    selection_pos,
16977                    move_to_end,
16978                    &display_map,
16979                    &buffer,
16980                );
16981
16982                any_cursor_moved |= new_pos != selection_pos;
16983
16984                Selection {
16985                    id: selection.id,
16986                    start: new_pos,
16987                    end: new_pos,
16988                    goal: SelectionGoal::None,
16989                    reversed: false,
16990                }
16991            })
16992            .collect::<Vec<_>>();
16993
16994        self.change_selections(Default::default(), window, cx, |s| {
16995            s.select(new_selections);
16996        });
16997        self.request_autoscroll(Autoscroll::newest(), cx);
16998
16999        any_cursor_moved
17000    }
17001
17002    pub fn select_to_start_of_larger_syntax_node(
17003        &mut self,
17004        _: &SelectToStartOfLargerSyntaxNode,
17005        window: &mut Window,
17006        cx: &mut Context<Self>,
17007    ) {
17008        self.select_to_syntax_nodes(window, cx, false);
17009    }
17010
17011    pub fn select_to_end_of_larger_syntax_node(
17012        &mut self,
17013        _: &SelectToEndOfLargerSyntaxNode,
17014        window: &mut Window,
17015        cx: &mut Context<Self>,
17016    ) {
17017        self.select_to_syntax_nodes(window, cx, true);
17018    }
17019
17020    fn select_to_syntax_nodes(
17021        &mut self,
17022        window: &mut Window,
17023        cx: &mut Context<Self>,
17024        move_to_end: bool,
17025    ) {
17026        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17027
17028        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17029        let buffer = self.buffer.read(cx).snapshot(cx);
17030        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17031
17032        let new_selections = old_selections
17033            .iter()
17034            .map(|selection| {
17035                let new_pos = self.find_syntax_node_boundary(
17036                    selection.head(),
17037                    move_to_end,
17038                    &display_map,
17039                    &buffer,
17040                );
17041
17042                let mut new_selection = selection.clone();
17043                new_selection.set_head(new_pos, SelectionGoal::None);
17044                new_selection
17045            })
17046            .collect::<Vec<_>>();
17047
17048        self.change_selections(Default::default(), window, cx, |s| {
17049            s.select(new_selections);
17050        });
17051    }
17052
17053    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17054        if !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables {
17055            self.clear_tasks();
17056            return Task::ready(());
17057        }
17058        let project = self.project().map(Entity::downgrade);
17059        let task_sources = self.lsp_task_sources(cx);
17060        let multi_buffer = self.buffer.downgrade();
17061        cx.spawn_in(window, async move |editor, cx| {
17062            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17063            let Some(project) = project.and_then(|p| p.upgrade()) else {
17064                return;
17065            };
17066            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17067                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17068            }) else {
17069                return;
17070            };
17071
17072            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17073            if hide_runnables {
17074                return;
17075            }
17076            let new_rows =
17077                cx.background_spawn({
17078                    let snapshot = display_snapshot.clone();
17079                    async move {
17080                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
17081                    }
17082                })
17083                    .await;
17084            let Ok(lsp_tasks) =
17085                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17086            else {
17087                return;
17088            };
17089            let lsp_tasks = lsp_tasks.await;
17090
17091            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17092                lsp_tasks
17093                    .into_iter()
17094                    .flat_map(|(kind, tasks)| {
17095                        tasks.into_iter().filter_map(move |(location, task)| {
17096                            Some((kind.clone(), location?, task))
17097                        })
17098                    })
17099                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17100                        let buffer = location.target.buffer;
17101                        let buffer_snapshot = buffer.read(cx).snapshot();
17102                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17103                            |(excerpt_id, snapshot, _)| {
17104                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17105                                    display_snapshot
17106                                        .buffer_snapshot()
17107                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17108                                } else {
17109                                    None
17110                                }
17111                            },
17112                        );
17113                        if let Some(offset) = offset {
17114                            let task_buffer_range =
17115                                location.target.range.to_point(&buffer_snapshot);
17116                            let context_buffer_range =
17117                                task_buffer_range.to_offset(&buffer_snapshot);
17118                            let context_range = BufferOffset(context_buffer_range.start)
17119                                ..BufferOffset(context_buffer_range.end);
17120
17121                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17122                                .or_insert_with(|| RunnableTasks {
17123                                    templates: Vec::new(),
17124                                    offset,
17125                                    column: task_buffer_range.start.column,
17126                                    extra_variables: HashMap::default(),
17127                                    context_range,
17128                                })
17129                                .templates
17130                                .push((kind, task.original_task().clone()));
17131                        }
17132
17133                        acc
17134                    })
17135            }) else {
17136                return;
17137            };
17138
17139            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17140                buffer.language_settings(cx).tasks.prefer_lsp
17141            }) else {
17142                return;
17143            };
17144
17145            let rows = Self::runnable_rows(
17146                project,
17147                display_snapshot,
17148                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17149                new_rows,
17150                cx.clone(),
17151            )
17152            .await;
17153            editor
17154                .update(cx, |editor, _| {
17155                    editor.clear_tasks();
17156                    for (key, mut value) in rows {
17157                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17158                            value.templates.extend(lsp_tasks.templates);
17159                        }
17160
17161                        editor.insert_tasks(key, value);
17162                    }
17163                    for (key, value) in lsp_tasks_by_rows {
17164                        editor.insert_tasks(key, value);
17165                    }
17166                })
17167                .ok();
17168        })
17169    }
17170    fn fetch_runnable_ranges(
17171        snapshot: &DisplaySnapshot,
17172        range: Range<Anchor>,
17173    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
17174        snapshot.buffer_snapshot().runnable_ranges(range).collect()
17175    }
17176
17177    fn runnable_rows(
17178        project: Entity<Project>,
17179        snapshot: DisplaySnapshot,
17180        prefer_lsp: bool,
17181        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17182        cx: AsyncWindowContext,
17183    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17184        cx.spawn(async move |cx| {
17185            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17186            for (run_range, mut runnable) in runnable_ranges {
17187                let Some(tasks) = cx
17188                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17189                    .ok()
17190                else {
17191                    continue;
17192                };
17193                let mut tasks = tasks.await;
17194
17195                if prefer_lsp {
17196                    tasks.retain(|(task_kind, _)| {
17197                        !matches!(task_kind, TaskSourceKind::Language { .. })
17198                    });
17199                }
17200                if tasks.is_empty() {
17201                    continue;
17202                }
17203
17204                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17205                let Some(row) = snapshot
17206                    .buffer_snapshot()
17207                    .buffer_line_for_row(MultiBufferRow(point.row))
17208                    .map(|(_, range)| range.start.row)
17209                else {
17210                    continue;
17211                };
17212
17213                let context_range =
17214                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17215                runnable_rows.push((
17216                    (runnable.buffer_id, row),
17217                    RunnableTasks {
17218                        templates: tasks,
17219                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17220                        context_range,
17221                        column: point.column,
17222                        extra_variables: runnable.extra_captures,
17223                    },
17224                ));
17225            }
17226            runnable_rows
17227        })
17228    }
17229
17230    fn templates_with_tags(
17231        project: &Entity<Project>,
17232        runnable: &mut Runnable,
17233        cx: &mut App,
17234    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17235        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17236            let (worktree_id, file) = project
17237                .buffer_for_id(runnable.buffer, cx)
17238                .and_then(|buffer| buffer.read(cx).file())
17239                .map(|file| (file.worktree_id(cx), file.clone()))
17240                .unzip();
17241
17242            (
17243                project.task_store().read(cx).task_inventory().cloned(),
17244                worktree_id,
17245                file,
17246            )
17247        });
17248
17249        let tags = mem::take(&mut runnable.tags);
17250        let language = runnable.language.clone();
17251        cx.spawn(async move |cx| {
17252            let mut templates_with_tags = Vec::new();
17253            if let Some(inventory) = inventory {
17254                for RunnableTag(tag) in tags {
17255                    let new_tasks = inventory.update(cx, |inventory, cx| {
17256                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17257                    });
17258                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17259                        move |(_, template)| {
17260                            template.tags.iter().any(|source_tag| source_tag == &tag)
17261                        },
17262                    ));
17263                }
17264            }
17265            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17266
17267            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17268                // Strongest source wins; if we have worktree tag binding, prefer that to
17269                // global and language bindings;
17270                // if we have a global binding, prefer that to language binding.
17271                let first_mismatch = templates_with_tags
17272                    .iter()
17273                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17274                if let Some(index) = first_mismatch {
17275                    templates_with_tags.truncate(index);
17276                }
17277            }
17278
17279            templates_with_tags
17280        })
17281    }
17282
17283    pub fn move_to_enclosing_bracket(
17284        &mut self,
17285        _: &MoveToEnclosingBracket,
17286        window: &mut Window,
17287        cx: &mut Context<Self>,
17288    ) {
17289        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17290        self.change_selections(Default::default(), window, cx, |s| {
17291            s.move_offsets_with(&mut |snapshot, selection| {
17292                let Some(enclosing_bracket_ranges) =
17293                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17294                else {
17295                    return;
17296                };
17297
17298                let mut best_length = usize::MAX;
17299                let mut best_inside = false;
17300                let mut best_in_bracket_range = false;
17301                let mut best_destination = None;
17302                for (open, close) in enclosing_bracket_ranges {
17303                    let close = close.to_inclusive();
17304                    let length = *close.end() - open.start;
17305                    let inside = selection.start >= open.end && selection.end <= *close.start();
17306                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17307                        || close.contains(&selection.head());
17308
17309                    // If best is next to a bracket and current isn't, skip
17310                    if !in_bracket_range && best_in_bracket_range {
17311                        continue;
17312                    }
17313
17314                    // Prefer smaller lengths unless best is inside and current isn't
17315                    if length > best_length && (best_inside || !inside) {
17316                        continue;
17317                    }
17318
17319                    best_length = length;
17320                    best_inside = inside;
17321                    best_in_bracket_range = in_bracket_range;
17322                    best_destination = Some(
17323                        if close.contains(&selection.start) && close.contains(&selection.end) {
17324                            if inside { open.end } else { open.start }
17325                        } else if inside {
17326                            *close.start()
17327                        } else {
17328                            *close.end()
17329                        },
17330                    );
17331                }
17332
17333                if let Some(destination) = best_destination {
17334                    selection.collapse_to(destination, SelectionGoal::None);
17335                }
17336            })
17337        });
17338    }
17339
17340    pub fn undo_selection(
17341        &mut self,
17342        _: &UndoSelection,
17343        window: &mut Window,
17344        cx: &mut Context<Self>,
17345    ) {
17346        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17347        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17348            self.selection_history.mode = SelectionHistoryMode::Undoing;
17349            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17350                this.end_selection(window, cx);
17351                this.change_selections(
17352                    SelectionEffects::scroll(Autoscroll::newest()),
17353                    window,
17354                    cx,
17355                    |s| s.select_anchors(entry.selections.to_vec()),
17356                );
17357            });
17358            self.selection_history.mode = SelectionHistoryMode::Normal;
17359
17360            self.select_next_state = entry.select_next_state;
17361            self.select_prev_state = entry.select_prev_state;
17362            self.add_selections_state = entry.add_selections_state;
17363        }
17364    }
17365
17366    pub fn redo_selection(
17367        &mut self,
17368        _: &RedoSelection,
17369        window: &mut Window,
17370        cx: &mut Context<Self>,
17371    ) {
17372        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17373        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17374            self.selection_history.mode = SelectionHistoryMode::Redoing;
17375            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17376                this.end_selection(window, cx);
17377                this.change_selections(
17378                    SelectionEffects::scroll(Autoscroll::newest()),
17379                    window,
17380                    cx,
17381                    |s| s.select_anchors(entry.selections.to_vec()),
17382                );
17383            });
17384            self.selection_history.mode = SelectionHistoryMode::Normal;
17385
17386            self.select_next_state = entry.select_next_state;
17387            self.select_prev_state = entry.select_prev_state;
17388            self.add_selections_state = entry.add_selections_state;
17389        }
17390    }
17391
17392    pub fn expand_excerpts(
17393        &mut self,
17394        action: &ExpandExcerpts,
17395        _: &mut Window,
17396        cx: &mut Context<Self>,
17397    ) {
17398        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17399    }
17400
17401    pub fn expand_excerpts_down(
17402        &mut self,
17403        action: &ExpandExcerptsDown,
17404        _: &mut Window,
17405        cx: &mut Context<Self>,
17406    ) {
17407        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17408    }
17409
17410    pub fn expand_excerpts_up(
17411        &mut self,
17412        action: &ExpandExcerptsUp,
17413        _: &mut Window,
17414        cx: &mut Context<Self>,
17415    ) {
17416        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17417    }
17418
17419    pub fn expand_excerpts_for_direction(
17420        &mut self,
17421        lines: u32,
17422        direction: ExpandExcerptDirection,
17423        cx: &mut Context<Self>,
17424    ) {
17425        let selections = self.selections.disjoint_anchors_arc();
17426
17427        let lines = if lines == 0 {
17428            EditorSettings::get_global(cx).expand_excerpt_lines
17429        } else {
17430            lines
17431        };
17432
17433        let snapshot = self.buffer.read(cx).snapshot(cx);
17434        let excerpt_ids = selections
17435            .iter()
17436            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17437            .unique()
17438            .sorted()
17439            .collect::<Vec<_>>();
17440
17441        if self.delegate_expand_excerpts {
17442            cx.emit(EditorEvent::ExpandExcerptsRequested {
17443                excerpt_ids,
17444                lines,
17445                direction,
17446            });
17447            return;
17448        }
17449
17450        self.buffer.update(cx, |buffer, cx| {
17451            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17452        })
17453    }
17454
17455    pub fn expand_excerpt(
17456        &mut self,
17457        excerpt: ExcerptId,
17458        direction: ExpandExcerptDirection,
17459        window: &mut Window,
17460        cx: &mut Context<Self>,
17461    ) {
17462        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17463
17464        if self.delegate_expand_excerpts {
17465            cx.emit(EditorEvent::ExpandExcerptsRequested {
17466                excerpt_ids: vec![excerpt],
17467                lines: lines_to_expand,
17468                direction,
17469            });
17470            return;
17471        }
17472
17473        let current_scroll_position = self.scroll_position(cx);
17474        let mut scroll = None;
17475
17476        if direction == ExpandExcerptDirection::Down {
17477            let multi_buffer = self.buffer.read(cx);
17478            let snapshot = multi_buffer.snapshot(cx);
17479            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17480                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17481                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17482            {
17483                let buffer_snapshot = buffer.read(cx).snapshot();
17484                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17485                let last_row = buffer_snapshot.max_point().row;
17486                let lines_below = last_row.saturating_sub(excerpt_end_row);
17487                if lines_below >= lines_to_expand {
17488                    scroll = Some(
17489                        current_scroll_position
17490                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17491                    );
17492                }
17493            }
17494        }
17495        if direction == ExpandExcerptDirection::Up
17496            && self
17497                .buffer
17498                .read(cx)
17499                .snapshot(cx)
17500                .excerpt_before(excerpt)
17501                .is_none()
17502        {
17503            scroll = Some(current_scroll_position);
17504        }
17505
17506        self.buffer.update(cx, |buffer, cx| {
17507            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17508        });
17509
17510        if let Some(new_scroll_position) = scroll {
17511            self.set_scroll_position(new_scroll_position, window, cx);
17512        }
17513    }
17514
17515    pub fn go_to_singleton_buffer_point(
17516        &mut self,
17517        point: Point,
17518        window: &mut Window,
17519        cx: &mut Context<Self>,
17520    ) {
17521        self.go_to_singleton_buffer_range(point..point, window, cx);
17522    }
17523
17524    pub fn go_to_singleton_buffer_range(
17525        &mut self,
17526        range: Range<Point>,
17527        window: &mut Window,
17528        cx: &mut Context<Self>,
17529    ) {
17530        let multibuffer = self.buffer().read(cx);
17531        let Some(buffer) = multibuffer.as_singleton() else {
17532            return;
17533        };
17534        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17535            return;
17536        };
17537        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17538            return;
17539        };
17540        self.change_selections(
17541            SelectionEffects::default().nav_history(true),
17542            window,
17543            cx,
17544            |s| s.select_anchor_ranges([start..end]),
17545        );
17546    }
17547
17548    pub fn go_to_diagnostic(
17549        &mut self,
17550        action: &GoToDiagnostic,
17551        window: &mut Window,
17552        cx: &mut Context<Self>,
17553    ) {
17554        if !self.diagnostics_enabled() {
17555            return;
17556        }
17557        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17558        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17559    }
17560
17561    pub fn go_to_prev_diagnostic(
17562        &mut self,
17563        action: &GoToPreviousDiagnostic,
17564        window: &mut Window,
17565        cx: &mut Context<Self>,
17566    ) {
17567        if !self.diagnostics_enabled() {
17568            return;
17569        }
17570        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17571        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17572    }
17573
17574    pub fn go_to_diagnostic_impl(
17575        &mut self,
17576        direction: Direction,
17577        severity: GoToDiagnosticSeverityFilter,
17578        window: &mut Window,
17579        cx: &mut Context<Self>,
17580    ) {
17581        let buffer = self.buffer.read(cx).snapshot(cx);
17582        let selection = self
17583            .selections
17584            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17585
17586        let mut active_group_id = None;
17587        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17588            && active_group.active_range.start.to_offset(&buffer) == selection.start
17589        {
17590            active_group_id = Some(active_group.group_id);
17591        }
17592
17593        fn filtered<'a>(
17594            severity: GoToDiagnosticSeverityFilter,
17595            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17596        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17597            diagnostics
17598                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17599                .filter(|entry| entry.range.start != entry.range.end)
17600                .filter(|entry| !entry.diagnostic.is_unnecessary)
17601        }
17602
17603        let before = filtered(
17604            severity,
17605            buffer
17606                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17607                .filter(|entry| entry.range.start <= selection.start),
17608        );
17609        let after = filtered(
17610            severity,
17611            buffer
17612                .diagnostics_in_range(selection.start..buffer.len())
17613                .filter(|entry| entry.range.start >= selection.start),
17614        );
17615
17616        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17617        if direction == Direction::Prev {
17618            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17619            {
17620                for diagnostic in prev_diagnostics.into_iter().rev() {
17621                    if diagnostic.range.start != selection.start
17622                        || active_group_id
17623                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17624                    {
17625                        found = Some(diagnostic);
17626                        break 'outer;
17627                    }
17628                }
17629            }
17630        } else {
17631            for diagnostic in after.chain(before) {
17632                if diagnostic.range.start != selection.start
17633                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17634                {
17635                    found = Some(diagnostic);
17636                    break;
17637                }
17638            }
17639        }
17640        let Some(next_diagnostic) = found else {
17641            return;
17642        };
17643
17644        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17645        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17646            return;
17647        };
17648        let snapshot = self.snapshot(window, cx);
17649        if snapshot.intersects_fold(next_diagnostic.range.start) {
17650            self.unfold_ranges(
17651                std::slice::from_ref(&next_diagnostic.range),
17652                true,
17653                false,
17654                cx,
17655            );
17656        }
17657        self.change_selections(Default::default(), window, cx, |s| {
17658            s.select_ranges(vec![
17659                next_diagnostic.range.start..next_diagnostic.range.start,
17660            ])
17661        });
17662        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17663        self.refresh_edit_prediction(false, true, window, cx);
17664    }
17665
17666    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17667        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17668        let snapshot = self.snapshot(window, cx);
17669        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17670        self.go_to_hunk_before_or_after_position(
17671            &snapshot,
17672            selection.head(),
17673            Direction::Next,
17674            window,
17675            cx,
17676        );
17677    }
17678
17679    pub fn go_to_hunk_before_or_after_position(
17680        &mut self,
17681        snapshot: &EditorSnapshot,
17682        position: Point,
17683        direction: Direction,
17684        window: &mut Window,
17685        cx: &mut Context<Editor>,
17686    ) {
17687        let row = if direction == Direction::Next {
17688            self.hunk_after_position(snapshot, position)
17689                .map(|hunk| hunk.row_range.start)
17690        } else {
17691            self.hunk_before_position(snapshot, position)
17692        };
17693
17694        if let Some(row) = row {
17695            let destination = Point::new(row.0, 0);
17696            let autoscroll = Autoscroll::center();
17697
17698            self.unfold_ranges(&[destination..destination], false, false, cx);
17699            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17700                s.select_ranges([destination..destination]);
17701            });
17702        }
17703    }
17704
17705    fn hunk_after_position(
17706        &mut self,
17707        snapshot: &EditorSnapshot,
17708        position: Point,
17709    ) -> Option<MultiBufferDiffHunk> {
17710        snapshot
17711            .buffer_snapshot()
17712            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17713            .find(|hunk| hunk.row_range.start.0 > position.row)
17714            .or_else(|| {
17715                snapshot
17716                    .buffer_snapshot()
17717                    .diff_hunks_in_range(Point::zero()..position)
17718                    .find(|hunk| hunk.row_range.end.0 < position.row)
17719            })
17720    }
17721
17722    fn go_to_prev_hunk(
17723        &mut self,
17724        _: &GoToPreviousHunk,
17725        window: &mut Window,
17726        cx: &mut Context<Self>,
17727    ) {
17728        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17729        let snapshot = self.snapshot(window, cx);
17730        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17731        self.go_to_hunk_before_or_after_position(
17732            &snapshot,
17733            selection.head(),
17734            Direction::Prev,
17735            window,
17736            cx,
17737        );
17738    }
17739
17740    fn hunk_before_position(
17741        &mut self,
17742        snapshot: &EditorSnapshot,
17743        position: Point,
17744    ) -> Option<MultiBufferRow> {
17745        snapshot
17746            .buffer_snapshot()
17747            .diff_hunk_before(position)
17748            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17749    }
17750
17751    fn go_to_next_change(
17752        &mut self,
17753        _: &GoToNextChange,
17754        window: &mut Window,
17755        cx: &mut Context<Self>,
17756    ) {
17757        if let Some(selections) = self
17758            .change_list
17759            .next_change(1, Direction::Next)
17760            .map(|s| s.to_vec())
17761        {
17762            self.change_selections(Default::default(), window, cx, |s| {
17763                let map = s.display_snapshot();
17764                s.select_display_ranges(selections.iter().map(|a| {
17765                    let point = a.to_display_point(&map);
17766                    point..point
17767                }))
17768            })
17769        }
17770    }
17771
17772    fn go_to_previous_change(
17773        &mut self,
17774        _: &GoToPreviousChange,
17775        window: &mut Window,
17776        cx: &mut Context<Self>,
17777    ) {
17778        if let Some(selections) = self
17779            .change_list
17780            .next_change(1, Direction::Prev)
17781            .map(|s| s.to_vec())
17782        {
17783            self.change_selections(Default::default(), window, cx, |s| {
17784                let map = s.display_snapshot();
17785                s.select_display_ranges(selections.iter().map(|a| {
17786                    let point = a.to_display_point(&map);
17787                    point..point
17788                }))
17789            })
17790        }
17791    }
17792
17793    pub fn go_to_next_document_highlight(
17794        &mut self,
17795        _: &GoToNextDocumentHighlight,
17796        window: &mut Window,
17797        cx: &mut Context<Self>,
17798    ) {
17799        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17800    }
17801
17802    pub fn go_to_prev_document_highlight(
17803        &mut self,
17804        _: &GoToPreviousDocumentHighlight,
17805        window: &mut Window,
17806        cx: &mut Context<Self>,
17807    ) {
17808        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17809    }
17810
17811    pub fn go_to_document_highlight_before_or_after_position(
17812        &mut self,
17813        direction: Direction,
17814        window: &mut Window,
17815        cx: &mut Context<Editor>,
17816    ) {
17817        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17818        let snapshot = self.snapshot(window, cx);
17819        let buffer = &snapshot.buffer_snapshot();
17820        let position = self
17821            .selections
17822            .newest::<Point>(&snapshot.display_snapshot)
17823            .head();
17824        let anchor_position = buffer.anchor_after(position);
17825
17826        // Get all document highlights (both read and write)
17827        let mut all_highlights = Vec::new();
17828
17829        if let Some((_, read_highlights)) = self
17830            .background_highlights
17831            .get(&HighlightKey::DocumentHighlightRead)
17832        {
17833            all_highlights.extend(read_highlights.iter());
17834        }
17835
17836        if let Some((_, write_highlights)) = self
17837            .background_highlights
17838            .get(&HighlightKey::DocumentHighlightWrite)
17839        {
17840            all_highlights.extend(write_highlights.iter());
17841        }
17842
17843        if all_highlights.is_empty() {
17844            return;
17845        }
17846
17847        // Sort highlights by position
17848        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17849
17850        let target_highlight = match direction {
17851            Direction::Next => {
17852                // Find the first highlight after the current position
17853                all_highlights
17854                    .iter()
17855                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17856            }
17857            Direction::Prev => {
17858                // Find the last highlight before the current position
17859                all_highlights
17860                    .iter()
17861                    .rev()
17862                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17863            }
17864        };
17865
17866        if let Some(highlight) = target_highlight {
17867            let destination = highlight.start.to_point(buffer);
17868            let autoscroll = Autoscroll::center();
17869
17870            self.unfold_ranges(&[destination..destination], false, false, cx);
17871            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17872                s.select_ranges([destination..destination]);
17873            });
17874        }
17875    }
17876
17877    fn go_to_line<T: 'static>(
17878        &mut self,
17879        position: Anchor,
17880        highlight_color: Option<Hsla>,
17881        window: &mut Window,
17882        cx: &mut Context<Self>,
17883    ) {
17884        let snapshot = self.snapshot(window, cx).display_snapshot;
17885        let position = position.to_point(&snapshot.buffer_snapshot());
17886        let start = snapshot
17887            .buffer_snapshot()
17888            .clip_point(Point::new(position.row, 0), Bias::Left);
17889        let end = start + Point::new(1, 0);
17890        let start = snapshot.buffer_snapshot().anchor_before(start);
17891        let end = snapshot.buffer_snapshot().anchor_before(end);
17892
17893        self.highlight_rows::<T>(
17894            start..end,
17895            highlight_color
17896                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17897            Default::default(),
17898            cx,
17899        );
17900
17901        if self.buffer.read(cx).is_singleton() {
17902            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17903        }
17904    }
17905
17906    pub fn go_to_definition(
17907        &mut self,
17908        _: &GoToDefinition,
17909        window: &mut Window,
17910        cx: &mut Context<Self>,
17911    ) -> Task<Result<Navigated>> {
17912        let definition =
17913            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17914        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17915        cx.spawn_in(window, async move |editor, cx| {
17916            if definition.await? == Navigated::Yes {
17917                return Ok(Navigated::Yes);
17918            }
17919            match fallback_strategy {
17920                GoToDefinitionFallback::None => Ok(Navigated::No),
17921                GoToDefinitionFallback::FindAllReferences => {
17922                    match editor.update_in(cx, |editor, window, cx| {
17923                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17924                    })? {
17925                        Some(references) => references.await,
17926                        None => Ok(Navigated::No),
17927                    }
17928                }
17929            }
17930        })
17931    }
17932
17933    pub fn go_to_declaration(
17934        &mut self,
17935        _: &GoToDeclaration,
17936        window: &mut Window,
17937        cx: &mut Context<Self>,
17938    ) -> Task<Result<Navigated>> {
17939        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17940    }
17941
17942    pub fn go_to_declaration_split(
17943        &mut self,
17944        _: &GoToDeclaration,
17945        window: &mut Window,
17946        cx: &mut Context<Self>,
17947    ) -> Task<Result<Navigated>> {
17948        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17949    }
17950
17951    pub fn go_to_implementation(
17952        &mut self,
17953        _: &GoToImplementation,
17954        window: &mut Window,
17955        cx: &mut Context<Self>,
17956    ) -> Task<Result<Navigated>> {
17957        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17958    }
17959
17960    pub fn go_to_implementation_split(
17961        &mut self,
17962        _: &GoToImplementationSplit,
17963        window: &mut Window,
17964        cx: &mut Context<Self>,
17965    ) -> Task<Result<Navigated>> {
17966        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17967    }
17968
17969    pub fn go_to_type_definition(
17970        &mut self,
17971        _: &GoToTypeDefinition,
17972        window: &mut Window,
17973        cx: &mut Context<Self>,
17974    ) -> Task<Result<Navigated>> {
17975        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17976    }
17977
17978    pub fn go_to_definition_split(
17979        &mut self,
17980        _: &GoToDefinitionSplit,
17981        window: &mut Window,
17982        cx: &mut Context<Self>,
17983    ) -> Task<Result<Navigated>> {
17984        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17985    }
17986
17987    pub fn go_to_type_definition_split(
17988        &mut self,
17989        _: &GoToTypeDefinitionSplit,
17990        window: &mut Window,
17991        cx: &mut Context<Self>,
17992    ) -> Task<Result<Navigated>> {
17993        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17994    }
17995
17996    fn go_to_definition_of_kind(
17997        &mut self,
17998        kind: GotoDefinitionKind,
17999        split: bool,
18000        window: &mut Window,
18001        cx: &mut Context<Self>,
18002    ) -> Task<Result<Navigated>> {
18003        let Some(provider) = self.semantics_provider.clone() else {
18004            return Task::ready(Ok(Navigated::No));
18005        };
18006        let head = self
18007            .selections
18008            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18009            .head();
18010        let buffer = self.buffer.read(cx);
18011        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18012            return Task::ready(Ok(Navigated::No));
18013        };
18014        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18015            return Task::ready(Ok(Navigated::No));
18016        };
18017
18018        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18019
18020        cx.spawn_in(window, async move |editor, cx| {
18021            let Some(definitions) = definitions.await? else {
18022                return Ok(Navigated::No);
18023            };
18024            let navigated = editor
18025                .update_in(cx, |editor, window, cx| {
18026                    editor.navigate_to_hover_links(
18027                        Some(kind),
18028                        definitions
18029                            .into_iter()
18030                            .filter(|location| {
18031                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18032                            })
18033                            .map(HoverLink::Text)
18034                            .collect::<Vec<_>>(),
18035                        nav_entry,
18036                        split,
18037                        window,
18038                        cx,
18039                    )
18040                })?
18041                .await?;
18042            anyhow::Ok(navigated)
18043        })
18044    }
18045
18046    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18047        let selection = self.selections.newest_anchor();
18048        let head = selection.head();
18049        let tail = selection.tail();
18050
18051        let Some((buffer, start_position)) =
18052            self.buffer.read(cx).text_anchor_for_position(head, cx)
18053        else {
18054            return;
18055        };
18056
18057        let end_position = if head != tail {
18058            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18059                return;
18060            };
18061            Some(pos)
18062        } else {
18063            None
18064        };
18065
18066        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18067            let url = if let Some(end_pos) = end_position {
18068                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18069            } else {
18070                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18071            };
18072
18073            if let Some(url) = url {
18074                cx.update(|window, cx| {
18075                    if parse_zed_link(&url, cx).is_some() {
18076                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18077                    } else {
18078                        cx.open_url(&url);
18079                    }
18080                })?;
18081            }
18082
18083            anyhow::Ok(())
18084        });
18085
18086        url_finder.detach();
18087    }
18088
18089    pub fn open_selected_filename(
18090        &mut self,
18091        _: &OpenSelectedFilename,
18092        window: &mut Window,
18093        cx: &mut Context<Self>,
18094    ) {
18095        let Some(workspace) = self.workspace() else {
18096            return;
18097        };
18098
18099        let position = self.selections.newest_anchor().head();
18100
18101        let Some((buffer, buffer_position)) =
18102            self.buffer.read(cx).text_anchor_for_position(position, cx)
18103        else {
18104            return;
18105        };
18106
18107        let project = self.project.clone();
18108
18109        cx.spawn_in(window, async move |_, cx| {
18110            let result = find_file(&buffer, project, buffer_position, cx).await;
18111
18112            if let Some((_, path)) = result {
18113                workspace
18114                    .update_in(cx, |workspace, window, cx| {
18115                        workspace.open_resolved_path(path, window, cx)
18116                    })?
18117                    .await?;
18118            }
18119            anyhow::Ok(())
18120        })
18121        .detach();
18122    }
18123
18124    pub(crate) fn navigate_to_hover_links(
18125        &mut self,
18126        kind: Option<GotoDefinitionKind>,
18127        definitions: Vec<HoverLink>,
18128        origin: Option<NavigationEntry>,
18129        split: bool,
18130        window: &mut Window,
18131        cx: &mut Context<Editor>,
18132    ) -> Task<Result<Navigated>> {
18133        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18134        let mut first_url_or_file = None;
18135        let definitions: Vec<_> = definitions
18136            .into_iter()
18137            .filter_map(|def| match def {
18138                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18139                HoverLink::InlayHint(lsp_location, server_id) => {
18140                    let computation =
18141                        self.compute_target_location(lsp_location, server_id, window, cx);
18142                    Some(cx.background_spawn(computation))
18143                }
18144                HoverLink::Url(url) => {
18145                    first_url_or_file = Some(Either::Left(url));
18146                    None
18147                }
18148                HoverLink::File(path) => {
18149                    first_url_or_file = Some(Either::Right(path));
18150                    None
18151                }
18152            })
18153            .collect();
18154
18155        let workspace = self.workspace();
18156
18157        cx.spawn_in(window, async move |editor, cx| {
18158            let locations: Vec<Location> = future::join_all(definitions)
18159                .await
18160                .into_iter()
18161                .filter_map(|location| location.transpose())
18162                .collect::<Result<_>>()
18163                .context("location tasks")?;
18164            let mut locations = cx.update(|_, cx| {
18165                locations
18166                    .into_iter()
18167                    .map(|location| {
18168                        let buffer = location.buffer.read(cx);
18169                        (location.buffer, location.range.to_point(buffer))
18170                    })
18171                    .into_group_map()
18172            })?;
18173            let mut num_locations = 0;
18174            for ranges in locations.values_mut() {
18175                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18176                ranges.dedup();
18177                num_locations += ranges.len();
18178            }
18179
18180            if num_locations > 1 {
18181                let tab_kind = match kind {
18182                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18183                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18184                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18185                    Some(GotoDefinitionKind::Type) => "Types",
18186                };
18187                let title = editor
18188                    .update_in(cx, |_, _, cx| {
18189                        let target = locations
18190                            .iter()
18191                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18192                            .map(|(buffer, location)| {
18193                                buffer
18194                                    .read(cx)
18195                                    .text_for_range(location.clone())
18196                                    .collect::<String>()
18197                            })
18198                            .filter(|text| !text.contains('\n'))
18199                            .unique()
18200                            .take(3)
18201                            .join(", ");
18202                        if target.is_empty() {
18203                            tab_kind.to_owned()
18204                        } else {
18205                            format!("{tab_kind} for {target}")
18206                        }
18207                    })
18208                    .context("buffer title")?;
18209
18210                let Some(workspace) = workspace else {
18211                    return Ok(Navigated::No);
18212                };
18213
18214                let opened = workspace
18215                    .update_in(cx, |workspace, window, cx| {
18216                        let allow_preview = PreviewTabsSettings::get_global(cx)
18217                            .enable_preview_multibuffer_from_code_navigation;
18218                        if let Some((target_editor, target_pane)) =
18219                            Self::open_locations_in_multibuffer(
18220                                workspace,
18221                                locations,
18222                                title,
18223                                split,
18224                                allow_preview,
18225                                MultibufferSelectionMode::First,
18226                                window,
18227                                cx,
18228                            )
18229                        {
18230                            // We create our own nav history instead of using
18231                            // `target_editor.nav_history` because `nav_history`
18232                            // seems to be populated asynchronously when an item
18233                            // is added to a pane
18234                            let mut nav_history = target_pane
18235                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18236                            target_editor.update(cx, |editor, cx| {
18237                                let nav_data = editor
18238                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18239                                let target =
18240                                    Some(nav_history.navigation_entry(Some(
18241                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18242                                    )));
18243                                nav_history.push_tag(origin, target);
18244                            })
18245                        }
18246                    })
18247                    .is_ok();
18248
18249                anyhow::Ok(Navigated::from_bool(opened))
18250            } else if num_locations == 0 {
18251                // If there is one url or file, open it directly
18252                match first_url_or_file {
18253                    Some(Either::Left(url)) => {
18254                        cx.update(|window, cx| {
18255                            if parse_zed_link(&url, cx).is_some() {
18256                                window
18257                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18258                            } else {
18259                                cx.open_url(&url);
18260                            }
18261                        })?;
18262                        Ok(Navigated::Yes)
18263                    }
18264                    Some(Either::Right(path)) => {
18265                        // TODO(andrew): respect preview tab settings
18266                        //               `enable_keep_preview_on_code_navigation` and
18267                        //               `enable_preview_file_from_code_navigation`
18268                        let Some(workspace) = workspace else {
18269                            return Ok(Navigated::No);
18270                        };
18271                        workspace
18272                            .update_in(cx, |workspace, window, cx| {
18273                                workspace.open_resolved_path(path, window, cx)
18274                            })?
18275                            .await?;
18276                        Ok(Navigated::Yes)
18277                    }
18278                    None => Ok(Navigated::No),
18279                }
18280            } else {
18281                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18282                let target_range = target_ranges.first().unwrap().clone();
18283
18284                editor.update_in(cx, |editor, window, cx| {
18285                    let range = editor.range_for_match(&target_range);
18286                    let range = collapse_multiline_range(range);
18287
18288                    if !split
18289                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18290                    {
18291                        editor.go_to_singleton_buffer_range(range, window, cx);
18292
18293                        let target =
18294                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18295                        if let Some(mut nav_history) = editor.nav_history.clone() {
18296                            nav_history.push_tag(origin, target);
18297                        }
18298                    } else {
18299                        let Some(workspace) = workspace else {
18300                            return Navigated::No;
18301                        };
18302                        let pane = workspace.read(cx).active_pane().clone();
18303                        window.defer(cx, move |window, cx| {
18304                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18305                                workspace.update(cx, |workspace, cx| {
18306                                    let pane = if split {
18307                                        workspace.adjacent_pane(window, cx)
18308                                    } else {
18309                                        workspace.active_pane().clone()
18310                                    };
18311
18312                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18313                                    let keep_old_preview = preview_tabs_settings
18314                                        .enable_keep_preview_on_code_navigation;
18315                                    let allow_new_preview = preview_tabs_settings
18316                                        .enable_preview_file_from_code_navigation;
18317
18318                                    let editor = workspace.open_project_item(
18319                                        pane.clone(),
18320                                        target_buffer.clone(),
18321                                        true,
18322                                        true,
18323                                        keep_old_preview,
18324                                        allow_new_preview,
18325                                        window,
18326                                        cx,
18327                                    );
18328                                    (editor, pane)
18329                                });
18330                            // We create our own nav history instead of using
18331                            // `target_editor.nav_history` because `nav_history`
18332                            // seems to be populated asynchronously when an item
18333                            // is added to a pane
18334                            let mut nav_history = target_pane
18335                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18336                            target_editor.update(cx, |target_editor, cx| {
18337                                // When selecting a definition in a different buffer, disable the nav history
18338                                // to avoid creating a history entry at the previous cursor location.
18339                                pane.update(cx, |pane, _| pane.disable_history());
18340                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18341
18342                                let nav_data = target_editor.navigation_data(
18343                                    target_editor.selections.newest_anchor().head(),
18344                                    cx,
18345                                );
18346                                let target =
18347                                    Some(nav_history.navigation_entry(Some(
18348                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18349                                    )));
18350                                nav_history.push_tag(origin, target);
18351                                pane.update(cx, |pane, _| pane.enable_history());
18352                            });
18353                        });
18354                    }
18355                    Navigated::Yes
18356                })
18357            }
18358        })
18359    }
18360
18361    fn compute_target_location(
18362        &self,
18363        lsp_location: lsp::Location,
18364        server_id: LanguageServerId,
18365        window: &mut Window,
18366        cx: &mut Context<Self>,
18367    ) -> Task<anyhow::Result<Option<Location>>> {
18368        let Some(project) = self.project.clone() else {
18369            return Task::ready(Ok(None));
18370        };
18371
18372        cx.spawn_in(window, async move |editor, cx| {
18373            let location_task = editor.update(cx, |_, cx| {
18374                project.update(cx, |project, cx| {
18375                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18376                })
18377            })?;
18378            let location = Some({
18379                let target_buffer_handle = location_task.await.context("open local buffer")?;
18380                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18381                    let target_start = target_buffer
18382                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18383                    let target_end = target_buffer
18384                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18385                    target_buffer.anchor_after(target_start)
18386                        ..target_buffer.anchor_before(target_end)
18387                });
18388                Location {
18389                    buffer: target_buffer_handle,
18390                    range,
18391                }
18392            });
18393            Ok(location)
18394        })
18395    }
18396
18397    fn go_to_next_reference(
18398        &mut self,
18399        _: &GoToNextReference,
18400        window: &mut Window,
18401        cx: &mut Context<Self>,
18402    ) {
18403        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18404        if let Some(task) = task {
18405            task.detach();
18406        };
18407    }
18408
18409    fn go_to_prev_reference(
18410        &mut self,
18411        _: &GoToPreviousReference,
18412        window: &mut Window,
18413        cx: &mut Context<Self>,
18414    ) {
18415        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18416        if let Some(task) = task {
18417            task.detach();
18418        };
18419    }
18420
18421    pub fn go_to_reference_before_or_after_position(
18422        &mut self,
18423        direction: Direction,
18424        count: usize,
18425        window: &mut Window,
18426        cx: &mut Context<Self>,
18427    ) -> Option<Task<Result<()>>> {
18428        let selection = self.selections.newest_anchor();
18429        let head = selection.head();
18430
18431        let multi_buffer = self.buffer.read(cx);
18432
18433        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18434        let workspace = self.workspace()?;
18435        let project = workspace.read(cx).project().clone();
18436        let references =
18437            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18438        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18439            let Some(locations) = references.await? else {
18440                return Ok(());
18441            };
18442
18443            if locations.is_empty() {
18444                // totally normal - the cursor may be on something which is not
18445                // a symbol (e.g. a keyword)
18446                log::info!("no references found under cursor");
18447                return Ok(());
18448            }
18449
18450            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18451
18452            let (locations, current_location_index) =
18453                multi_buffer.update(cx, |multi_buffer, cx| {
18454                    let mut locations = locations
18455                        .into_iter()
18456                        .filter_map(|loc| {
18457                            let start = multi_buffer.buffer_anchor_to_anchor(
18458                                &loc.buffer,
18459                                loc.range.start,
18460                                cx,
18461                            )?;
18462                            let end = multi_buffer.buffer_anchor_to_anchor(
18463                                &loc.buffer,
18464                                loc.range.end,
18465                                cx,
18466                            )?;
18467                            Some(start..end)
18468                        })
18469                        .collect::<Vec<_>>();
18470
18471                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18472                    // There is an O(n) implementation, but given this list will be
18473                    // small (usually <100 items), the extra O(log(n)) factor isn't
18474                    // worth the (surprisingly large amount of) extra complexity.
18475                    locations
18476                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18477
18478                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18479
18480                    let current_location_index = locations.iter().position(|loc| {
18481                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18482                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18483                    });
18484
18485                    (locations, current_location_index)
18486                });
18487
18488            let Some(current_location_index) = current_location_index else {
18489                // This indicates something has gone wrong, because we already
18490                // handle the "no references" case above
18491                log::error!(
18492                    "failed to find current reference under cursor. Total references: {}",
18493                    locations.len()
18494                );
18495                return Ok(());
18496            };
18497
18498            let destination_location_index = match direction {
18499                Direction::Next => (current_location_index + count) % locations.len(),
18500                Direction::Prev => {
18501                    (current_location_index + locations.len() - count % locations.len())
18502                        % locations.len()
18503                }
18504            };
18505
18506            // TODO(cameron): is this needed?
18507            // the thinking is to avoid "jumping to the current location" (avoid
18508            // polluting "jumplist" in vim terms)
18509            if current_location_index == destination_location_index {
18510                return Ok(());
18511            }
18512
18513            let Range { start, end } = locations[destination_location_index];
18514
18515            editor.update_in(cx, |editor, window, cx| {
18516                let effects = SelectionEffects::default();
18517
18518                editor.unfold_ranges(&[start..end], false, false, cx);
18519                editor.change_selections(effects, window, cx, |s| {
18520                    s.select_ranges([start..start]);
18521                });
18522            })?;
18523
18524            Ok(())
18525        }))
18526    }
18527
18528    pub fn find_all_references(
18529        &mut self,
18530        action: &FindAllReferences,
18531        window: &mut Window,
18532        cx: &mut Context<Self>,
18533    ) -> Option<Task<Result<Navigated>>> {
18534        let always_open_multibuffer = action.always_open_multibuffer;
18535        let selection = self.selections.newest_anchor();
18536        let multi_buffer = self.buffer.read(cx);
18537        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18538        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18539        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18540        let head = selection_offset.head();
18541
18542        let head_anchor = multi_buffer_snapshot.anchor_at(
18543            head,
18544            if head < selection_offset.tail() {
18545                Bias::Right
18546            } else {
18547                Bias::Left
18548            },
18549        );
18550
18551        match self
18552            .find_all_references_task_sources
18553            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18554        {
18555            Ok(_) => {
18556                log::info!(
18557                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18558                );
18559                return None;
18560            }
18561            Err(i) => {
18562                self.find_all_references_task_sources.insert(i, head_anchor);
18563            }
18564        }
18565
18566        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18567        let workspace = self.workspace()?;
18568        let project = workspace.read(cx).project().clone();
18569        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18570        Some(cx.spawn_in(window, async move |editor, cx| {
18571            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18572                if let Ok(i) = editor
18573                    .find_all_references_task_sources
18574                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18575                {
18576                    editor.find_all_references_task_sources.remove(i);
18577                }
18578            });
18579
18580            let Some(locations) = references.await? else {
18581                return anyhow::Ok(Navigated::No);
18582            };
18583            let mut locations = cx.update(|_, cx| {
18584                locations
18585                    .into_iter()
18586                    .map(|location| {
18587                        let buffer = location.buffer.read(cx);
18588                        (location.buffer, location.range.to_point(buffer))
18589                    })
18590                    // if special-casing the single-match case, remove ranges
18591                    // that intersect current selection
18592                    .filter(|(location_buffer, location)| {
18593                        if always_open_multibuffer || &buffer != location_buffer {
18594                            return true;
18595                        }
18596
18597                        !location.contains_inclusive(&selection_point.range())
18598                    })
18599                    .into_group_map()
18600            })?;
18601            if locations.is_empty() {
18602                return anyhow::Ok(Navigated::No);
18603            }
18604            for ranges in locations.values_mut() {
18605                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18606                ranges.dedup();
18607            }
18608            let mut num_locations = 0;
18609            for ranges in locations.values_mut() {
18610                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18611                ranges.dedup();
18612                num_locations += ranges.len();
18613            }
18614
18615            if num_locations == 1 && !always_open_multibuffer {
18616                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18617                let target_range = target_ranges.first().unwrap().clone();
18618
18619                return editor.update_in(cx, |editor, window, cx| {
18620                    let range = target_range.to_point(target_buffer.read(cx));
18621                    let range = editor.range_for_match(&range);
18622                    let range = range.start..range.start;
18623
18624                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18625                        editor.go_to_singleton_buffer_range(range, window, cx);
18626                    } else {
18627                        let pane = workspace.read(cx).active_pane().clone();
18628                        window.defer(cx, move |window, cx| {
18629                            let target_editor: Entity<Self> =
18630                                workspace.update(cx, |workspace, cx| {
18631                                    let pane = workspace.active_pane().clone();
18632
18633                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18634                                    let keep_old_preview = preview_tabs_settings
18635                                        .enable_keep_preview_on_code_navigation;
18636                                    let allow_new_preview = preview_tabs_settings
18637                                        .enable_preview_file_from_code_navigation;
18638
18639                                    workspace.open_project_item(
18640                                        pane,
18641                                        target_buffer.clone(),
18642                                        true,
18643                                        true,
18644                                        keep_old_preview,
18645                                        allow_new_preview,
18646                                        window,
18647                                        cx,
18648                                    )
18649                                });
18650                            target_editor.update(cx, |target_editor, cx| {
18651                                // When selecting a definition in a different buffer, disable the nav history
18652                                // to avoid creating a history entry at the previous cursor location.
18653                                pane.update(cx, |pane, _| pane.disable_history());
18654                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18655                                pane.update(cx, |pane, _| pane.enable_history());
18656                            });
18657                        });
18658                    }
18659                    Navigated::No
18660                });
18661            }
18662
18663            workspace.update_in(cx, |workspace, window, cx| {
18664                let target = locations
18665                    .iter()
18666                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18667                    .map(|(buffer, location)| {
18668                        buffer
18669                            .read(cx)
18670                            .text_for_range(location.clone())
18671                            .collect::<String>()
18672                    })
18673                    .filter(|text| !text.contains('\n'))
18674                    .unique()
18675                    .take(3)
18676                    .join(", ");
18677                let title = if target.is_empty() {
18678                    "References".to_owned()
18679                } else {
18680                    format!("References to {target}")
18681                };
18682                let allow_preview = PreviewTabsSettings::get_global(cx)
18683                    .enable_preview_multibuffer_from_code_navigation;
18684                Self::open_locations_in_multibuffer(
18685                    workspace,
18686                    locations,
18687                    title,
18688                    false,
18689                    allow_preview,
18690                    MultibufferSelectionMode::First,
18691                    window,
18692                    cx,
18693                );
18694                Navigated::Yes
18695            })
18696        }))
18697    }
18698
18699    /// Opens a multibuffer with the given project locations in it.
18700    pub fn open_locations_in_multibuffer(
18701        workspace: &mut Workspace,
18702        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18703        title: String,
18704        split: bool,
18705        allow_preview: bool,
18706        multibuffer_selection_mode: MultibufferSelectionMode,
18707        window: &mut Window,
18708        cx: &mut Context<Workspace>,
18709    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18710        if locations.is_empty() {
18711            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18712            return None;
18713        }
18714
18715        let capability = workspace.project().read(cx).capability();
18716        let mut ranges = <Vec<Range<Anchor>>>::new();
18717
18718        // a key to find existing multibuffer editors with the same set of locations
18719        // to prevent us from opening more and more multibuffer tabs for searches and the like
18720        let mut key = (title.clone(), vec![]);
18721        let excerpt_buffer = cx.new(|cx| {
18722            let key = &mut key.1;
18723            let mut multibuffer = MultiBuffer::new(capability);
18724            for (buffer, mut ranges_for_buffer) in locations {
18725                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18726                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18727                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18728                    PathKey::for_buffer(&buffer, cx),
18729                    buffer.clone(),
18730                    ranges_for_buffer,
18731                    multibuffer_context_lines(cx),
18732                    cx,
18733                );
18734                ranges.extend(new_ranges)
18735            }
18736
18737            multibuffer.with_title(title)
18738        });
18739        let existing = workspace.active_pane().update(cx, |pane, cx| {
18740            pane.items()
18741                .filter_map(|item| item.downcast::<Editor>())
18742                .find(|editor| {
18743                    editor
18744                        .read(cx)
18745                        .lookup_key
18746                        .as_ref()
18747                        .and_then(|it| {
18748                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18749                        })
18750                        .is_some_and(|it| *it == key)
18751                })
18752        });
18753        let was_existing = existing.is_some();
18754        let editor = existing.unwrap_or_else(|| {
18755            cx.new(|cx| {
18756                let mut editor = Editor::for_multibuffer(
18757                    excerpt_buffer,
18758                    Some(workspace.project().clone()),
18759                    window,
18760                    cx,
18761                );
18762                editor.lookup_key = Some(Box::new(key));
18763                editor
18764            })
18765        });
18766        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18767            MultibufferSelectionMode::First => {
18768                if let Some(first_range) = ranges.first() {
18769                    editor.change_selections(
18770                        SelectionEffects::no_scroll(),
18771                        window,
18772                        cx,
18773                        |selections| {
18774                            selections.clear_disjoint();
18775                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18776                        },
18777                    );
18778                }
18779                editor.highlight_background(
18780                    HighlightKey::Editor,
18781                    &ranges,
18782                    |_, theme| theme.colors().editor_highlighted_line_background,
18783                    cx,
18784                );
18785            }
18786            MultibufferSelectionMode::All => {
18787                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18788                    selections.clear_disjoint();
18789                    selections.select_anchor_ranges(ranges);
18790                });
18791            }
18792        });
18793
18794        let item = Box::new(editor.clone());
18795
18796        let pane = if split {
18797            workspace.adjacent_pane(window, cx)
18798        } else {
18799            workspace.active_pane().clone()
18800        };
18801        let activate_pane = split;
18802
18803        let mut destination_index = None;
18804        pane.update(cx, |pane, cx| {
18805            if allow_preview && !was_existing {
18806                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18807            }
18808            if was_existing && !allow_preview {
18809                pane.unpreview_item_if_preview(item.item_id());
18810            }
18811            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18812        });
18813
18814        Some((editor, pane))
18815    }
18816
18817    pub fn rename(
18818        &mut self,
18819        _: &Rename,
18820        window: &mut Window,
18821        cx: &mut Context<Self>,
18822    ) -> Option<Task<Result<()>>> {
18823        use language::ToOffset as _;
18824
18825        let provider = self.semantics_provider.clone()?;
18826        let selection = self.selections.newest_anchor().clone();
18827        let (cursor_buffer, cursor_buffer_position) = self
18828            .buffer
18829            .read(cx)
18830            .text_anchor_for_position(selection.head(), cx)?;
18831        let (tail_buffer, cursor_buffer_position_end) = self
18832            .buffer
18833            .read(cx)
18834            .text_anchor_for_position(selection.tail(), cx)?;
18835        if tail_buffer != cursor_buffer {
18836            return None;
18837        }
18838
18839        let snapshot = cursor_buffer.read(cx).snapshot();
18840        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18841        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18842        let prepare_rename = provider
18843            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18844            .unwrap_or_else(|| Task::ready(Ok(None)));
18845        drop(snapshot);
18846
18847        Some(cx.spawn_in(window, async move |this, cx| {
18848            let rename_range = if let Some(range) = prepare_rename.await? {
18849                Some(range)
18850            } else {
18851                this.update(cx, |this, cx| {
18852                    let buffer = this.buffer.read(cx).snapshot(cx);
18853                    let mut buffer_highlights = this
18854                        .document_highlights_for_position(selection.head(), &buffer)
18855                        .filter(|highlight| {
18856                            highlight.start.excerpt_id == selection.head().excerpt_id
18857                                && highlight.end.excerpt_id == selection.head().excerpt_id
18858                        });
18859                    buffer_highlights
18860                        .next()
18861                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18862                })?
18863            };
18864            if let Some(rename_range) = rename_range {
18865                this.update_in(cx, |this, window, cx| {
18866                    let snapshot = cursor_buffer.read(cx).snapshot();
18867                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18868                    let cursor_offset_in_rename_range =
18869                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18870                    let cursor_offset_in_rename_range_end =
18871                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18872
18873                    this.take_rename(false, window, cx);
18874                    let buffer = this.buffer.read(cx).read(cx);
18875                    let cursor_offset = selection.head().to_offset(&buffer);
18876                    let rename_start =
18877                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18878                    let rename_end = rename_start + rename_buffer_range.len();
18879                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18880                    let mut old_highlight_id = None;
18881                    let old_name: Arc<str> = buffer
18882                        .chunks(rename_start..rename_end, true)
18883                        .map(|chunk| {
18884                            if old_highlight_id.is_none() {
18885                                old_highlight_id = chunk.syntax_highlight_id;
18886                            }
18887                            chunk.text
18888                        })
18889                        .collect::<String>()
18890                        .into();
18891
18892                    drop(buffer);
18893
18894                    // Position the selection in the rename editor so that it matches the current selection.
18895                    this.show_local_selections = false;
18896                    let rename_editor = cx.new(|cx| {
18897                        let mut editor = Editor::single_line(window, cx);
18898                        editor.buffer.update(cx, |buffer, cx| {
18899                            buffer.edit(
18900                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18901                                None,
18902                                cx,
18903                            )
18904                        });
18905                        let cursor_offset_in_rename_range =
18906                            MultiBufferOffset(cursor_offset_in_rename_range);
18907                        let cursor_offset_in_rename_range_end =
18908                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18909                        let rename_selection_range = match cursor_offset_in_rename_range
18910                            .cmp(&cursor_offset_in_rename_range_end)
18911                        {
18912                            Ordering::Equal => {
18913                                editor.select_all(&SelectAll, window, cx);
18914                                return editor;
18915                            }
18916                            Ordering::Less => {
18917                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18918                            }
18919                            Ordering::Greater => {
18920                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18921                            }
18922                        };
18923                        if rename_selection_range.end.0 > old_name.len() {
18924                            editor.select_all(&SelectAll, window, cx);
18925                        } else {
18926                            editor.change_selections(Default::default(), window, cx, |s| {
18927                                s.select_ranges([rename_selection_range]);
18928                            });
18929                        }
18930                        editor
18931                    });
18932                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18933                        if e == &EditorEvent::Focused {
18934                            cx.emit(EditorEvent::FocusedIn)
18935                        }
18936                    })
18937                    .detach();
18938
18939                    let write_highlights =
18940                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18941                    let read_highlights =
18942                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18943                    let ranges = write_highlights
18944                        .iter()
18945                        .flat_map(|(_, ranges)| ranges.iter())
18946                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18947                        .cloned()
18948                        .collect();
18949
18950                    this.highlight_text(
18951                        HighlightKey::Rename,
18952                        ranges,
18953                        HighlightStyle {
18954                            fade_out: Some(0.6),
18955                            ..Default::default()
18956                        },
18957                        cx,
18958                    );
18959                    let rename_focus_handle = rename_editor.focus_handle(cx);
18960                    window.focus(&rename_focus_handle, cx);
18961                    let block_id = this.insert_blocks(
18962                        [BlockProperties {
18963                            style: BlockStyle::Flex,
18964                            placement: BlockPlacement::Below(range.start),
18965                            height: Some(1),
18966                            render: Arc::new({
18967                                let rename_editor = rename_editor.clone();
18968                                move |cx: &mut BlockContext| {
18969                                    let mut text_style = cx.editor_style.text.clone();
18970                                    if let Some(highlight_style) = old_highlight_id
18971                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18972                                    {
18973                                        text_style = text_style.highlight(highlight_style);
18974                                    }
18975                                    div()
18976                                        .block_mouse_except_scroll()
18977                                        .pl(cx.anchor_x)
18978                                        .child(EditorElement::new(
18979                                            &rename_editor,
18980                                            EditorStyle {
18981                                                background: cx.theme().system().transparent,
18982                                                local_player: cx.editor_style.local_player,
18983                                                text: text_style,
18984                                                scrollbar_width: cx.editor_style.scrollbar_width,
18985                                                syntax: cx.editor_style.syntax.clone(),
18986                                                status: cx.editor_style.status.clone(),
18987                                                inlay_hints_style: HighlightStyle {
18988                                                    font_weight: Some(FontWeight::BOLD),
18989                                                    ..make_inlay_hints_style(cx.app)
18990                                                },
18991                                                edit_prediction_styles: make_suggestion_styles(
18992                                                    cx.app,
18993                                                ),
18994                                                ..EditorStyle::default()
18995                                            },
18996                                        ))
18997                                        .into_any_element()
18998                                }
18999                            }),
19000                            priority: 0,
19001                        }],
19002                        Some(Autoscroll::fit()),
19003                        cx,
19004                    )[0];
19005                    this.pending_rename = Some(RenameState {
19006                        range,
19007                        old_name,
19008                        editor: rename_editor,
19009                        block_id,
19010                    });
19011                })?;
19012            }
19013
19014            Ok(())
19015        }))
19016    }
19017
19018    pub fn confirm_rename(
19019        &mut self,
19020        _: &ConfirmRename,
19021        window: &mut Window,
19022        cx: &mut Context<Self>,
19023    ) -> Option<Task<Result<()>>> {
19024        let rename = self.take_rename(false, window, cx)?;
19025        let workspace = self.workspace()?.downgrade();
19026        let (buffer, start) = self
19027            .buffer
19028            .read(cx)
19029            .text_anchor_for_position(rename.range.start, cx)?;
19030        let (end_buffer, _) = self
19031            .buffer
19032            .read(cx)
19033            .text_anchor_for_position(rename.range.end, cx)?;
19034        if buffer != end_buffer {
19035            return None;
19036        }
19037
19038        let old_name = rename.old_name;
19039        let new_name = rename.editor.read(cx).text(cx);
19040
19041        let rename = self.semantics_provider.as_ref()?.perform_rename(
19042            &buffer,
19043            start,
19044            new_name.clone(),
19045            cx,
19046        )?;
19047
19048        Some(cx.spawn_in(window, async move |editor, cx| {
19049            let project_transaction = rename.await?;
19050            Self::open_project_transaction(
19051                &editor,
19052                workspace,
19053                project_transaction,
19054                format!("Rename: {}{}", old_name, new_name),
19055                cx,
19056            )
19057            .await?;
19058
19059            editor.update(cx, |editor, cx| {
19060                editor.refresh_document_highlights(cx);
19061            })?;
19062            Ok(())
19063        }))
19064    }
19065
19066    fn take_rename(
19067        &mut self,
19068        moving_cursor: bool,
19069        window: &mut Window,
19070        cx: &mut Context<Self>,
19071    ) -> Option<RenameState> {
19072        let rename = self.pending_rename.take()?;
19073        if rename.editor.focus_handle(cx).is_focused(window) {
19074            window.focus(&self.focus_handle, cx);
19075        }
19076
19077        self.remove_blocks(
19078            [rename.block_id].into_iter().collect(),
19079            Some(Autoscroll::fit()),
19080            cx,
19081        );
19082        self.clear_highlights(HighlightKey::Rename, cx);
19083        self.show_local_selections = true;
19084
19085        if moving_cursor {
19086            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19087                editor
19088                    .selections
19089                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19090                    .head()
19091            });
19092
19093            // Update the selection to match the position of the selection inside
19094            // the rename editor.
19095            let snapshot = self.buffer.read(cx).read(cx);
19096            let rename_range = rename.range.to_offset(&snapshot);
19097            let cursor_in_editor = snapshot
19098                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19099                .min(rename_range.end);
19100            drop(snapshot);
19101
19102            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19103                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19104            });
19105        } else {
19106            self.refresh_document_highlights(cx);
19107        }
19108
19109        Some(rename)
19110    }
19111
19112    pub fn pending_rename(&self) -> Option<&RenameState> {
19113        self.pending_rename.as_ref()
19114    }
19115
19116    fn format(
19117        &mut self,
19118        _: &Format,
19119        window: &mut Window,
19120        cx: &mut Context<Self>,
19121    ) -> Option<Task<Result<()>>> {
19122        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19123
19124        let project = match &self.project {
19125            Some(project) => project.clone(),
19126            None => return None,
19127        };
19128
19129        Some(self.perform_format(
19130            project,
19131            FormatTrigger::Manual,
19132            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19133            window,
19134            cx,
19135        ))
19136    }
19137
19138    fn format_selections(
19139        &mut self,
19140        _: &FormatSelections,
19141        window: &mut Window,
19142        cx: &mut Context<Self>,
19143    ) -> Option<Task<Result<()>>> {
19144        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19145
19146        let project = match &self.project {
19147            Some(project) => project.clone(),
19148            None => return None,
19149        };
19150
19151        let ranges = self
19152            .selections
19153            .all_adjusted(&self.display_snapshot(cx))
19154            .into_iter()
19155            .map(|selection| selection.range())
19156            .collect_vec();
19157
19158        Some(self.perform_format(
19159            project,
19160            FormatTrigger::Manual,
19161            FormatTarget::Ranges(ranges),
19162            window,
19163            cx,
19164        ))
19165    }
19166
19167    fn perform_format(
19168        &mut self,
19169        project: Entity<Project>,
19170        trigger: FormatTrigger,
19171        target: FormatTarget,
19172        window: &mut Window,
19173        cx: &mut Context<Self>,
19174    ) -> Task<Result<()>> {
19175        let buffer = self.buffer.clone();
19176        let (buffers, target) = match target {
19177            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19178            FormatTarget::Ranges(selection_ranges) => {
19179                let multi_buffer = buffer.read(cx);
19180                let snapshot = multi_buffer.read(cx);
19181                let mut buffers = HashSet::default();
19182                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19183                    BTreeMap::new();
19184                for selection_range in selection_ranges {
19185                    for (buffer, buffer_range, _) in
19186                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19187                    {
19188                        let buffer_id = buffer.remote_id();
19189                        let start = buffer.anchor_before(buffer_range.start);
19190                        let end = buffer.anchor_after(buffer_range.end);
19191                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19192                        buffer_id_to_ranges
19193                            .entry(buffer_id)
19194                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19195                            .or_insert_with(|| vec![start..end]);
19196                    }
19197                }
19198                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19199            }
19200        };
19201
19202        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19203        let selections_prev = transaction_id_prev
19204            .and_then(|transaction_id_prev| {
19205                // default to selections as they were after the last edit, if we have them,
19206                // instead of how they are now.
19207                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19208                // will take you back to where you made the last edit, instead of staying where you scrolled
19209                self.selection_history
19210                    .transaction(transaction_id_prev)
19211                    .map(|t| t.0.clone())
19212            })
19213            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19214
19215        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19216        let format = project.update(cx, |project, cx| {
19217            project.format(buffers, target, true, trigger, cx)
19218        });
19219
19220        cx.spawn_in(window, async move |editor, cx| {
19221            let transaction = futures::select_biased! {
19222                transaction = format.log_err().fuse() => transaction,
19223                () = timeout => {
19224                    log::warn!("timed out waiting for formatting");
19225                    None
19226                }
19227            };
19228
19229            buffer.update(cx, |buffer, cx| {
19230                if let Some(transaction) = transaction
19231                    && !buffer.is_singleton()
19232                {
19233                    buffer.push_transaction(&transaction.0, cx);
19234                }
19235                cx.notify();
19236            });
19237
19238            if let Some(transaction_id_now) =
19239                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19240            {
19241                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19242                if has_new_transaction {
19243                    editor
19244                        .update(cx, |editor, _| {
19245                            editor
19246                                .selection_history
19247                                .insert_transaction(transaction_id_now, selections_prev);
19248                        })
19249                        .ok();
19250                }
19251            }
19252
19253            Ok(())
19254        })
19255    }
19256
19257    fn organize_imports(
19258        &mut self,
19259        _: &OrganizeImports,
19260        window: &mut Window,
19261        cx: &mut Context<Self>,
19262    ) -> Option<Task<Result<()>>> {
19263        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19264        let project = match &self.project {
19265            Some(project) => project.clone(),
19266            None => return None,
19267        };
19268        Some(self.perform_code_action_kind(
19269            project,
19270            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19271            window,
19272            cx,
19273        ))
19274    }
19275
19276    fn perform_code_action_kind(
19277        &mut self,
19278        project: Entity<Project>,
19279        kind: CodeActionKind,
19280        window: &mut Window,
19281        cx: &mut Context<Self>,
19282    ) -> Task<Result<()>> {
19283        let buffer = self.buffer.clone();
19284        let buffers = buffer.read(cx).all_buffers();
19285        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19286        let apply_action = project.update(cx, |project, cx| {
19287            project.apply_code_action_kind(buffers, kind, true, cx)
19288        });
19289        cx.spawn_in(window, async move |_, cx| {
19290            let transaction = futures::select_biased! {
19291                () = timeout => {
19292                    log::warn!("timed out waiting for executing code action");
19293                    None
19294                }
19295                transaction = apply_action.log_err().fuse() => transaction,
19296            };
19297            buffer.update(cx, |buffer, cx| {
19298                // check if we need this
19299                if let Some(transaction) = transaction
19300                    && !buffer.is_singleton()
19301                {
19302                    buffer.push_transaction(&transaction.0, cx);
19303                }
19304                cx.notify();
19305            });
19306            Ok(())
19307        })
19308    }
19309
19310    pub fn restart_language_server(
19311        &mut self,
19312        _: &RestartLanguageServer,
19313        _: &mut Window,
19314        cx: &mut Context<Self>,
19315    ) {
19316        if let Some(project) = self.project.clone() {
19317            self.buffer.update(cx, |multi_buffer, cx| {
19318                project.update(cx, |project, cx| {
19319                    project.restart_language_servers_for_buffers(
19320                        multi_buffer.all_buffers().into_iter().collect(),
19321                        HashSet::default(),
19322                        cx,
19323                    );
19324                });
19325            })
19326        }
19327    }
19328
19329    pub fn stop_language_server(
19330        &mut self,
19331        _: &StopLanguageServer,
19332        _: &mut Window,
19333        cx: &mut Context<Self>,
19334    ) {
19335        if let Some(project) = self.project.clone() {
19336            self.buffer.update(cx, |multi_buffer, cx| {
19337                project.update(cx, |project, cx| {
19338                    project.stop_language_servers_for_buffers(
19339                        multi_buffer.all_buffers().into_iter().collect(),
19340                        HashSet::default(),
19341                        cx,
19342                    );
19343                });
19344            });
19345        }
19346    }
19347
19348    fn cancel_language_server_work(
19349        workspace: &mut Workspace,
19350        _: &actions::CancelLanguageServerWork,
19351        _: &mut Window,
19352        cx: &mut Context<Workspace>,
19353    ) {
19354        let project = workspace.project();
19355        let buffers = workspace
19356            .active_item(cx)
19357            .and_then(|item| item.act_as::<Editor>(cx))
19358            .map_or(HashSet::default(), |editor| {
19359                editor.read(cx).buffer.read(cx).all_buffers()
19360            });
19361        project.update(cx, |project, cx| {
19362            project.cancel_language_server_work_for_buffers(buffers, cx);
19363        });
19364    }
19365
19366    fn show_character_palette(
19367        &mut self,
19368        _: &ShowCharacterPalette,
19369        window: &mut Window,
19370        _: &mut Context<Self>,
19371    ) {
19372        window.show_character_palette();
19373    }
19374
19375    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19376        if !self.diagnostics_enabled() {
19377            return;
19378        }
19379
19380        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19381            let buffer = self.buffer.read(cx).snapshot(cx);
19382            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19383            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19384            let is_valid = buffer
19385                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19386                .any(|entry| {
19387                    entry.diagnostic.is_primary
19388                        && !entry.range.is_empty()
19389                        && entry.range.start == primary_range_start
19390                        && entry.diagnostic.message == active_diagnostics.active_message
19391                });
19392
19393            if !is_valid {
19394                self.dismiss_diagnostics(cx);
19395            }
19396        }
19397    }
19398
19399    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19400        match &self.active_diagnostics {
19401            ActiveDiagnostic::Group(group) => Some(group),
19402            _ => None,
19403        }
19404    }
19405
19406    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19407        if !self.diagnostics_enabled() {
19408            return;
19409        }
19410        self.dismiss_diagnostics(cx);
19411        self.active_diagnostics = ActiveDiagnostic::All;
19412    }
19413
19414    fn activate_diagnostics(
19415        &mut self,
19416        buffer_id: BufferId,
19417        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19418        window: &mut Window,
19419        cx: &mut Context<Self>,
19420    ) {
19421        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19422            return;
19423        }
19424        self.dismiss_diagnostics(cx);
19425        let snapshot = self.snapshot(window, cx);
19426        let buffer = self.buffer.read(cx).snapshot(cx);
19427        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19428            return;
19429        };
19430
19431        let diagnostic_group = buffer
19432            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19433            .collect::<Vec<_>>();
19434
19435        let language_registry = self
19436            .project()
19437            .map(|project| project.read(cx).languages().clone());
19438
19439        let blocks = renderer.render_group(
19440            diagnostic_group,
19441            buffer_id,
19442            snapshot,
19443            cx.weak_entity(),
19444            language_registry,
19445            cx,
19446        );
19447
19448        let blocks = self.display_map.update(cx, |display_map, cx| {
19449            display_map.insert_blocks(blocks, cx).into_iter().collect()
19450        });
19451        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19452            active_range: buffer.anchor_before(diagnostic.range.start)
19453                ..buffer.anchor_after(diagnostic.range.end),
19454            active_message: diagnostic.diagnostic.message.clone(),
19455            group_id: diagnostic.diagnostic.group_id,
19456            blocks,
19457        });
19458        cx.notify();
19459    }
19460
19461    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19462        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19463            return;
19464        };
19465
19466        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19467        if let ActiveDiagnostic::Group(group) = prev {
19468            self.display_map.update(cx, |display_map, cx| {
19469                display_map.remove_blocks(group.blocks, cx);
19470            });
19471            cx.notify();
19472        }
19473    }
19474
19475    /// Disable inline diagnostics rendering for this editor.
19476    pub fn disable_inline_diagnostics(&mut self) {
19477        self.inline_diagnostics_enabled = false;
19478        self.inline_diagnostics_update = Task::ready(());
19479        self.inline_diagnostics.clear();
19480    }
19481
19482    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19483        self.diagnostics_enabled = false;
19484        self.dismiss_diagnostics(cx);
19485        self.inline_diagnostics_update = Task::ready(());
19486        self.inline_diagnostics.clear();
19487    }
19488
19489    pub fn disable_word_completions(&mut self) {
19490        self.word_completions_enabled = false;
19491    }
19492
19493    pub fn diagnostics_enabled(&self) -> bool {
19494        self.diagnostics_enabled && self.mode.is_full()
19495    }
19496
19497    pub fn inline_diagnostics_enabled(&self) -> bool {
19498        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19499    }
19500
19501    pub fn show_inline_diagnostics(&self) -> bool {
19502        self.show_inline_diagnostics
19503    }
19504
19505    pub fn toggle_inline_diagnostics(
19506        &mut self,
19507        _: &ToggleInlineDiagnostics,
19508        window: &mut Window,
19509        cx: &mut Context<Editor>,
19510    ) {
19511        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19512        self.refresh_inline_diagnostics(false, window, cx);
19513    }
19514
19515    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19516        self.diagnostics_max_severity = severity;
19517        self.display_map.update(cx, |display_map, _| {
19518            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19519        });
19520    }
19521
19522    pub fn toggle_diagnostics(
19523        &mut self,
19524        _: &ToggleDiagnostics,
19525        window: &mut Window,
19526        cx: &mut Context<Editor>,
19527    ) {
19528        if !self.diagnostics_enabled() {
19529            return;
19530        }
19531
19532        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19533            EditorSettings::get_global(cx)
19534                .diagnostics_max_severity
19535                .filter(|severity| severity != &DiagnosticSeverity::Off)
19536                .unwrap_or(DiagnosticSeverity::Hint)
19537        } else {
19538            DiagnosticSeverity::Off
19539        };
19540        self.set_max_diagnostics_severity(new_severity, cx);
19541        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19542            self.active_diagnostics = ActiveDiagnostic::None;
19543            self.inline_diagnostics_update = Task::ready(());
19544            self.inline_diagnostics.clear();
19545        } else {
19546            self.refresh_inline_diagnostics(false, window, cx);
19547        }
19548
19549        cx.notify();
19550    }
19551
19552    pub fn toggle_minimap(
19553        &mut self,
19554        _: &ToggleMinimap,
19555        window: &mut Window,
19556        cx: &mut Context<Editor>,
19557    ) {
19558        if self.supports_minimap(cx) {
19559            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19560        }
19561    }
19562
19563    fn refresh_inline_diagnostics(
19564        &mut self,
19565        debounce: bool,
19566        window: &mut Window,
19567        cx: &mut Context<Self>,
19568    ) {
19569        let max_severity = ProjectSettings::get_global(cx)
19570            .diagnostics
19571            .inline
19572            .max_severity
19573            .unwrap_or(self.diagnostics_max_severity);
19574
19575        if !self.inline_diagnostics_enabled()
19576            || !self.diagnostics_enabled()
19577            || !self.show_inline_diagnostics
19578            || max_severity == DiagnosticSeverity::Off
19579        {
19580            self.inline_diagnostics_update = Task::ready(());
19581            self.inline_diagnostics.clear();
19582            return;
19583        }
19584
19585        let debounce_ms = ProjectSettings::get_global(cx)
19586            .diagnostics
19587            .inline
19588            .update_debounce_ms;
19589        let debounce = if debounce && debounce_ms > 0 {
19590            Some(Duration::from_millis(debounce_ms))
19591        } else {
19592            None
19593        };
19594        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19595            if let Some(debounce) = debounce {
19596                cx.background_executor().timer(debounce).await;
19597            }
19598            let Some(snapshot) = editor.upgrade().map(|editor| {
19599                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19600            }) else {
19601                return;
19602            };
19603
19604            let new_inline_diagnostics = cx
19605                .background_spawn(async move {
19606                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19607                    for diagnostic_entry in
19608                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19609                    {
19610                        let message = diagnostic_entry
19611                            .diagnostic
19612                            .message
19613                            .split_once('\n')
19614                            .map(|(line, _)| line)
19615                            .map(SharedString::new)
19616                            .unwrap_or_else(|| {
19617                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19618                            });
19619                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19620                        let (Ok(i) | Err(i)) = inline_diagnostics
19621                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19622                        inline_diagnostics.insert(
19623                            i,
19624                            (
19625                                start_anchor,
19626                                InlineDiagnostic {
19627                                    message,
19628                                    group_id: diagnostic_entry.diagnostic.group_id,
19629                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19630                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19631                                    severity: diagnostic_entry.diagnostic.severity,
19632                                },
19633                            ),
19634                        );
19635                    }
19636                    inline_diagnostics
19637                })
19638                .await;
19639
19640            editor
19641                .update(cx, |editor, cx| {
19642                    editor.inline_diagnostics = new_inline_diagnostics;
19643                    cx.notify();
19644                })
19645                .ok();
19646        });
19647    }
19648
19649    fn pull_diagnostics(
19650        &mut self,
19651        buffer_id: BufferId,
19652        _window: &Window,
19653        cx: &mut Context<Self>,
19654    ) -> Option<()> {
19655        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19656        // skip any LSP updates for it.
19657
19658        if self.active_diagnostics == ActiveDiagnostic::All
19659            || !self.mode().is_full()
19660            || !self.diagnostics_enabled()
19661        {
19662            return None;
19663        }
19664        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19665            .diagnostics
19666            .lsp_pull_diagnostics;
19667        if !pull_diagnostics_settings.enabled {
19668            return None;
19669        }
19670        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19671        let project = self.project()?.downgrade();
19672        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19673
19674        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19675            cx.background_executor().timer(debounce).await;
19676            if let Ok(task) = project.update(cx, |project, cx| {
19677                project.lsp_store().update(cx, |lsp_store, cx| {
19678                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19679                })
19680            }) {
19681                task.await.log_err();
19682            }
19683            project
19684                .update(cx, |project, cx| {
19685                    project.lsp_store().update(cx, |lsp_store, cx| {
19686                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19687                    })
19688                })
19689                .log_err();
19690        });
19691
19692        Some(())
19693    }
19694
19695    pub fn set_selections_from_remote(
19696        &mut self,
19697        selections: Vec<Selection<Anchor>>,
19698        pending_selection: Option<Selection<Anchor>>,
19699        window: &mut Window,
19700        cx: &mut Context<Self>,
19701    ) {
19702        let old_cursor_position = self.selections.newest_anchor().head();
19703        self.selections
19704            .change_with(&self.display_snapshot(cx), |s| {
19705                s.select_anchors(selections);
19706                if let Some(pending_selection) = pending_selection {
19707                    s.set_pending(pending_selection, SelectMode::Character);
19708                } else {
19709                    s.clear_pending();
19710                }
19711            });
19712        self.selections_did_change(
19713            false,
19714            &old_cursor_position,
19715            SelectionEffects::default(),
19716            window,
19717            cx,
19718        );
19719    }
19720
19721    pub fn transact(
19722        &mut self,
19723        window: &mut Window,
19724        cx: &mut Context<Self>,
19725        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19726    ) -> Option<TransactionId> {
19727        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19728            this.start_transaction_at(Instant::now(), window, cx);
19729            update(this, window, cx);
19730            this.end_transaction_at(Instant::now(), cx)
19731        })
19732    }
19733
19734    pub fn start_transaction_at(
19735        &mut self,
19736        now: Instant,
19737        window: &mut Window,
19738        cx: &mut Context<Self>,
19739    ) -> Option<TransactionId> {
19740        self.end_selection(window, cx);
19741        if let Some(tx_id) = self
19742            .buffer
19743            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19744        {
19745            self.selection_history
19746                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19747            cx.emit(EditorEvent::TransactionBegun {
19748                transaction_id: tx_id,
19749            });
19750            Some(tx_id)
19751        } else {
19752            None
19753        }
19754    }
19755
19756    pub fn end_transaction_at(
19757        &mut self,
19758        now: Instant,
19759        cx: &mut Context<Self>,
19760    ) -> Option<TransactionId> {
19761        if let Some(transaction_id) = self
19762            .buffer
19763            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19764        {
19765            if let Some((_, end_selections)) =
19766                self.selection_history.transaction_mut(transaction_id)
19767            {
19768                *end_selections = Some(self.selections.disjoint_anchors_arc());
19769            } else {
19770                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19771            }
19772
19773            cx.emit(EditorEvent::Edited { transaction_id });
19774            Some(transaction_id)
19775        } else {
19776            None
19777        }
19778    }
19779
19780    pub fn modify_transaction_selection_history(
19781        &mut self,
19782        transaction_id: TransactionId,
19783        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19784    ) -> bool {
19785        self.selection_history
19786            .transaction_mut(transaction_id)
19787            .map(modify)
19788            .is_some()
19789    }
19790
19791    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19792        if self.selection_mark_mode {
19793            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19794                s.move_with(&mut |_, sel| {
19795                    sel.collapse_to(sel.head(), SelectionGoal::None);
19796                });
19797            })
19798        }
19799        self.selection_mark_mode = true;
19800        cx.notify();
19801    }
19802
19803    pub fn swap_selection_ends(
19804        &mut self,
19805        _: &actions::SwapSelectionEnds,
19806        window: &mut Window,
19807        cx: &mut Context<Self>,
19808    ) {
19809        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19810            s.move_with(&mut |_, sel| {
19811                if sel.start != sel.end {
19812                    sel.reversed = !sel.reversed
19813                }
19814            });
19815        });
19816        self.request_autoscroll(Autoscroll::newest(), cx);
19817        cx.notify();
19818    }
19819
19820    pub fn toggle_focus(
19821        workspace: &mut Workspace,
19822        _: &actions::ToggleFocus,
19823        window: &mut Window,
19824        cx: &mut Context<Workspace>,
19825    ) {
19826        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19827            return;
19828        };
19829        workspace.activate_item(&item, true, true, window, cx);
19830    }
19831
19832    pub fn toggle_fold(
19833        &mut self,
19834        _: &actions::ToggleFold,
19835        window: &mut Window,
19836        cx: &mut Context<Self>,
19837    ) {
19838        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19839            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19840            let selection = self.selections.newest::<Point>(&display_map);
19841
19842            let range = if selection.is_empty() {
19843                let point = selection.head().to_display_point(&display_map);
19844                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19845                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19846                    .to_point(&display_map);
19847                start..end
19848            } else {
19849                selection.range()
19850            };
19851            if display_map.folds_in_range(range).next().is_some() {
19852                self.unfold_lines(&Default::default(), window, cx)
19853            } else {
19854                self.fold(&Default::default(), window, cx)
19855            }
19856        } else {
19857            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19858            let buffer_ids: HashSet<_> = self
19859                .selections
19860                .disjoint_anchor_ranges()
19861                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19862                .collect();
19863
19864            let should_unfold = buffer_ids
19865                .iter()
19866                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19867
19868            for buffer_id in buffer_ids {
19869                if should_unfold {
19870                    self.unfold_buffer(buffer_id, cx);
19871                } else {
19872                    self.fold_buffer(buffer_id, cx);
19873                }
19874            }
19875        }
19876    }
19877
19878    pub fn toggle_fold_recursive(
19879        &mut self,
19880        _: &actions::ToggleFoldRecursive,
19881        window: &mut Window,
19882        cx: &mut Context<Self>,
19883    ) {
19884        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19885
19886        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19887        let range = if selection.is_empty() {
19888            let point = selection.head().to_display_point(&display_map);
19889            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19890            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19891                .to_point(&display_map);
19892            start..end
19893        } else {
19894            selection.range()
19895        };
19896        if display_map.folds_in_range(range).next().is_some() {
19897            self.unfold_recursive(&Default::default(), window, cx)
19898        } else {
19899            self.fold_recursive(&Default::default(), window, cx)
19900        }
19901    }
19902
19903    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19904        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19905            let mut to_fold = Vec::new();
19906            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19907            let selections = self.selections.all_adjusted(&display_map);
19908
19909            for selection in selections {
19910                let range = selection.range().sorted();
19911                let buffer_start_row = range.start.row;
19912
19913                if range.start.row != range.end.row {
19914                    let mut found = false;
19915                    let mut row = range.start.row;
19916                    while row <= range.end.row {
19917                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19918                        {
19919                            found = true;
19920                            row = crease.range().end.row + 1;
19921                            to_fold.push(crease);
19922                        } else {
19923                            row += 1
19924                        }
19925                    }
19926                    if found {
19927                        continue;
19928                    }
19929                }
19930
19931                for row in (0..=range.start.row).rev() {
19932                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19933                        && crease.range().end.row >= buffer_start_row
19934                    {
19935                        to_fold.push(crease);
19936                        if row <= range.start.row {
19937                            break;
19938                        }
19939                    }
19940                }
19941            }
19942
19943            self.fold_creases(to_fold, true, window, cx);
19944        } else {
19945            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19946            let buffer_ids = self
19947                .selections
19948                .disjoint_anchor_ranges()
19949                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19950                .collect::<HashSet<_>>();
19951            for buffer_id in buffer_ids {
19952                self.fold_buffer(buffer_id, cx);
19953            }
19954        }
19955    }
19956
19957    pub fn toggle_fold_all(
19958        &mut self,
19959        _: &actions::ToggleFoldAll,
19960        window: &mut Window,
19961        cx: &mut Context<Self>,
19962    ) {
19963        let has_folds = if self.buffer.read(cx).is_singleton() {
19964            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19965            let has_folds = display_map
19966                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19967                .next()
19968                .is_some();
19969            has_folds
19970        } else {
19971            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19972            let has_folds = buffer_ids
19973                .iter()
19974                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19975            has_folds
19976        };
19977
19978        if has_folds {
19979            self.unfold_all(&actions::UnfoldAll, window, cx);
19980        } else {
19981            self.fold_all(&actions::FoldAll, window, cx);
19982        }
19983    }
19984
19985    fn fold_at_level(
19986        &mut self,
19987        fold_at: &FoldAtLevel,
19988        window: &mut Window,
19989        cx: &mut Context<Self>,
19990    ) {
19991        if !self.buffer.read(cx).is_singleton() {
19992            return;
19993        }
19994
19995        let fold_at_level = fold_at.0;
19996        let snapshot = self.buffer.read(cx).snapshot(cx);
19997        let mut to_fold = Vec::new();
19998        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19999
20000        let row_ranges_to_keep: Vec<Range<u32>> = self
20001            .selections
20002            .all::<Point>(&self.display_snapshot(cx))
20003            .into_iter()
20004            .map(|sel| sel.start.row..sel.end.row)
20005            .collect();
20006
20007        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20008            while start_row < end_row {
20009                match self
20010                    .snapshot(window, cx)
20011                    .crease_for_buffer_row(MultiBufferRow(start_row))
20012                {
20013                    Some(crease) => {
20014                        let nested_start_row = crease.range().start.row + 1;
20015                        let nested_end_row = crease.range().end.row;
20016
20017                        if current_level < fold_at_level {
20018                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20019                        } else if current_level == fold_at_level {
20020                            // Fold iff there is no selection completely contained within the fold region
20021                            if !row_ranges_to_keep.iter().any(|selection| {
20022                                selection.end >= nested_start_row
20023                                    && selection.start <= nested_end_row
20024                            }) {
20025                                to_fold.push(crease);
20026                            }
20027                        }
20028
20029                        start_row = nested_end_row + 1;
20030                    }
20031                    None => start_row += 1,
20032                }
20033            }
20034        }
20035
20036        self.fold_creases(to_fold, true, window, cx);
20037    }
20038
20039    pub fn fold_at_level_1(
20040        &mut self,
20041        _: &actions::FoldAtLevel1,
20042        window: &mut Window,
20043        cx: &mut Context<Self>,
20044    ) {
20045        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20046    }
20047
20048    pub fn fold_at_level_2(
20049        &mut self,
20050        _: &actions::FoldAtLevel2,
20051        window: &mut Window,
20052        cx: &mut Context<Self>,
20053    ) {
20054        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20055    }
20056
20057    pub fn fold_at_level_3(
20058        &mut self,
20059        _: &actions::FoldAtLevel3,
20060        window: &mut Window,
20061        cx: &mut Context<Self>,
20062    ) {
20063        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20064    }
20065
20066    pub fn fold_at_level_4(
20067        &mut self,
20068        _: &actions::FoldAtLevel4,
20069        window: &mut Window,
20070        cx: &mut Context<Self>,
20071    ) {
20072        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20073    }
20074
20075    pub fn fold_at_level_5(
20076        &mut self,
20077        _: &actions::FoldAtLevel5,
20078        window: &mut Window,
20079        cx: &mut Context<Self>,
20080    ) {
20081        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20082    }
20083
20084    pub fn fold_at_level_6(
20085        &mut self,
20086        _: &actions::FoldAtLevel6,
20087        window: &mut Window,
20088        cx: &mut Context<Self>,
20089    ) {
20090        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20091    }
20092
20093    pub fn fold_at_level_7(
20094        &mut self,
20095        _: &actions::FoldAtLevel7,
20096        window: &mut Window,
20097        cx: &mut Context<Self>,
20098    ) {
20099        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20100    }
20101
20102    pub fn fold_at_level_8(
20103        &mut self,
20104        _: &actions::FoldAtLevel8,
20105        window: &mut Window,
20106        cx: &mut Context<Self>,
20107    ) {
20108        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20109    }
20110
20111    pub fn fold_at_level_9(
20112        &mut self,
20113        _: &actions::FoldAtLevel9,
20114        window: &mut Window,
20115        cx: &mut Context<Self>,
20116    ) {
20117        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20118    }
20119
20120    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20121        if self.buffer.read(cx).is_singleton() {
20122            let mut fold_ranges = Vec::new();
20123            let snapshot = self.buffer.read(cx).snapshot(cx);
20124
20125            for row in 0..snapshot.max_row().0 {
20126                if let Some(foldable_range) = self
20127                    .snapshot(window, cx)
20128                    .crease_for_buffer_row(MultiBufferRow(row))
20129                {
20130                    fold_ranges.push(foldable_range);
20131                }
20132            }
20133
20134            self.fold_creases(fold_ranges, true, window, cx);
20135        } else {
20136            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20137                editor
20138                    .update_in(cx, |editor, _, cx| {
20139                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20140                            editor.fold_buffer(buffer_id, cx);
20141                        }
20142                    })
20143                    .ok();
20144            });
20145        }
20146    }
20147
20148    pub fn fold_function_bodies(
20149        &mut self,
20150        _: &actions::FoldFunctionBodies,
20151        window: &mut Window,
20152        cx: &mut Context<Self>,
20153    ) {
20154        let snapshot = self.buffer.read(cx).snapshot(cx);
20155
20156        let ranges = snapshot
20157            .text_object_ranges(
20158                MultiBufferOffset(0)..snapshot.len(),
20159                TreeSitterOptions::default(),
20160            )
20161            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20162            .collect::<Vec<_>>();
20163
20164        let creases = ranges
20165            .into_iter()
20166            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20167            .collect();
20168
20169        self.fold_creases(creases, true, window, cx);
20170    }
20171
20172    pub fn fold_recursive(
20173        &mut self,
20174        _: &actions::FoldRecursive,
20175        window: &mut Window,
20176        cx: &mut Context<Self>,
20177    ) {
20178        let mut to_fold = Vec::new();
20179        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20180        let selections = self.selections.all_adjusted(&display_map);
20181
20182        for selection in selections {
20183            let range = selection.range().sorted();
20184            let buffer_start_row = range.start.row;
20185
20186            if range.start.row != range.end.row {
20187                let mut found = false;
20188                for row in range.start.row..=range.end.row {
20189                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20190                        found = true;
20191                        to_fold.push(crease);
20192                    }
20193                }
20194                if found {
20195                    continue;
20196                }
20197            }
20198
20199            for row in (0..=range.start.row).rev() {
20200                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20201                    if crease.range().end.row >= buffer_start_row {
20202                        to_fold.push(crease);
20203                    } else {
20204                        break;
20205                    }
20206                }
20207            }
20208        }
20209
20210        self.fold_creases(to_fold, true, window, cx);
20211    }
20212
20213    pub fn fold_at(
20214        &mut self,
20215        buffer_row: MultiBufferRow,
20216        window: &mut Window,
20217        cx: &mut Context<Self>,
20218    ) {
20219        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20220
20221        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20222            let autoscroll = self
20223                .selections
20224                .all::<Point>(&display_map)
20225                .iter()
20226                .any(|selection| crease.range().overlaps(&selection.range()));
20227
20228            self.fold_creases(vec![crease], autoscroll, window, cx);
20229        }
20230    }
20231
20232    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20233        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20234            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20235            let buffer = display_map.buffer_snapshot();
20236            let selections = self.selections.all::<Point>(&display_map);
20237            let ranges = selections
20238                .iter()
20239                .map(|s| {
20240                    let range = s.display_range(&display_map).sorted();
20241                    let mut start = range.start.to_point(&display_map);
20242                    let mut end = range.end.to_point(&display_map);
20243                    start.column = 0;
20244                    end.column = buffer.line_len(MultiBufferRow(end.row));
20245                    start..end
20246                })
20247                .collect::<Vec<_>>();
20248
20249            self.unfold_ranges(&ranges, true, true, cx);
20250        } else {
20251            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20252            let buffer_ids = self
20253                .selections
20254                .disjoint_anchor_ranges()
20255                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20256                .collect::<HashSet<_>>();
20257            for buffer_id in buffer_ids {
20258                self.unfold_buffer(buffer_id, cx);
20259            }
20260        }
20261    }
20262
20263    pub fn unfold_recursive(
20264        &mut self,
20265        _: &UnfoldRecursive,
20266        _window: &mut Window,
20267        cx: &mut Context<Self>,
20268    ) {
20269        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20270        let selections = self.selections.all::<Point>(&display_map);
20271        let ranges = selections
20272            .iter()
20273            .map(|s| {
20274                let mut range = s.display_range(&display_map).sorted();
20275                *range.start.column_mut() = 0;
20276                *range.end.column_mut() = display_map.line_len(range.end.row());
20277                let start = range.start.to_point(&display_map);
20278                let end = range.end.to_point(&display_map);
20279                start..end
20280            })
20281            .collect::<Vec<_>>();
20282
20283        self.unfold_ranges(&ranges, true, true, cx);
20284    }
20285
20286    pub fn unfold_at(
20287        &mut self,
20288        buffer_row: MultiBufferRow,
20289        _window: &mut Window,
20290        cx: &mut Context<Self>,
20291    ) {
20292        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20293
20294        let intersection_range = Point::new(buffer_row.0, 0)
20295            ..Point::new(
20296                buffer_row.0,
20297                display_map.buffer_snapshot().line_len(buffer_row),
20298            );
20299
20300        let autoscroll = self
20301            .selections
20302            .all::<Point>(&display_map)
20303            .iter()
20304            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20305
20306        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20307    }
20308
20309    pub fn unfold_all(
20310        &mut self,
20311        _: &actions::UnfoldAll,
20312        _window: &mut Window,
20313        cx: &mut Context<Self>,
20314    ) {
20315        if self.buffer.read(cx).is_singleton() {
20316            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20317            self.unfold_ranges(
20318                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20319                true,
20320                true,
20321                cx,
20322            );
20323        } else {
20324            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20325                editor
20326                    .update(cx, |editor, cx| {
20327                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20328                            editor.unfold_buffer(buffer_id, cx);
20329                        }
20330                    })
20331                    .ok();
20332            });
20333        }
20334    }
20335
20336    pub fn fold_selected_ranges(
20337        &mut self,
20338        _: &FoldSelectedRanges,
20339        window: &mut Window,
20340        cx: &mut Context<Self>,
20341    ) {
20342        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20343        let selections = self.selections.all_adjusted(&display_map);
20344        let ranges = selections
20345            .into_iter()
20346            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20347            .collect::<Vec<_>>();
20348        self.fold_creases(ranges, true, window, cx);
20349    }
20350
20351    pub fn fold_ranges<T: ToOffset + Clone>(
20352        &mut self,
20353        ranges: Vec<Range<T>>,
20354        auto_scroll: bool,
20355        window: &mut Window,
20356        cx: &mut Context<Self>,
20357    ) {
20358        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20359        let ranges = ranges
20360            .into_iter()
20361            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20362            .collect::<Vec<_>>();
20363        self.fold_creases(ranges, auto_scroll, window, cx);
20364    }
20365
20366    pub fn fold_creases<T: ToOffset + Clone>(
20367        &mut self,
20368        creases: Vec<Crease<T>>,
20369        auto_scroll: bool,
20370        _window: &mut Window,
20371        cx: &mut Context<Self>,
20372    ) {
20373        if creases.is_empty() {
20374            return;
20375        }
20376
20377        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20378
20379        if auto_scroll {
20380            self.request_autoscroll(Autoscroll::fit(), cx);
20381        }
20382
20383        cx.notify();
20384
20385        self.scrollbar_marker_state.dirty = true;
20386        self.folds_did_change(cx);
20387    }
20388
20389    /// Removes any folds whose ranges intersect any of the given ranges.
20390    pub fn unfold_ranges<T: ToOffset + Clone>(
20391        &mut self,
20392        ranges: &[Range<T>],
20393        inclusive: bool,
20394        auto_scroll: bool,
20395        cx: &mut Context<Self>,
20396    ) {
20397        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20398            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20399        });
20400        self.folds_did_change(cx);
20401    }
20402
20403    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20404        self.fold_buffers([buffer_id], cx);
20405    }
20406
20407    pub fn fold_buffers(
20408        &mut self,
20409        buffer_ids: impl IntoIterator<Item = BufferId>,
20410        cx: &mut Context<Self>,
20411    ) {
20412        if self.buffer().read(cx).is_singleton() {
20413            return;
20414        }
20415
20416        let ids_to_fold: Vec<BufferId> = buffer_ids
20417            .into_iter()
20418            .filter(|id| !self.is_buffer_folded(*id, cx))
20419            .collect();
20420
20421        if ids_to_fold.is_empty() {
20422            return;
20423        }
20424
20425        let mut all_folded_excerpt_ids = Vec::new();
20426        for buffer_id in &ids_to_fold {
20427            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20428            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
20429        }
20430
20431        self.display_map.update(cx, |display_map, cx| {
20432            display_map.fold_buffers(ids_to_fold.clone(), cx)
20433        });
20434
20435        let snapshot = self.display_snapshot(cx);
20436        self.selections.change_with(&snapshot, |selections| {
20437            for buffer_id in ids_to_fold {
20438                selections.remove_selections_from_buffer(buffer_id);
20439            }
20440        });
20441
20442        cx.emit(EditorEvent::BufferFoldToggled {
20443            ids: all_folded_excerpt_ids,
20444            folded: true,
20445        });
20446        cx.notify();
20447    }
20448
20449    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20450        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20451            return;
20452        }
20453        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20454        self.display_map.update(cx, |display_map, cx| {
20455            display_map.unfold_buffers([buffer_id], cx);
20456        });
20457        cx.emit(EditorEvent::BufferFoldToggled {
20458            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20459            folded: false,
20460        });
20461        cx.notify();
20462    }
20463
20464    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20465        self.display_map.read(cx).is_buffer_folded(buffer)
20466    }
20467
20468    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20469        if self.buffer().read(cx).is_singleton() {
20470            return false;
20471        }
20472        !self.folded_buffers(cx).is_empty()
20473    }
20474
20475    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20476        self.display_map.read(cx).folded_buffers()
20477    }
20478
20479    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20480        self.display_map.update(cx, |display_map, cx| {
20481            display_map.disable_header_for_buffer(buffer_id, cx);
20482        });
20483        cx.notify();
20484    }
20485
20486    /// Removes any folds with the given ranges.
20487    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20488        &mut self,
20489        ranges: &[Range<T>],
20490        type_id: TypeId,
20491        auto_scroll: bool,
20492        cx: &mut Context<Self>,
20493    ) {
20494        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20495            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20496        });
20497        self.folds_did_change(cx);
20498    }
20499
20500    fn remove_folds_with<T: ToOffset + Clone>(
20501        &mut self,
20502        ranges: &[Range<T>],
20503        auto_scroll: bool,
20504        cx: &mut Context<Self>,
20505        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20506    ) {
20507        if ranges.is_empty() {
20508            return;
20509        }
20510
20511        let mut buffers_affected = HashSet::default();
20512        let multi_buffer = self.buffer().read(cx);
20513        for range in ranges {
20514            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20515                buffers_affected.insert(buffer.read(cx).remote_id());
20516            };
20517        }
20518
20519        self.display_map.update(cx, update);
20520
20521        if auto_scroll {
20522            self.request_autoscroll(Autoscroll::fit(), cx);
20523        }
20524
20525        cx.notify();
20526        self.scrollbar_marker_state.dirty = true;
20527        self.active_indent_guides_state.dirty = true;
20528    }
20529
20530    pub fn update_renderer_widths(
20531        &mut self,
20532        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20533        cx: &mut Context<Self>,
20534    ) -> bool {
20535        self.display_map
20536            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20537    }
20538
20539    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20540        self.display_map.read(cx).fold_placeholder.clone()
20541    }
20542
20543    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20544        self.buffer.update(cx, |buffer, cx| {
20545            buffer.set_all_diff_hunks_expanded(cx);
20546        });
20547    }
20548
20549    pub fn expand_all_diff_hunks(
20550        &mut self,
20551        _: &ExpandAllDiffHunks,
20552        _window: &mut Window,
20553        cx: &mut Context<Self>,
20554    ) {
20555        self.buffer.update(cx, |buffer, cx| {
20556            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20557        });
20558    }
20559
20560    pub fn collapse_all_diff_hunks(
20561        &mut self,
20562        _: &CollapseAllDiffHunks,
20563        _window: &mut Window,
20564        cx: &mut Context<Self>,
20565    ) {
20566        self.buffer.update(cx, |buffer, cx| {
20567            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20568        });
20569    }
20570
20571    pub fn toggle_selected_diff_hunks(
20572        &mut self,
20573        _: &ToggleSelectedDiffHunks,
20574        _window: &mut Window,
20575        cx: &mut Context<Self>,
20576    ) {
20577        let ranges: Vec<_> = self
20578            .selections
20579            .disjoint_anchors()
20580            .iter()
20581            .map(|s| s.range())
20582            .collect();
20583        self.toggle_diff_hunks_in_ranges(ranges, cx);
20584    }
20585
20586    pub fn diff_hunks_in_ranges<'a>(
20587        &'a self,
20588        ranges: &'a [Range<Anchor>],
20589        buffer: &'a MultiBufferSnapshot,
20590    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20591        ranges.iter().flat_map(move |range| {
20592            let end_excerpt_id = range.end.excerpt_id;
20593            let range = range.to_point(buffer);
20594            let mut peek_end = range.end;
20595            if range.end.row < buffer.max_row().0 {
20596                peek_end = Point::new(range.end.row + 1, 0);
20597            }
20598            buffer
20599                .diff_hunks_in_range(range.start..peek_end)
20600                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20601        })
20602    }
20603
20604    pub fn has_stageable_diff_hunks_in_ranges(
20605        &self,
20606        ranges: &[Range<Anchor>],
20607        snapshot: &MultiBufferSnapshot,
20608    ) -> bool {
20609        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20610        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20611    }
20612
20613    pub fn toggle_staged_selected_diff_hunks(
20614        &mut self,
20615        _: &::git::ToggleStaged,
20616        _: &mut Window,
20617        cx: &mut Context<Self>,
20618    ) {
20619        let snapshot = self.buffer.read(cx).snapshot(cx);
20620        let ranges: Vec<_> = self
20621            .selections
20622            .disjoint_anchors()
20623            .iter()
20624            .map(|s| s.range())
20625            .collect();
20626        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20627        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20628    }
20629
20630    pub fn set_render_diff_hunk_controls(
20631        &mut self,
20632        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20633        cx: &mut Context<Self>,
20634    ) {
20635        self.render_diff_hunk_controls = render_diff_hunk_controls;
20636        cx.notify();
20637    }
20638
20639    pub fn stage_and_next(
20640        &mut self,
20641        _: &::git::StageAndNext,
20642        window: &mut Window,
20643        cx: &mut Context<Self>,
20644    ) {
20645        self.do_stage_or_unstage_and_next(true, window, cx);
20646    }
20647
20648    pub fn unstage_and_next(
20649        &mut self,
20650        _: &::git::UnstageAndNext,
20651        window: &mut Window,
20652        cx: &mut Context<Self>,
20653    ) {
20654        self.do_stage_or_unstage_and_next(false, window, cx);
20655    }
20656
20657    pub fn stage_or_unstage_diff_hunks(
20658        &mut self,
20659        stage: bool,
20660        ranges: Vec<Range<Anchor>>,
20661        cx: &mut Context<Self>,
20662    ) {
20663        if self.delegate_stage_and_restore {
20664            let snapshot = self.buffer.read(cx).snapshot(cx);
20665            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20666            if !hunks.is_empty() {
20667                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20668            }
20669            return;
20670        }
20671        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20672        cx.spawn(async move |this, cx| {
20673            task.await?;
20674            this.update(cx, |this, cx| {
20675                let snapshot = this.buffer.read(cx).snapshot(cx);
20676                let chunk_by = this
20677                    .diff_hunks_in_ranges(&ranges, &snapshot)
20678                    .chunk_by(|hunk| hunk.buffer_id);
20679                for (buffer_id, hunks) in &chunk_by {
20680                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20681                }
20682            })
20683        })
20684        .detach_and_log_err(cx);
20685    }
20686
20687    fn save_buffers_for_ranges_if_needed(
20688        &mut self,
20689        ranges: &[Range<Anchor>],
20690        cx: &mut Context<Editor>,
20691    ) -> Task<Result<()>> {
20692        let multibuffer = self.buffer.read(cx);
20693        let snapshot = multibuffer.read(cx);
20694        let buffer_ids: HashSet<_> = ranges
20695            .iter()
20696            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20697            .collect();
20698        drop(snapshot);
20699
20700        let mut buffers = HashSet::default();
20701        for buffer_id in buffer_ids {
20702            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20703                let buffer = buffer_entity.read(cx);
20704                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20705                {
20706                    buffers.insert(buffer_entity);
20707                }
20708            }
20709        }
20710
20711        if let Some(project) = &self.project {
20712            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20713        } else {
20714            Task::ready(Ok(()))
20715        }
20716    }
20717
20718    fn do_stage_or_unstage_and_next(
20719        &mut self,
20720        stage: bool,
20721        window: &mut Window,
20722        cx: &mut Context<Self>,
20723    ) {
20724        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20725
20726        if ranges.iter().any(|range| range.start != range.end) {
20727            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20728            return;
20729        }
20730
20731        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20732        let snapshot = self.snapshot(window, cx);
20733        let position = self
20734            .selections
20735            .newest::<Point>(&snapshot.display_snapshot)
20736            .head();
20737        let mut row = snapshot
20738            .buffer_snapshot()
20739            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20740            .find(|hunk| hunk.row_range.start.0 > position.row)
20741            .map(|hunk| hunk.row_range.start);
20742
20743        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20744        // Outside of the project diff editor, wrap around to the beginning.
20745        if !all_diff_hunks_expanded {
20746            row = row.or_else(|| {
20747                snapshot
20748                    .buffer_snapshot()
20749                    .diff_hunks_in_range(Point::zero()..position)
20750                    .find(|hunk| hunk.row_range.end.0 < position.row)
20751                    .map(|hunk| hunk.row_range.start)
20752            });
20753        }
20754
20755        if let Some(row) = row {
20756            let destination = Point::new(row.0, 0);
20757            let autoscroll = Autoscroll::center();
20758
20759            self.unfold_ranges(&[destination..destination], false, false, cx);
20760            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20761                s.select_ranges([destination..destination]);
20762            });
20763        }
20764    }
20765
20766    pub(crate) fn do_stage_or_unstage(
20767        &self,
20768        stage: bool,
20769        buffer_id: BufferId,
20770        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20771        cx: &mut App,
20772    ) -> Option<()> {
20773        let project = self.project()?;
20774        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20775        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20776        let buffer_snapshot = buffer.read(cx).snapshot();
20777        let file_exists = buffer_snapshot
20778            .file()
20779            .is_some_and(|file| file.disk_state().exists());
20780        diff.update(cx, |diff, cx| {
20781            diff.stage_or_unstage_hunks(
20782                stage,
20783                &hunks
20784                    .map(|hunk| buffer_diff::DiffHunk {
20785                        buffer_range: hunk.buffer_range,
20786                        // We don't need to pass in word diffs here because they're only used for rendering and
20787                        // this function changes internal state
20788                        base_word_diffs: Vec::default(),
20789                        buffer_word_diffs: Vec::default(),
20790                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20791                            ..hunk.diff_base_byte_range.end.0,
20792                        secondary_status: hunk.status.secondary,
20793                        range: Point::zero()..Point::zero(), // unused
20794                    })
20795                    .collect::<Vec<_>>(),
20796                &buffer_snapshot,
20797                file_exists,
20798                cx,
20799            )
20800        });
20801        None
20802    }
20803
20804    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20805        let ranges: Vec<_> = self
20806            .selections
20807            .disjoint_anchors()
20808            .iter()
20809            .map(|s| s.range())
20810            .collect();
20811        self.buffer
20812            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20813    }
20814
20815    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20816        self.buffer.update(cx, |buffer, cx| {
20817            let ranges = vec![Anchor::min()..Anchor::max()];
20818            if !buffer.all_diff_hunks_expanded()
20819                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20820            {
20821                buffer.collapse_diff_hunks(ranges, cx);
20822                true
20823            } else {
20824                false
20825            }
20826        })
20827    }
20828
20829    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20830        if self.buffer.read(cx).all_diff_hunks_expanded() {
20831            return true;
20832        }
20833        let ranges = vec![Anchor::min()..Anchor::max()];
20834        self.buffer
20835            .read(cx)
20836            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20837    }
20838
20839    fn toggle_diff_hunks_in_ranges(
20840        &mut self,
20841        ranges: Vec<Range<Anchor>>,
20842        cx: &mut Context<Editor>,
20843    ) {
20844        self.buffer.update(cx, |buffer, cx| {
20845            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20846            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20847        })
20848    }
20849
20850    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20851        self.buffer.update(cx, |buffer, cx| {
20852            buffer.toggle_single_diff_hunk(range, cx);
20853        })
20854    }
20855
20856    pub(crate) fn apply_all_diff_hunks(
20857        &mut self,
20858        _: &ApplyAllDiffHunks,
20859        window: &mut Window,
20860        cx: &mut Context<Self>,
20861    ) {
20862        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20863
20864        let buffers = self.buffer.read(cx).all_buffers();
20865        for branch_buffer in buffers {
20866            branch_buffer.update(cx, |branch_buffer, cx| {
20867                branch_buffer.merge_into_base(Vec::new(), cx);
20868            });
20869        }
20870
20871        if let Some(project) = self.project.clone() {
20872            self.save(
20873                SaveOptions {
20874                    format: true,
20875                    autosave: false,
20876                },
20877                project,
20878                window,
20879                cx,
20880            )
20881            .detach_and_log_err(cx);
20882        }
20883    }
20884
20885    pub(crate) fn apply_selected_diff_hunks(
20886        &mut self,
20887        _: &ApplyDiffHunk,
20888        window: &mut Window,
20889        cx: &mut Context<Self>,
20890    ) {
20891        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20892        let snapshot = self.snapshot(window, cx);
20893        let hunks = snapshot.hunks_for_ranges(
20894            self.selections
20895                .all(&snapshot.display_snapshot)
20896                .into_iter()
20897                .map(|selection| selection.range()),
20898        );
20899        let mut ranges_by_buffer = HashMap::default();
20900        self.transact(window, cx, |editor, _window, cx| {
20901            for hunk in hunks {
20902                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20903                    ranges_by_buffer
20904                        .entry(buffer.clone())
20905                        .or_insert_with(Vec::new)
20906                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20907                }
20908            }
20909
20910            for (buffer, ranges) in ranges_by_buffer {
20911                buffer.update(cx, |buffer, cx| {
20912                    buffer.merge_into_base(ranges, cx);
20913                });
20914            }
20915        });
20916
20917        if let Some(project) = self.project.clone() {
20918            self.save(
20919                SaveOptions {
20920                    format: true,
20921                    autosave: false,
20922                },
20923                project,
20924                window,
20925                cx,
20926            )
20927            .detach_and_log_err(cx);
20928        }
20929    }
20930
20931    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20932        if hovered != self.gutter_hovered {
20933            self.gutter_hovered = hovered;
20934            cx.notify();
20935        }
20936    }
20937
20938    pub fn insert_blocks(
20939        &mut self,
20940        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20941        autoscroll: Option<Autoscroll>,
20942        cx: &mut Context<Self>,
20943    ) -> Vec<CustomBlockId> {
20944        let blocks = self
20945            .display_map
20946            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20947        if let Some(autoscroll) = autoscroll {
20948            self.request_autoscroll(autoscroll, cx);
20949        }
20950        cx.notify();
20951        blocks
20952    }
20953
20954    pub fn resize_blocks(
20955        &mut self,
20956        heights: HashMap<CustomBlockId, u32>,
20957        autoscroll: Option<Autoscroll>,
20958        cx: &mut Context<Self>,
20959    ) {
20960        self.display_map
20961            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20962        if let Some(autoscroll) = autoscroll {
20963            self.request_autoscroll(autoscroll, cx);
20964        }
20965        cx.notify();
20966    }
20967
20968    pub fn replace_blocks(
20969        &mut self,
20970        renderers: HashMap<CustomBlockId, RenderBlock>,
20971        autoscroll: Option<Autoscroll>,
20972        cx: &mut Context<Self>,
20973    ) {
20974        self.display_map
20975            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20976        if let Some(autoscroll) = autoscroll {
20977            self.request_autoscroll(autoscroll, cx);
20978        }
20979        cx.notify();
20980    }
20981
20982    pub fn remove_blocks(
20983        &mut self,
20984        block_ids: HashSet<CustomBlockId>,
20985        autoscroll: Option<Autoscroll>,
20986        cx: &mut Context<Self>,
20987    ) {
20988        self.display_map.update(cx, |display_map, cx| {
20989            display_map.remove_blocks(block_ids, cx)
20990        });
20991        if let Some(autoscroll) = autoscroll {
20992            self.request_autoscroll(autoscroll, cx);
20993        }
20994        cx.notify();
20995    }
20996
20997    pub fn row_for_block(
20998        &self,
20999        block_id: CustomBlockId,
21000        cx: &mut Context<Self>,
21001    ) -> Option<DisplayRow> {
21002        self.display_map
21003            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21004    }
21005
21006    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21007        self.focused_block = Some(focused_block);
21008    }
21009
21010    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21011        self.focused_block.take()
21012    }
21013
21014    pub fn insert_creases(
21015        &mut self,
21016        creases: impl IntoIterator<Item = Crease<Anchor>>,
21017        cx: &mut Context<Self>,
21018    ) -> Vec<CreaseId> {
21019        self.display_map
21020            .update(cx, |map, cx| map.insert_creases(creases, cx))
21021    }
21022
21023    pub fn remove_creases(
21024        &mut self,
21025        ids: impl IntoIterator<Item = CreaseId>,
21026        cx: &mut Context<Self>,
21027    ) -> Vec<(CreaseId, Range<Anchor>)> {
21028        self.display_map
21029            .update(cx, |map, cx| map.remove_creases(ids, cx))
21030    }
21031
21032    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21033        self.display_map
21034            .update(cx, |map, cx| map.snapshot(cx))
21035            .longest_row()
21036    }
21037
21038    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21039        self.display_map
21040            .update(cx, |map, cx| map.snapshot(cx))
21041            .max_point()
21042    }
21043
21044    pub fn text(&self, cx: &App) -> String {
21045        self.buffer.read(cx).read(cx).text()
21046    }
21047
21048    pub fn is_empty(&self, cx: &App) -> bool {
21049        self.buffer.read(cx).read(cx).is_empty()
21050    }
21051
21052    pub fn text_option(&self, cx: &App) -> Option<String> {
21053        let text = self.text(cx);
21054        let text = text.trim();
21055
21056        if text.is_empty() {
21057            return None;
21058        }
21059
21060        Some(text.to_string())
21061    }
21062
21063    pub fn set_text(
21064        &mut self,
21065        text: impl Into<Arc<str>>,
21066        window: &mut Window,
21067        cx: &mut Context<Self>,
21068    ) {
21069        self.transact(window, cx, |this, _, cx| {
21070            this.buffer
21071                .read(cx)
21072                .as_singleton()
21073                .expect("you can only call set_text on editors for singleton buffers")
21074                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21075        });
21076    }
21077
21078    pub fn display_text(&self, cx: &mut App) -> String {
21079        self.display_map
21080            .update(cx, |map, cx| map.snapshot(cx))
21081            .text()
21082    }
21083
21084    fn create_minimap(
21085        &self,
21086        minimap_settings: MinimapSettings,
21087        window: &mut Window,
21088        cx: &mut Context<Self>,
21089    ) -> Option<Entity<Self>> {
21090        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21091            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21092    }
21093
21094    fn initialize_new_minimap(
21095        &self,
21096        minimap_settings: MinimapSettings,
21097        window: &mut Window,
21098        cx: &mut Context<Self>,
21099    ) -> Entity<Self> {
21100        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21101        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21102
21103        let mut minimap = Editor::new_internal(
21104            EditorMode::Minimap {
21105                parent: cx.weak_entity(),
21106            },
21107            self.buffer.clone(),
21108            None,
21109            Some(self.display_map.clone()),
21110            window,
21111            cx,
21112        );
21113        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21114        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21115        minimap.scroll_manager.clone_state(
21116            &self.scroll_manager,
21117            &my_snapshot,
21118            &minimap_snapshot,
21119            cx,
21120        );
21121        minimap.set_text_style_refinement(TextStyleRefinement {
21122            font_size: Some(MINIMAP_FONT_SIZE),
21123            font_weight: Some(MINIMAP_FONT_WEIGHT),
21124            font_family: Some(MINIMAP_FONT_FAMILY),
21125            ..Default::default()
21126        });
21127        minimap.update_minimap_configuration(minimap_settings, cx);
21128        cx.new(|_| minimap)
21129    }
21130
21131    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21132        let current_line_highlight = minimap_settings
21133            .current_line_highlight
21134            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21135        self.set_current_line_highlight(Some(current_line_highlight));
21136    }
21137
21138    pub fn minimap(&self) -> Option<&Entity<Self>> {
21139        self.minimap
21140            .as_ref()
21141            .filter(|_| self.minimap_visibility.visible())
21142    }
21143
21144    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21145        let mut wrap_guides = smallvec![];
21146
21147        if self.show_wrap_guides == Some(false) {
21148            return wrap_guides;
21149        }
21150
21151        let settings = self.buffer.read(cx).language_settings(cx);
21152        if settings.show_wrap_guides {
21153            match self.soft_wrap_mode(cx) {
21154                SoftWrap::Column(soft_wrap) => {
21155                    wrap_guides.push((soft_wrap as usize, true));
21156                }
21157                SoftWrap::Bounded(soft_wrap) => {
21158                    wrap_guides.push((soft_wrap as usize, true));
21159                }
21160                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21161            }
21162            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21163        }
21164
21165        wrap_guides
21166    }
21167
21168    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21169        let settings = self.buffer.read(cx).language_settings(cx);
21170        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21171        match mode {
21172            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21173                SoftWrap::None
21174            }
21175            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21176            language_settings::SoftWrap::PreferredLineLength => {
21177                SoftWrap::Column(settings.preferred_line_length)
21178            }
21179            language_settings::SoftWrap::Bounded => {
21180                SoftWrap::Bounded(settings.preferred_line_length)
21181            }
21182        }
21183    }
21184
21185    pub fn set_soft_wrap_mode(
21186        &mut self,
21187        mode: language_settings::SoftWrap,
21188        cx: &mut Context<Self>,
21189    ) {
21190        self.soft_wrap_mode_override = Some(mode);
21191        cx.notify();
21192    }
21193
21194    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21195        self.hard_wrap = hard_wrap;
21196        cx.notify();
21197    }
21198
21199    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21200        self.text_style_refinement = Some(style);
21201    }
21202
21203    /// called by the Element so we know what style we were most recently rendered with.
21204    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21205        // We intentionally do not inform the display map about the minimap style
21206        // so that wrapping is not recalculated and stays consistent for the editor
21207        // and its linked minimap.
21208        if !self.mode.is_minimap() {
21209            let font = style.text.font();
21210            let font_size = style.text.font_size.to_pixels(window.rem_size());
21211            let display_map = self
21212                .placeholder_display_map
21213                .as_ref()
21214                .filter(|_| self.is_empty(cx))
21215                .unwrap_or(&self.display_map);
21216
21217            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21218        }
21219        self.style = Some(style);
21220    }
21221
21222    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21223        if self.style.is_none() {
21224            self.style = Some(self.create_style(cx));
21225        }
21226        self.style.as_ref().unwrap()
21227    }
21228
21229    // Called by the element. This method is not designed to be called outside of the editor
21230    // element's layout code because it does not notify when rewrapping is computed synchronously.
21231    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21232        if self.is_empty(cx) {
21233            self.placeholder_display_map
21234                .as_ref()
21235                .map_or(false, |display_map| {
21236                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21237                })
21238        } else {
21239            self.display_map
21240                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21241        }
21242    }
21243
21244    pub fn set_soft_wrap(&mut self) {
21245        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21246    }
21247
21248    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21249        if self.soft_wrap_mode_override.is_some() {
21250            self.soft_wrap_mode_override.take();
21251        } else {
21252            let soft_wrap = match self.soft_wrap_mode(cx) {
21253                SoftWrap::GitDiff => return,
21254                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21255                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21256                    language_settings::SoftWrap::None
21257                }
21258            };
21259            self.soft_wrap_mode_override = Some(soft_wrap);
21260        }
21261        cx.notify();
21262    }
21263
21264    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21265        let Some(workspace) = self.workspace() else {
21266            return;
21267        };
21268        let fs = workspace.read(cx).app_state().fs.clone();
21269        let current_show = TabBarSettings::get_global(cx).show;
21270        update_settings_file(fs, cx, move |setting, _| {
21271            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21272        });
21273    }
21274
21275    pub fn toggle_indent_guides(
21276        &mut self,
21277        _: &ToggleIndentGuides,
21278        _: &mut Window,
21279        cx: &mut Context<Self>,
21280    ) {
21281        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21282            self.buffer
21283                .read(cx)
21284                .language_settings(cx)
21285                .indent_guides
21286                .enabled
21287        });
21288        self.show_indent_guides = Some(!currently_enabled);
21289        cx.notify();
21290    }
21291
21292    fn should_show_indent_guides(&self) -> Option<bool> {
21293        self.show_indent_guides
21294    }
21295
21296    pub fn disable_indent_guides_for_buffer(
21297        &mut self,
21298        buffer_id: BufferId,
21299        cx: &mut Context<Self>,
21300    ) {
21301        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21302        cx.notify();
21303    }
21304
21305    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21306        self.buffers_with_disabled_indent_guides
21307            .contains(&buffer_id)
21308    }
21309
21310    pub fn toggle_line_numbers(
21311        &mut self,
21312        _: &ToggleLineNumbers,
21313        _: &mut Window,
21314        cx: &mut Context<Self>,
21315    ) {
21316        let mut editor_settings = EditorSettings::get_global(cx).clone();
21317        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21318        EditorSettings::override_global(editor_settings, cx);
21319    }
21320
21321    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21322        if let Some(show_line_numbers) = self.show_line_numbers {
21323            return show_line_numbers;
21324        }
21325        EditorSettings::get_global(cx).gutter.line_numbers
21326    }
21327
21328    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21329        match (
21330            self.use_relative_line_numbers,
21331            EditorSettings::get_global(cx).relative_line_numbers,
21332        ) {
21333            (None, setting) => setting,
21334            (Some(false), _) => RelativeLineNumbers::Disabled,
21335            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21336            (Some(true), _) => RelativeLineNumbers::Enabled,
21337        }
21338    }
21339
21340    pub fn toggle_relative_line_numbers(
21341        &mut self,
21342        _: &ToggleRelativeLineNumbers,
21343        _: &mut Window,
21344        cx: &mut Context<Self>,
21345    ) {
21346        let is_relative = self.relative_line_numbers(cx);
21347        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21348    }
21349
21350    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21351        self.use_relative_line_numbers = is_relative;
21352        cx.notify();
21353    }
21354
21355    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21356        self.show_gutter = show_gutter;
21357        cx.notify();
21358    }
21359
21360    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21361        self.show_scrollbars = ScrollbarAxes {
21362            horizontal: show,
21363            vertical: show,
21364        };
21365        cx.notify();
21366    }
21367
21368    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21369        self.show_scrollbars.vertical = show;
21370        cx.notify();
21371    }
21372
21373    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21374        self.show_scrollbars.horizontal = show;
21375        cx.notify();
21376    }
21377
21378    pub fn set_minimap_visibility(
21379        &mut self,
21380        minimap_visibility: MinimapVisibility,
21381        window: &mut Window,
21382        cx: &mut Context<Self>,
21383    ) {
21384        if self.minimap_visibility != minimap_visibility {
21385            if minimap_visibility.visible() && self.minimap.is_none() {
21386                let minimap_settings = EditorSettings::get_global(cx).minimap;
21387                self.minimap =
21388                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21389            }
21390            self.minimap_visibility = minimap_visibility;
21391            cx.notify();
21392        }
21393    }
21394
21395    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21396        self.set_show_scrollbars(false, cx);
21397        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21398    }
21399
21400    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21401        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21402    }
21403
21404    /// Normally the text in full mode and auto height editors is padded on the
21405    /// left side by roughly half a character width for improved hit testing.
21406    ///
21407    /// Use this method to disable this for cases where this is not wanted (e.g.
21408    /// if you want to align the editor text with some other text above or below)
21409    /// or if you want to add this padding to single-line editors.
21410    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21411        self.offset_content = offset_content;
21412        cx.notify();
21413    }
21414
21415    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21416        self.show_line_numbers = Some(show_line_numbers);
21417        cx.notify();
21418    }
21419
21420    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21421        self.disable_expand_excerpt_buttons = true;
21422        cx.notify();
21423    }
21424
21425    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21426        self.number_deleted_lines = number;
21427        cx.notify();
21428    }
21429
21430    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21431        self.delegate_expand_excerpts = delegate;
21432    }
21433
21434    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21435        self.delegate_stage_and_restore = delegate;
21436    }
21437
21438    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21439        self.delegate_open_excerpts = delegate;
21440    }
21441
21442    pub fn set_on_local_selections_changed(
21443        &mut self,
21444        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21445    ) {
21446        self.on_local_selections_changed = callback;
21447    }
21448
21449    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21450        self.suppress_selection_callback = suppress;
21451    }
21452
21453    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21454        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21455        cx.notify();
21456    }
21457
21458    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21459        self.show_code_actions = Some(show_code_actions);
21460        cx.notify();
21461    }
21462
21463    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21464        self.show_runnables = Some(show_runnables);
21465        cx.notify();
21466    }
21467
21468    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21469        self.show_breakpoints = Some(show_breakpoints);
21470        cx.notify();
21471    }
21472
21473    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21474        self.show_diff_review_button = show;
21475        cx.notify();
21476    }
21477
21478    pub fn show_diff_review_button(&self) -> bool {
21479        self.show_diff_review_button
21480    }
21481
21482    pub fn render_diff_review_button(
21483        &self,
21484        display_row: DisplayRow,
21485        width: Pixels,
21486        cx: &mut Context<Self>,
21487    ) -> impl IntoElement {
21488        let text_color = cx.theme().colors().text;
21489        let icon_color = cx.theme().colors().icon_accent;
21490
21491        h_flex()
21492            .id("diff_review_button")
21493            .cursor_pointer()
21494            .w(width - px(1.))
21495            .h(relative(0.9))
21496            .justify_center()
21497            .rounded_sm()
21498            .border_1()
21499            .border_color(text_color.opacity(0.1))
21500            .bg(text_color.opacity(0.15))
21501            .hover(|s| {
21502                s.bg(icon_color.opacity(0.4))
21503                    .border_color(icon_color.opacity(0.5))
21504            })
21505            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21506            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21507            .on_mouse_down(
21508                gpui::MouseButton::Left,
21509                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21510                    editor.start_diff_review_drag(display_row, window, cx);
21511                }),
21512            )
21513    }
21514
21515    pub fn start_diff_review_drag(
21516        &mut self,
21517        display_row: DisplayRow,
21518        window: &mut Window,
21519        cx: &mut Context<Self>,
21520    ) {
21521        let snapshot = self.snapshot(window, cx);
21522        let point = snapshot
21523            .display_snapshot
21524            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21525        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21526        self.diff_review_drag_state = Some(DiffReviewDragState {
21527            start_anchor: anchor,
21528            current_anchor: anchor,
21529        });
21530        cx.notify();
21531    }
21532
21533    pub fn update_diff_review_drag(
21534        &mut self,
21535        display_row: DisplayRow,
21536        window: &mut Window,
21537        cx: &mut Context<Self>,
21538    ) {
21539        if self.diff_review_drag_state.is_none() {
21540            return;
21541        }
21542        let snapshot = self.snapshot(window, cx);
21543        let point = snapshot
21544            .display_snapshot
21545            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21546        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21547        if let Some(drag_state) = &mut self.diff_review_drag_state {
21548            drag_state.current_anchor = anchor;
21549            cx.notify();
21550        }
21551    }
21552
21553    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21554        if let Some(drag_state) = self.diff_review_drag_state.take() {
21555            let snapshot = self.snapshot(window, cx);
21556            let range = drag_state.row_range(&snapshot.display_snapshot);
21557            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21558        }
21559        cx.notify();
21560    }
21561
21562    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21563        self.diff_review_drag_state = None;
21564        cx.notify();
21565    }
21566
21567    /// Calculates the appropriate block height for the diff review overlay.
21568    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21569    /// and 2 lines per comment when expanded.
21570    fn calculate_overlay_height(
21571        &self,
21572        hunk_key: &DiffHunkKey,
21573        comments_expanded: bool,
21574        snapshot: &MultiBufferSnapshot,
21575    ) -> u32 {
21576        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21577        let base_height: u32 = 2; // Input row with avatar and buttons
21578
21579        if comment_count == 0 {
21580            base_height
21581        } else if comments_expanded {
21582            // Header (1 line) + 2 lines per comment
21583            base_height + 1 + (comment_count as u32 * 2)
21584        } else {
21585            // Just header when collapsed
21586            base_height + 1
21587        }
21588    }
21589
21590    pub fn show_diff_review_overlay(
21591        &mut self,
21592        display_range: Range<DisplayRow>,
21593        window: &mut Window,
21594        cx: &mut Context<Self>,
21595    ) {
21596        let Range { start, end } = display_range.sorted();
21597
21598        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21599        let editor_snapshot = self.snapshot(window, cx);
21600
21601        // Convert display rows to multibuffer points
21602        let start_point = editor_snapshot
21603            .display_snapshot
21604            .display_point_to_point(start.as_display_point(), Bias::Left);
21605        let end_point = editor_snapshot
21606            .display_snapshot
21607            .display_point_to_point(end.as_display_point(), Bias::Left);
21608        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21609
21610        // Create anchor range for the selected lines (start of first line to end of last line)
21611        let line_end = Point::new(
21612            end_point.row,
21613            buffer_snapshot.line_len(end_multi_buffer_row),
21614        );
21615        let anchor_range =
21616            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21617
21618        // Compute the hunk key for this display row
21619        let file_path = buffer_snapshot
21620            .file_at(start_point)
21621            .map(|file: &Arc<dyn language::File>| file.path().clone())
21622            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21623        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21624        let new_hunk_key = DiffHunkKey {
21625            file_path,
21626            hunk_start_anchor,
21627        };
21628
21629        // Check if we already have an overlay for this hunk
21630        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21631            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21632        }) {
21633            // Just focus the existing overlay's prompt editor
21634            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21635            window.focus(&focus_handle, cx);
21636            return;
21637        }
21638
21639        // Dismiss overlays that have no comments for their hunks
21640        self.dismiss_overlays_without_comments(cx);
21641
21642        // Get the current user's avatar URI from the project's user_store
21643        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21644            let user_store = project.read(cx).user_store();
21645            user_store
21646                .read(cx)
21647                .current_user()
21648                .map(|user| user.avatar_uri.clone())
21649        });
21650
21651        // Create anchor at the end of the last row so the block appears immediately below it
21652        // Use multibuffer coordinates for anchor creation
21653        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21654        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21655
21656        // Use the hunk key we already computed
21657        let hunk_key = new_hunk_key;
21658
21659        // Create the prompt editor for the review input
21660        let prompt_editor = cx.new(|cx| {
21661            let mut editor = Editor::single_line(window, cx);
21662            editor.set_placeholder_text("Add a review comment...", window, cx);
21663            editor
21664        });
21665
21666        // Register the Newline action on the prompt editor to submit the review
21667        let parent_editor = cx.entity().downgrade();
21668        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21669            prompt_editor.register_action({
21670                let parent_editor = parent_editor.clone();
21671                move |_: &crate::actions::Newline, window, cx| {
21672                    if let Some(editor) = parent_editor.upgrade() {
21673                        editor.update(cx, |editor, cx| {
21674                            editor.submit_diff_review_comment(window, cx);
21675                        });
21676                    }
21677                }
21678            })
21679        });
21680
21681        // Calculate initial height based on existing comments for this hunk
21682        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21683
21684        // Create the overlay block
21685        let prompt_editor_for_render = prompt_editor.clone();
21686        let hunk_key_for_render = hunk_key.clone();
21687        let editor_handle = cx.entity().downgrade();
21688        let block = BlockProperties {
21689            style: BlockStyle::Sticky,
21690            placement: BlockPlacement::Below(anchor),
21691            height: Some(initial_height),
21692            render: Arc::new(move |cx| {
21693                Self::render_diff_review_overlay(
21694                    &prompt_editor_for_render,
21695                    &hunk_key_for_render,
21696                    &editor_handle,
21697                    cx,
21698                )
21699            }),
21700            priority: 0,
21701        };
21702
21703        let block_ids = self.insert_blocks([block], None, cx);
21704        let Some(block_id) = block_ids.into_iter().next() else {
21705            log::error!("Failed to insert diff review overlay block");
21706            return;
21707        };
21708
21709        self.diff_review_overlays.push(DiffReviewOverlay {
21710            anchor_range,
21711            block_id,
21712            prompt_editor: prompt_editor.clone(),
21713            hunk_key,
21714            comments_expanded: true,
21715            inline_edit_editors: HashMap::default(),
21716            inline_edit_subscriptions: HashMap::default(),
21717            user_avatar_uri,
21718            _subscription: subscription,
21719        });
21720
21721        // Focus the prompt editor
21722        let focus_handle = prompt_editor.focus_handle(cx);
21723        window.focus(&focus_handle, cx);
21724
21725        cx.notify();
21726    }
21727
21728    /// Dismisses all diff review overlays.
21729    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21730        if self.diff_review_overlays.is_empty() {
21731            return;
21732        }
21733        let block_ids: HashSet<_> = self
21734            .diff_review_overlays
21735            .drain(..)
21736            .map(|overlay| overlay.block_id)
21737            .collect();
21738        self.remove_blocks(block_ids, None, cx);
21739        cx.notify();
21740    }
21741
21742    /// Dismisses overlays that have no comments stored for their hunks.
21743    /// Keeps overlays that have at least one comment.
21744    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21745        let snapshot = self.buffer.read(cx).snapshot(cx);
21746
21747        // First, compute which overlays have comments (to avoid borrow issues with retain)
21748        let overlays_with_comments: Vec<bool> = self
21749            .diff_review_overlays
21750            .iter()
21751            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21752            .collect();
21753
21754        // Now collect block IDs to remove and retain overlays
21755        let mut block_ids_to_remove = HashSet::default();
21756        let mut index = 0;
21757        self.diff_review_overlays.retain(|overlay| {
21758            let has_comments = overlays_with_comments[index];
21759            index += 1;
21760            if !has_comments {
21761                block_ids_to_remove.insert(overlay.block_id);
21762            }
21763            has_comments
21764        });
21765
21766        if !block_ids_to_remove.is_empty() {
21767            self.remove_blocks(block_ids_to_remove, None, cx);
21768            cx.notify();
21769        }
21770    }
21771
21772    /// Refreshes the diff review overlay block to update its height and render function.
21773    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21774    fn refresh_diff_review_overlay_height(
21775        &mut self,
21776        hunk_key: &DiffHunkKey,
21777        _window: &mut Window,
21778        cx: &mut Context<Self>,
21779    ) {
21780        // Extract all needed data from overlay first to avoid borrow conflicts
21781        let snapshot = self.buffer.read(cx).snapshot(cx);
21782        let (comments_expanded, block_id, prompt_editor) = {
21783            let Some(overlay) = self
21784                .diff_review_overlays
21785                .iter()
21786                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21787            else {
21788                return;
21789            };
21790
21791            (
21792                overlay.comments_expanded,
21793                overlay.block_id,
21794                overlay.prompt_editor.clone(),
21795            )
21796        };
21797
21798        // Calculate new height
21799        let snapshot = self.buffer.read(cx).snapshot(cx);
21800        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21801
21802        // Update the block height using resize_blocks (avoids flicker)
21803        let mut heights = HashMap::default();
21804        heights.insert(block_id, new_height);
21805        self.resize_blocks(heights, None, cx);
21806
21807        // Update the render function using replace_blocks (avoids flicker)
21808        let hunk_key_for_render = hunk_key.clone();
21809        let editor_handle = cx.entity().downgrade();
21810        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21811            Arc::new(move |cx| {
21812                Self::render_diff_review_overlay(
21813                    &prompt_editor,
21814                    &hunk_key_for_render,
21815                    &editor_handle,
21816                    cx,
21817                )
21818            });
21819
21820        let mut renderers = HashMap::default();
21821        renderers.insert(block_id, render);
21822        self.replace_blocks(renderers, None, cx);
21823    }
21824
21825    /// Action handler for SubmitDiffReviewComment.
21826    pub fn submit_diff_review_comment_action(
21827        &mut self,
21828        _: &SubmitDiffReviewComment,
21829        window: &mut Window,
21830        cx: &mut Context<Self>,
21831    ) {
21832        self.submit_diff_review_comment(window, cx);
21833    }
21834
21835    /// Stores the diff review comment locally.
21836    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21837    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21838        // Find the overlay that currently has focus
21839        let overlay_index = self
21840            .diff_review_overlays
21841            .iter()
21842            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21843        let Some(overlay_index) = overlay_index else {
21844            return;
21845        };
21846        let overlay = &self.diff_review_overlays[overlay_index];
21847
21848        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21849        if comment_text.is_empty() {
21850            return;
21851        }
21852
21853        let anchor_range = overlay.anchor_range.clone();
21854        let hunk_key = overlay.hunk_key.clone();
21855
21856        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21857
21858        // Clear the prompt editor but keep the overlay open
21859        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21860            overlay.prompt_editor.update(cx, |editor, cx| {
21861                editor.clear(window, cx);
21862            });
21863        }
21864
21865        // Refresh the overlay to update the block height for the new comment
21866        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21867
21868        cx.notify();
21869    }
21870
21871    /// Returns the prompt editor for the diff review overlay, if one is active.
21872    /// This is primarily used for testing.
21873    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21874        self.diff_review_overlays
21875            .first()
21876            .map(|overlay| &overlay.prompt_editor)
21877    }
21878
21879    /// Returns the line range for the first diff review overlay, if one is active.
21880    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21881    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21882        let overlay = self.diff_review_overlays.first()?;
21883        let snapshot = self.buffer.read(cx).snapshot(cx);
21884        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21885        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21886        let start_row = snapshot
21887            .point_to_buffer_point(start_point)
21888            .map(|(_, p, _)| p.row)
21889            .unwrap_or(start_point.row);
21890        let end_row = snapshot
21891            .point_to_buffer_point(end_point)
21892            .map(|(_, p, _)| p.row)
21893            .unwrap_or(end_point.row);
21894        Some((start_row, end_row))
21895    }
21896
21897    /// Sets whether the comments section is expanded in the diff review overlay.
21898    /// This is primarily used for testing.
21899    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21900        for overlay in &mut self.diff_review_overlays {
21901            overlay.comments_expanded = expanded;
21902        }
21903        cx.notify();
21904    }
21905
21906    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21907    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21908        a.file_path == b.file_path
21909            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21910    }
21911
21912    /// Returns comments for a specific hunk, ordered by creation time.
21913    pub fn comments_for_hunk<'a>(
21914        &'a self,
21915        key: &DiffHunkKey,
21916        snapshot: &MultiBufferSnapshot,
21917    ) -> &'a [StoredReviewComment] {
21918        let key_point = key.hunk_start_anchor.to_point(snapshot);
21919        self.stored_review_comments
21920            .iter()
21921            .find(|(k, _)| {
21922                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21923            })
21924            .map(|(_, comments)| comments.as_slice())
21925            .unwrap_or(&[])
21926    }
21927
21928    /// Returns the total count of stored review comments across all hunks.
21929    pub fn total_review_comment_count(&self) -> usize {
21930        self.stored_review_comments
21931            .iter()
21932            .map(|(_, v)| v.len())
21933            .sum()
21934    }
21935
21936    /// Returns the count of comments for a specific hunk.
21937    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21938        let key_point = key.hunk_start_anchor.to_point(snapshot);
21939        self.stored_review_comments
21940            .iter()
21941            .find(|(k, _)| {
21942                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21943            })
21944            .map(|(_, v)| v.len())
21945            .unwrap_or(0)
21946    }
21947
21948    /// Adds a new review comment to a specific hunk.
21949    pub fn add_review_comment(
21950        &mut self,
21951        hunk_key: DiffHunkKey,
21952        comment: String,
21953        anchor_range: Range<Anchor>,
21954        cx: &mut Context<Self>,
21955    ) -> usize {
21956        let id = self.next_review_comment_id;
21957        self.next_review_comment_id += 1;
21958
21959        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21960
21961        let snapshot = self.buffer.read(cx).snapshot(cx);
21962        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21963
21964        // Find existing entry for this hunk or add a new one
21965        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21966            k.file_path == hunk_key.file_path
21967                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21968        }) {
21969            comments.push(stored_comment);
21970        } else {
21971            self.stored_review_comments
21972                .push((hunk_key, vec![stored_comment]));
21973        }
21974
21975        cx.emit(EditorEvent::ReviewCommentsChanged {
21976            total_count: self.total_review_comment_count(),
21977        });
21978        cx.notify();
21979        id
21980    }
21981
21982    /// Removes a review comment by ID from any hunk.
21983    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21984        for (_, comments) in self.stored_review_comments.iter_mut() {
21985            if let Some(index) = comments.iter().position(|c| c.id == id) {
21986                comments.remove(index);
21987                cx.emit(EditorEvent::ReviewCommentsChanged {
21988                    total_count: self.total_review_comment_count(),
21989                });
21990                cx.notify();
21991                return true;
21992            }
21993        }
21994        false
21995    }
21996
21997    /// Updates a review comment's text by ID.
21998    pub fn update_review_comment(
21999        &mut self,
22000        id: usize,
22001        new_comment: String,
22002        cx: &mut Context<Self>,
22003    ) -> bool {
22004        for (_, comments) in self.stored_review_comments.iter_mut() {
22005            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22006                comment.comment = new_comment;
22007                comment.is_editing = false;
22008                cx.emit(EditorEvent::ReviewCommentsChanged {
22009                    total_count: self.total_review_comment_count(),
22010                });
22011                cx.notify();
22012                return true;
22013            }
22014        }
22015        false
22016    }
22017
22018    /// Sets a comment's editing state.
22019    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22020        for (_, comments) in self.stored_review_comments.iter_mut() {
22021            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22022                comment.is_editing = is_editing;
22023                cx.notify();
22024                return;
22025            }
22026        }
22027    }
22028
22029    /// Takes all stored comments from all hunks, clearing the storage.
22030    /// Returns a Vec of (hunk_key, comments) pairs.
22031    pub fn take_all_review_comments(
22032        &mut self,
22033        cx: &mut Context<Self>,
22034    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22035        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22036        self.dismiss_all_diff_review_overlays(cx);
22037        let comments = std::mem::take(&mut self.stored_review_comments);
22038        // Reset the ID counter since all comments have been taken
22039        self.next_review_comment_id = 0;
22040        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22041        cx.notify();
22042        comments
22043    }
22044
22045    /// Removes review comments whose anchors are no longer valid or whose
22046    /// associated diff hunks no longer exist.
22047    ///
22048    /// This should be called when the buffer changes to prevent orphaned comments
22049    /// from accumulating.
22050    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22051        let snapshot = self.buffer.read(cx).snapshot(cx);
22052        let original_count = self.total_review_comment_count();
22053
22054        // Remove comments with invalid hunk anchors
22055        self.stored_review_comments
22056            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22057
22058        // Also clean up individual comments with invalid anchor ranges
22059        for (_, comments) in &mut self.stored_review_comments {
22060            comments.retain(|comment| {
22061                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22062            });
22063        }
22064
22065        // Remove empty hunk entries
22066        self.stored_review_comments
22067            .retain(|(_, comments)| !comments.is_empty());
22068
22069        let new_count = self.total_review_comment_count();
22070        if new_count != original_count {
22071            cx.emit(EditorEvent::ReviewCommentsChanged {
22072                total_count: new_count,
22073            });
22074            cx.notify();
22075        }
22076    }
22077
22078    /// Toggles the expanded state of the comments section in the overlay.
22079    pub fn toggle_review_comments_expanded(
22080        &mut self,
22081        _: &ToggleReviewCommentsExpanded,
22082        window: &mut Window,
22083        cx: &mut Context<Self>,
22084    ) {
22085        // Find the overlay that currently has focus, or use the first one
22086        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22087            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22088                overlay.comments_expanded = !overlay.comments_expanded;
22089                Some(overlay.hunk_key.clone())
22090            } else {
22091                None
22092            }
22093        });
22094
22095        // If no focused overlay found, toggle the first one
22096        let hunk_key = overlay_info.or_else(|| {
22097            self.diff_review_overlays.first_mut().map(|overlay| {
22098                overlay.comments_expanded = !overlay.comments_expanded;
22099                overlay.hunk_key.clone()
22100            })
22101        });
22102
22103        if let Some(hunk_key) = hunk_key {
22104            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22105            cx.notify();
22106        }
22107    }
22108
22109    /// Handles the EditReviewComment action - sets a comment into editing mode.
22110    pub fn edit_review_comment(
22111        &mut self,
22112        action: &EditReviewComment,
22113        window: &mut Window,
22114        cx: &mut Context<Self>,
22115    ) {
22116        let comment_id = action.id;
22117
22118        // Set the comment to editing mode
22119        self.set_comment_editing(comment_id, true, cx);
22120
22121        // Find the overlay that contains this comment and create an inline editor if needed
22122        // First, find which hunk this comment belongs to
22123        let hunk_key = self
22124            .stored_review_comments
22125            .iter()
22126            .find_map(|(key, comments)| {
22127                if comments.iter().any(|c| c.id == comment_id) {
22128                    Some(key.clone())
22129                } else {
22130                    None
22131                }
22132            });
22133
22134        let snapshot = self.buffer.read(cx).snapshot(cx);
22135        if let Some(hunk_key) = hunk_key {
22136            if let Some(overlay) = self
22137                .diff_review_overlays
22138                .iter_mut()
22139                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22140            {
22141                if let std::collections::hash_map::Entry::Vacant(entry) =
22142                    overlay.inline_edit_editors.entry(comment_id)
22143                {
22144                    // Find the comment text
22145                    let comment_text = self
22146                        .stored_review_comments
22147                        .iter()
22148                        .flat_map(|(_, comments)| comments)
22149                        .find(|c| c.id == comment_id)
22150                        .map(|c| c.comment.clone())
22151                        .unwrap_or_default();
22152
22153                    // Create inline editor
22154                    let parent_editor = cx.entity().downgrade();
22155                    let inline_editor = cx.new(|cx| {
22156                        let mut editor = Editor::single_line(window, cx);
22157                        editor.set_text(&*comment_text, window, cx);
22158                        // Select all text for easy replacement
22159                        editor.select_all(&crate::actions::SelectAll, window, cx);
22160                        editor
22161                    });
22162
22163                    // Register the Newline action to confirm the edit
22164                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22165                        inline_editor.register_action({
22166                            let parent_editor = parent_editor.clone();
22167                            move |_: &crate::actions::Newline, window, cx| {
22168                                if let Some(editor) = parent_editor.upgrade() {
22169                                    editor.update(cx, |editor, cx| {
22170                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22171                                    });
22172                                }
22173                            }
22174                        })
22175                    });
22176
22177                    // Store the subscription to keep the action handler alive
22178                    overlay
22179                        .inline_edit_subscriptions
22180                        .insert(comment_id, subscription);
22181
22182                    // Focus the inline editor
22183                    let focus_handle = inline_editor.focus_handle(cx);
22184                    window.focus(&focus_handle, cx);
22185
22186                    entry.insert(inline_editor);
22187                }
22188            }
22189        }
22190
22191        cx.notify();
22192    }
22193
22194    /// Confirms an inline edit of a review comment.
22195    pub fn confirm_edit_review_comment(
22196        &mut self,
22197        comment_id: usize,
22198        _window: &mut Window,
22199        cx: &mut Context<Self>,
22200    ) {
22201        // Get the new text from the inline editor
22202        // Find the overlay containing this comment's inline editor
22203        let snapshot = self.buffer.read(cx).snapshot(cx);
22204        let hunk_key = self
22205            .stored_review_comments
22206            .iter()
22207            .find_map(|(key, comments)| {
22208                if comments.iter().any(|c| c.id == comment_id) {
22209                    Some(key.clone())
22210                } else {
22211                    None
22212                }
22213            });
22214
22215        let new_text = hunk_key
22216            .as_ref()
22217            .and_then(|hunk_key| {
22218                self.diff_review_overlays
22219                    .iter()
22220                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22221            })
22222            .as_ref()
22223            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22224            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22225
22226        if let Some(new_text) = new_text {
22227            if !new_text.is_empty() {
22228                self.update_review_comment(comment_id, new_text, cx);
22229            }
22230        }
22231
22232        // Remove the inline editor and its subscription
22233        if let Some(hunk_key) = hunk_key {
22234            if let Some(overlay) = self
22235                .diff_review_overlays
22236                .iter_mut()
22237                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22238            {
22239                overlay.inline_edit_editors.remove(&comment_id);
22240                overlay.inline_edit_subscriptions.remove(&comment_id);
22241            }
22242        }
22243
22244        // Clear editing state
22245        self.set_comment_editing(comment_id, false, cx);
22246    }
22247
22248    /// Cancels an inline edit of a review comment.
22249    pub fn cancel_edit_review_comment(
22250        &mut self,
22251        comment_id: usize,
22252        _window: &mut Window,
22253        cx: &mut Context<Self>,
22254    ) {
22255        // Find which hunk this comment belongs to
22256        let hunk_key = self
22257            .stored_review_comments
22258            .iter()
22259            .find_map(|(key, comments)| {
22260                if comments.iter().any(|c| c.id == comment_id) {
22261                    Some(key.clone())
22262                } else {
22263                    None
22264                }
22265            });
22266
22267        // Remove the inline editor and its subscription
22268        if let Some(hunk_key) = hunk_key {
22269            let snapshot = self.buffer.read(cx).snapshot(cx);
22270            if let Some(overlay) = self
22271                .diff_review_overlays
22272                .iter_mut()
22273                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22274            {
22275                overlay.inline_edit_editors.remove(&comment_id);
22276                overlay.inline_edit_subscriptions.remove(&comment_id);
22277            }
22278        }
22279
22280        // Clear editing state
22281        self.set_comment_editing(comment_id, false, cx);
22282    }
22283
22284    /// Action handler for ConfirmEditReviewComment.
22285    pub fn confirm_edit_review_comment_action(
22286        &mut self,
22287        action: &ConfirmEditReviewComment,
22288        window: &mut Window,
22289        cx: &mut Context<Self>,
22290    ) {
22291        self.confirm_edit_review_comment(action.id, window, cx);
22292    }
22293
22294    /// Action handler for CancelEditReviewComment.
22295    pub fn cancel_edit_review_comment_action(
22296        &mut self,
22297        action: &CancelEditReviewComment,
22298        window: &mut Window,
22299        cx: &mut Context<Self>,
22300    ) {
22301        self.cancel_edit_review_comment(action.id, window, cx);
22302    }
22303
22304    /// Handles the DeleteReviewComment action - removes a comment.
22305    pub fn delete_review_comment(
22306        &mut self,
22307        action: &DeleteReviewComment,
22308        window: &mut Window,
22309        cx: &mut Context<Self>,
22310    ) {
22311        // Get the hunk key before removing the comment
22312        // Find the hunk key from the comment itself
22313        let comment_id = action.id;
22314        let hunk_key = self
22315            .stored_review_comments
22316            .iter()
22317            .find_map(|(key, comments)| {
22318                if comments.iter().any(|c| c.id == comment_id) {
22319                    Some(key.clone())
22320                } else {
22321                    None
22322                }
22323            });
22324
22325        // Also get it from the overlay for refresh purposes
22326        let overlay_hunk_key = self
22327            .diff_review_overlays
22328            .first()
22329            .map(|o| o.hunk_key.clone());
22330
22331        self.remove_review_comment(action.id, cx);
22332
22333        // Refresh the overlay height after removing a comment
22334        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22335            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22336        }
22337    }
22338
22339    fn render_diff_review_overlay(
22340        prompt_editor: &Entity<Editor>,
22341        hunk_key: &DiffHunkKey,
22342        editor_handle: &WeakEntity<Editor>,
22343        cx: &mut BlockContext,
22344    ) -> AnyElement {
22345        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22346            if ranges.is_empty() {
22347                return None;
22348            }
22349            let formatted: Vec<String> = ranges
22350                .iter()
22351                .map(|(start, end)| {
22352                    let start_line = start + 1;
22353                    let end_line = end + 1;
22354                    if start_line == end_line {
22355                        format!("Line {start_line}")
22356                    } else {
22357                        format!("Lines {start_line}-{end_line}")
22358                    }
22359                })
22360                .collect();
22361            // Don't show label for single line in single excerpt
22362            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22363                return None;
22364            }
22365            Some(formatted.join(""))
22366        }
22367
22368        let theme = cx.theme();
22369        let colors = theme.colors();
22370
22371        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22372            editor_handle
22373                .upgrade()
22374                .map(|editor| {
22375                    let editor = editor.read(cx);
22376                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22377                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22378                    let (expanded, editors, avatar_uri, line_ranges) = editor
22379                        .diff_review_overlays
22380                        .iter()
22381                        .find(|overlay| {
22382                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22383                        })
22384                        .map(|o| {
22385                            let start_point = o.anchor_range.start.to_point(&snapshot);
22386                            let end_point = o.anchor_range.end.to_point(&snapshot);
22387                            // Get line ranges per excerpt to detect discontinuities
22388                            let buffer_ranges =
22389                                snapshot.range_to_buffer_ranges(start_point..end_point);
22390                            let ranges: Vec<(u32, u32)> = buffer_ranges
22391                                .iter()
22392                                .map(|(buffer, range, _)| {
22393                                    let start = buffer.offset_to_point(range.start.0).row;
22394                                    let end = buffer.offset_to_point(range.end.0).row;
22395                                    (start, end)
22396                                })
22397                                .collect();
22398                            (
22399                                o.comments_expanded,
22400                                o.inline_edit_editors.clone(),
22401                                o.user_avatar_uri.clone(),
22402                                if ranges.is_empty() {
22403                                    None
22404                                } else {
22405                                    Some(ranges)
22406                                },
22407                            )
22408                        })
22409                        .unwrap_or((true, HashMap::default(), None, None));
22410                    (comments, expanded, editors, avatar_uri, line_ranges)
22411                })
22412                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22413
22414        let comment_count = comments.len();
22415        let avatar_size = px(20.);
22416        let action_icon_size = IconSize::XSmall;
22417
22418        v_flex()
22419            .w_full()
22420            .bg(colors.editor_background)
22421            .border_b_1()
22422            .border_color(colors.border)
22423            .px_2()
22424            .pb_2()
22425            .gap_2()
22426            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22427            .when_some(line_ranges, |el, ranges| {
22428                let label = format_line_ranges(&ranges);
22429                if let Some(label) = label {
22430                    el.child(
22431                        h_flex()
22432                            .w_full()
22433                            .px_2()
22434                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22435                    )
22436                } else {
22437                    el
22438                }
22439            })
22440            // Top row: editable input with user's avatar
22441            .child(
22442                h_flex()
22443                    .w_full()
22444                    .items_center()
22445                    .gap_2()
22446                    .px_2()
22447                    .py_1p5()
22448                    .rounded_md()
22449                    .bg(colors.surface_background)
22450                    .child(
22451                        div()
22452                            .size(avatar_size)
22453                            .flex_shrink_0()
22454                            .rounded_full()
22455                            .overflow_hidden()
22456                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22457                                Avatar::new(avatar_uri.clone())
22458                                    .size(avatar_size)
22459                                    .into_any_element()
22460                            } else {
22461                                Icon::new(IconName::Person)
22462                                    .size(IconSize::Small)
22463                                    .color(ui::Color::Muted)
22464                                    .into_any_element()
22465                            }),
22466                    )
22467                    .child(
22468                        div()
22469                            .flex_1()
22470                            .border_1()
22471                            .border_color(colors.border)
22472                            .rounded_md()
22473                            .bg(colors.editor_background)
22474                            .px_2()
22475                            .py_1()
22476                            .child(prompt_editor.clone()),
22477                    )
22478                    .child(
22479                        h_flex()
22480                            .flex_shrink_0()
22481                            .gap_1()
22482                            .child(
22483                                IconButton::new("diff-review-close", IconName::Close)
22484                                    .icon_color(ui::Color::Muted)
22485                                    .icon_size(action_icon_size)
22486                                    .tooltip(Tooltip::text("Close"))
22487                                    .on_click(|_, window, cx| {
22488                                        window
22489                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22490                                    }),
22491                            )
22492                            .child(
22493                                IconButton::new("diff-review-add", IconName::Return)
22494                                    .icon_color(ui::Color::Muted)
22495                                    .icon_size(action_icon_size)
22496                                    .tooltip(Tooltip::text("Add comment"))
22497                                    .on_click(|_, window, cx| {
22498                                        window.dispatch_action(
22499                                            Box::new(crate::actions::SubmitDiffReviewComment),
22500                                            cx,
22501                                        );
22502                                    }),
22503                            ),
22504                    ),
22505            )
22506            // Expandable comments section (only shown when there are comments)
22507            .when(comment_count > 0, |el| {
22508                el.child(Self::render_comments_section(
22509                    comments,
22510                    comments_expanded,
22511                    inline_editors,
22512                    user_avatar_uri,
22513                    avatar_size,
22514                    action_icon_size,
22515                    colors,
22516                ))
22517            })
22518            .into_any_element()
22519    }
22520
22521    fn render_comments_section(
22522        comments: Vec<StoredReviewComment>,
22523        expanded: bool,
22524        inline_editors: HashMap<usize, Entity<Editor>>,
22525        user_avatar_uri: Option<SharedUri>,
22526        avatar_size: Pixels,
22527        action_icon_size: IconSize,
22528        colors: &theme::ThemeColors,
22529    ) -> impl IntoElement {
22530        let comment_count = comments.len();
22531
22532        v_flex()
22533            .w_full()
22534            .gap_1()
22535            // Header with expand/collapse toggle
22536            .child(
22537                h_flex()
22538                    .id("review-comments-header")
22539                    .w_full()
22540                    .items_center()
22541                    .gap_1()
22542                    .px_2()
22543                    .py_1()
22544                    .cursor_pointer()
22545                    .rounded_md()
22546                    .hover(|style| style.bg(colors.ghost_element_hover))
22547                    .on_click(|_, window: &mut Window, cx| {
22548                        window.dispatch_action(
22549                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22550                            cx,
22551                        );
22552                    })
22553                    .child(
22554                        Icon::new(if expanded {
22555                            IconName::ChevronDown
22556                        } else {
22557                            IconName::ChevronRight
22558                        })
22559                        .size(IconSize::Small)
22560                        .color(ui::Color::Muted),
22561                    )
22562                    .child(
22563                        Label::new(format!(
22564                            "{} Comment{}",
22565                            comment_count,
22566                            if comment_count == 1 { "" } else { "s" }
22567                        ))
22568                        .size(LabelSize::Small)
22569                        .color(Color::Muted),
22570                    ),
22571            )
22572            // Comments list (when expanded)
22573            .when(expanded, |el| {
22574                el.children(comments.into_iter().map(|comment| {
22575                    let inline_editor = inline_editors.get(&comment.id).cloned();
22576                    Self::render_comment_row(
22577                        comment,
22578                        inline_editor,
22579                        user_avatar_uri.clone(),
22580                        avatar_size,
22581                        action_icon_size,
22582                        colors,
22583                    )
22584                }))
22585            })
22586    }
22587
22588    fn render_comment_row(
22589        comment: StoredReviewComment,
22590        inline_editor: Option<Entity<Editor>>,
22591        user_avatar_uri: Option<SharedUri>,
22592        avatar_size: Pixels,
22593        action_icon_size: IconSize,
22594        colors: &theme::ThemeColors,
22595    ) -> impl IntoElement {
22596        let comment_id = comment.id;
22597        let is_editing = inline_editor.is_some();
22598
22599        h_flex()
22600            .w_full()
22601            .items_center()
22602            .gap_2()
22603            .px_2()
22604            .py_1p5()
22605            .rounded_md()
22606            .bg(colors.surface_background)
22607            .child(
22608                div()
22609                    .size(avatar_size)
22610                    .flex_shrink_0()
22611                    .rounded_full()
22612                    .overflow_hidden()
22613                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22614                        Avatar::new(avatar_uri.clone())
22615                            .size(avatar_size)
22616                            .into_any_element()
22617                    } else {
22618                        Icon::new(IconName::Person)
22619                            .size(IconSize::Small)
22620                            .color(ui::Color::Muted)
22621                            .into_any_element()
22622                    }),
22623            )
22624            .child(if let Some(editor) = inline_editor {
22625                // Inline edit mode: show an editable text field
22626                div()
22627                    .flex_1()
22628                    .border_1()
22629                    .border_color(colors.border)
22630                    .rounded_md()
22631                    .bg(colors.editor_background)
22632                    .px_2()
22633                    .py_1()
22634                    .child(editor)
22635                    .into_any_element()
22636            } else {
22637                // Display mode: show the comment text
22638                div()
22639                    .flex_1()
22640                    .text_sm()
22641                    .text_color(colors.text)
22642                    .child(comment.comment)
22643                    .into_any_element()
22644            })
22645            .child(if is_editing {
22646                // Editing mode: show close and confirm buttons
22647                h_flex()
22648                    .gap_1()
22649                    .child(
22650                        IconButton::new(
22651                            format!("diff-review-cancel-edit-{comment_id}"),
22652                            IconName::Close,
22653                        )
22654                        .icon_color(ui::Color::Muted)
22655                        .icon_size(action_icon_size)
22656                        .tooltip(Tooltip::text("Cancel"))
22657                        .on_click(move |_, window, cx| {
22658                            window.dispatch_action(
22659                                Box::new(crate::actions::CancelEditReviewComment {
22660                                    id: comment_id,
22661                                }),
22662                                cx,
22663                            );
22664                        }),
22665                    )
22666                    .child(
22667                        IconButton::new(
22668                            format!("diff-review-confirm-edit-{comment_id}"),
22669                            IconName::Return,
22670                        )
22671                        .icon_color(ui::Color::Muted)
22672                        .icon_size(action_icon_size)
22673                        .tooltip(Tooltip::text("Confirm"))
22674                        .on_click(move |_, window, cx| {
22675                            window.dispatch_action(
22676                                Box::new(crate::actions::ConfirmEditReviewComment {
22677                                    id: comment_id,
22678                                }),
22679                                cx,
22680                            );
22681                        }),
22682                    )
22683                    .into_any_element()
22684            } else {
22685                // Display mode: no action buttons for now (edit/delete not yet implemented)
22686                gpui::Empty.into_any_element()
22687            })
22688    }
22689
22690    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22691        if self.display_map.read(cx).masked != masked {
22692            self.display_map.update(cx, |map, _| map.masked = masked);
22693        }
22694        cx.notify()
22695    }
22696
22697    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22698        self.show_wrap_guides = Some(show_wrap_guides);
22699        cx.notify();
22700    }
22701
22702    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22703        self.show_indent_guides = Some(show_indent_guides);
22704        cx.notify();
22705    }
22706
22707    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22708        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22709            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22710                && let Some(dir) = file.abs_path(cx).parent()
22711            {
22712                return Some(dir.to_owned());
22713            }
22714        }
22715
22716        None
22717    }
22718
22719    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22720        self.active_excerpt(cx)?
22721            .1
22722            .read(cx)
22723            .file()
22724            .and_then(|f| f.as_local())
22725    }
22726
22727    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22728        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22729            let buffer = buffer.read(cx);
22730            if let Some(project_path) = buffer.project_path(cx) {
22731                let project = self.project()?.read(cx);
22732                project.absolute_path(&project_path, cx)
22733            } else {
22734                buffer
22735                    .file()
22736                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22737            }
22738        })
22739    }
22740
22741    pub fn reveal_in_finder(
22742        &mut self,
22743        _: &RevealInFileManager,
22744        _window: &mut Window,
22745        cx: &mut Context<Self>,
22746    ) {
22747        if let Some(path) = self.target_file_abs_path(cx) {
22748            if let Some(project) = self.project() {
22749                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22750            } else {
22751                cx.reveal_path(&path);
22752            }
22753        }
22754    }
22755
22756    pub fn copy_path(
22757        &mut self,
22758        _: &zed_actions::workspace::CopyPath,
22759        _window: &mut Window,
22760        cx: &mut Context<Self>,
22761    ) {
22762        if let Some(path) = self.target_file_abs_path(cx)
22763            && let Some(path) = path.to_str()
22764        {
22765            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22766        } else {
22767            cx.propagate();
22768        }
22769    }
22770
22771    pub fn copy_relative_path(
22772        &mut self,
22773        _: &zed_actions::workspace::CopyRelativePath,
22774        _window: &mut Window,
22775        cx: &mut Context<Self>,
22776    ) {
22777        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22778            let project = self.project()?.read(cx);
22779            let path = buffer.read(cx).file()?.path();
22780            let path = path.display(project.path_style(cx));
22781            Some(path)
22782        }) {
22783            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22784        } else {
22785            cx.propagate();
22786        }
22787    }
22788
22789    /// Returns the project path for the editor's buffer, if any buffer is
22790    /// opened in the editor.
22791    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22792        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22793            buffer.read(cx).project_path(cx)
22794        } else {
22795            None
22796        }
22797    }
22798
22799    // Returns true if the editor handled a go-to-line request
22800    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22801        maybe!({
22802            let breakpoint_store = self.breakpoint_store.as_ref()?;
22803
22804            let (active_stack_frame, debug_line_pane_id) = {
22805                let store = breakpoint_store.read(cx);
22806                let active_stack_frame = store.active_position().cloned();
22807                let debug_line_pane_id = store.active_debug_line_pane_id();
22808                (active_stack_frame, debug_line_pane_id)
22809            };
22810
22811            let Some(active_stack_frame) = active_stack_frame else {
22812                self.clear_row_highlights::<ActiveDebugLine>();
22813                return None;
22814            };
22815
22816            if let Some(debug_line_pane_id) = debug_line_pane_id {
22817                if let Some(workspace) = self
22818                    .workspace
22819                    .as_ref()
22820                    .and_then(|(workspace, _)| workspace.upgrade())
22821                {
22822                    let editor_pane_id = workspace
22823                        .read(cx)
22824                        .pane_for_item_id(cx.entity_id())
22825                        .map(|pane| pane.entity_id());
22826
22827                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22828                        self.clear_row_highlights::<ActiveDebugLine>();
22829                        return None;
22830                    }
22831                }
22832            }
22833
22834            let position = active_stack_frame.position;
22835            let buffer_id = position.buffer_id?;
22836            let snapshot = self
22837                .project
22838                .as_ref()?
22839                .read(cx)
22840                .buffer_for_id(buffer_id, cx)?
22841                .read(cx)
22842                .snapshot();
22843
22844            let mut handled = false;
22845            for (id, ExcerptRange { context, .. }) in
22846                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22847            {
22848                if context.start.cmp(&position, &snapshot).is_ge()
22849                    || context.end.cmp(&position, &snapshot).is_lt()
22850                {
22851                    continue;
22852                }
22853                let snapshot = self.buffer.read(cx).snapshot(cx);
22854                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22855
22856                handled = true;
22857                self.clear_row_highlights::<ActiveDebugLine>();
22858
22859                self.go_to_line::<ActiveDebugLine>(
22860                    multibuffer_anchor,
22861                    Some(cx.theme().colors().editor_debugger_active_line_background),
22862                    window,
22863                    cx,
22864                );
22865
22866                cx.notify();
22867            }
22868
22869            handled.then_some(())
22870        })
22871        .is_some()
22872    }
22873
22874    pub fn copy_file_name_without_extension(
22875        &mut self,
22876        _: &CopyFileNameWithoutExtension,
22877        _: &mut Window,
22878        cx: &mut Context<Self>,
22879    ) {
22880        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22881            let file = buffer.read(cx).file()?;
22882            file.path().file_stem()
22883        }) {
22884            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22885        }
22886    }
22887
22888    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22889        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22890            let file = buffer.read(cx).file()?;
22891            Some(file.file_name(cx))
22892        }) {
22893            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22894        }
22895    }
22896
22897    pub fn toggle_git_blame(
22898        &mut self,
22899        _: &::git::Blame,
22900        window: &mut Window,
22901        cx: &mut Context<Self>,
22902    ) {
22903        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22904
22905        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22906            self.start_git_blame(true, window, cx);
22907        }
22908
22909        cx.notify();
22910    }
22911
22912    pub fn toggle_git_blame_inline(
22913        &mut self,
22914        _: &ToggleGitBlameInline,
22915        window: &mut Window,
22916        cx: &mut Context<Self>,
22917    ) {
22918        self.toggle_git_blame_inline_internal(true, window, cx);
22919        cx.notify();
22920    }
22921
22922    pub fn open_git_blame_commit(
22923        &mut self,
22924        _: &OpenGitBlameCommit,
22925        window: &mut Window,
22926        cx: &mut Context<Self>,
22927    ) {
22928        self.open_git_blame_commit_internal(window, cx);
22929    }
22930
22931    fn open_git_blame_commit_internal(
22932        &mut self,
22933        window: &mut Window,
22934        cx: &mut Context<Self>,
22935    ) -> Option<()> {
22936        let blame = self.blame.as_ref()?;
22937        let snapshot = self.snapshot(window, cx);
22938        let cursor = self
22939            .selections
22940            .newest::<Point>(&snapshot.display_snapshot)
22941            .head();
22942        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22943        let (_, blame_entry) = blame
22944            .update(cx, |blame, cx| {
22945                blame
22946                    .blame_for_rows(
22947                        &[RowInfo {
22948                            buffer_id: Some(buffer.remote_id()),
22949                            buffer_row: Some(point.row),
22950                            ..Default::default()
22951                        }],
22952                        cx,
22953                    )
22954                    .next()
22955            })
22956            .flatten()?;
22957        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22958        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22959        let workspace = self.workspace()?.downgrade();
22960        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22961        None
22962    }
22963
22964    pub fn git_blame_inline_enabled(&self) -> bool {
22965        self.git_blame_inline_enabled
22966    }
22967
22968    pub fn toggle_selection_menu(
22969        &mut self,
22970        _: &ToggleSelectionMenu,
22971        _: &mut Window,
22972        cx: &mut Context<Self>,
22973    ) {
22974        self.show_selection_menu = self
22975            .show_selection_menu
22976            .map(|show_selections_menu| !show_selections_menu)
22977            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22978
22979        cx.notify();
22980    }
22981
22982    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22983        self.show_selection_menu
22984            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22985    }
22986
22987    fn start_git_blame(
22988        &mut self,
22989        user_triggered: bool,
22990        window: &mut Window,
22991        cx: &mut Context<Self>,
22992    ) {
22993        if let Some(project) = self.project() {
22994            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22995                && buffer.read(cx).file().is_none()
22996            {
22997                return;
22998            }
22999
23000            let focused = self.focus_handle(cx).contains_focused(window, cx);
23001
23002            let project = project.clone();
23003            let blame = cx
23004                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23005            self.blame_subscription =
23006                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23007            self.blame = Some(blame);
23008        }
23009    }
23010
23011    fn toggle_git_blame_inline_internal(
23012        &mut self,
23013        user_triggered: bool,
23014        window: &mut Window,
23015        cx: &mut Context<Self>,
23016    ) {
23017        if self.git_blame_inline_enabled {
23018            self.git_blame_inline_enabled = false;
23019            self.show_git_blame_inline = false;
23020            self.show_git_blame_inline_delay_task.take();
23021        } else {
23022            self.git_blame_inline_enabled = true;
23023            self.start_git_blame_inline(user_triggered, window, cx);
23024        }
23025
23026        cx.notify();
23027    }
23028
23029    fn start_git_blame_inline(
23030        &mut self,
23031        user_triggered: bool,
23032        window: &mut Window,
23033        cx: &mut Context<Self>,
23034    ) {
23035        self.start_git_blame(user_triggered, window, cx);
23036
23037        if ProjectSettings::get_global(cx)
23038            .git
23039            .inline_blame_delay()
23040            .is_some()
23041        {
23042            self.start_inline_blame_timer(window, cx);
23043        } else {
23044            self.show_git_blame_inline = true
23045        }
23046    }
23047
23048    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23049        self.blame.as_ref()
23050    }
23051
23052    pub fn show_git_blame_gutter(&self) -> bool {
23053        self.show_git_blame_gutter
23054    }
23055
23056    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23057        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23058    }
23059
23060    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23061        self.show_git_blame_inline
23062            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23063            && !self.newest_selection_head_on_empty_line(cx)
23064            && self.has_blame_entries(cx)
23065    }
23066
23067    fn has_blame_entries(&self, cx: &App) -> bool {
23068        self.blame()
23069            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23070    }
23071
23072    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23073        let cursor_anchor = self.selections.newest_anchor().head();
23074
23075        let snapshot = self.buffer.read(cx).snapshot(cx);
23076        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23077
23078        snapshot.line_len(buffer_row) == 0
23079    }
23080
23081    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23082        let buffer_and_selection = maybe!({
23083            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23084            let selection_range = selection.range();
23085
23086            let multi_buffer = self.buffer().read(cx);
23087            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23088            let buffer_ranges = multi_buffer_snapshot
23089                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23090
23091            let (buffer, range, _) = if selection.reversed {
23092                buffer_ranges.first()
23093            } else {
23094                buffer_ranges.last()
23095            }?;
23096
23097            let buffer_range = range.to_point(buffer);
23098
23099            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23100                return Some((
23101                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23102                    buffer_range.start.row..buffer_range.end.row,
23103                ));
23104            };
23105
23106            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23107            let start =
23108                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23109            let end =
23110                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23111
23112            Some((
23113                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23114                start.row..end.row,
23115            ))
23116        });
23117
23118        let Some((buffer, selection)) = buffer_and_selection else {
23119            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23120        };
23121
23122        let Some(project) = self.project() else {
23123            return Task::ready(Err(anyhow!("editor does not have project")));
23124        };
23125
23126        project.update(cx, |project, cx| {
23127            project.get_permalink_to_line(&buffer, selection, cx)
23128        })
23129    }
23130
23131    pub fn copy_permalink_to_line(
23132        &mut self,
23133        _: &CopyPermalinkToLine,
23134        window: &mut Window,
23135        cx: &mut Context<Self>,
23136    ) {
23137        let permalink_task = self.get_permalink_to_line(cx);
23138        let workspace = self.workspace();
23139
23140        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23141            Ok(permalink) => {
23142                cx.update(|_, cx| {
23143                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23144                })
23145                .ok();
23146            }
23147            Err(err) => {
23148                let message = format!("Failed to copy permalink: {err}");
23149
23150                anyhow::Result::<()>::Err(err).log_err();
23151
23152                if let Some(workspace) = workspace {
23153                    workspace
23154                        .update_in(cx, |workspace, _, cx| {
23155                            struct CopyPermalinkToLine;
23156
23157                            workspace.show_toast(
23158                                Toast::new(
23159                                    NotificationId::unique::<CopyPermalinkToLine>(),
23160                                    message,
23161                                ),
23162                                cx,
23163                            )
23164                        })
23165                        .ok();
23166                }
23167            }
23168        })
23169        .detach();
23170    }
23171
23172    pub fn copy_file_location(
23173        &mut self,
23174        _: &CopyFileLocation,
23175        _: &mut Window,
23176        cx: &mut Context<Self>,
23177    ) {
23178        let selection = self
23179            .selections
23180            .newest::<Point>(&self.display_snapshot(cx))
23181            .start
23182            .row
23183            + 1;
23184        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23185            let project = self.project()?.read(cx);
23186            let file = buffer.read(cx).file()?;
23187            let path = file.path().display(project.path_style(cx));
23188
23189            Some(format!("{path}:{selection}"))
23190        }) {
23191            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23192        }
23193    }
23194
23195    pub fn open_permalink_to_line(
23196        &mut self,
23197        _: &OpenPermalinkToLine,
23198        window: &mut Window,
23199        cx: &mut Context<Self>,
23200    ) {
23201        let permalink_task = self.get_permalink_to_line(cx);
23202        let workspace = self.workspace();
23203
23204        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23205            Ok(permalink) => {
23206                cx.update(|_, cx| {
23207                    cx.open_url(permalink.as_ref());
23208                })
23209                .ok();
23210            }
23211            Err(err) => {
23212                let message = format!("Failed to open permalink: {err}");
23213
23214                anyhow::Result::<()>::Err(err).log_err();
23215
23216                if let Some(workspace) = workspace {
23217                    workspace.update(cx, |workspace, cx| {
23218                        struct OpenPermalinkToLine;
23219
23220                        workspace.show_toast(
23221                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23222                            cx,
23223                        )
23224                    });
23225                }
23226            }
23227        })
23228        .detach();
23229    }
23230
23231    pub fn insert_uuid_v4(
23232        &mut self,
23233        _: &InsertUuidV4,
23234        window: &mut Window,
23235        cx: &mut Context<Self>,
23236    ) {
23237        self.insert_uuid(UuidVersion::V4, window, cx);
23238    }
23239
23240    pub fn insert_uuid_v7(
23241        &mut self,
23242        _: &InsertUuidV7,
23243        window: &mut Window,
23244        cx: &mut Context<Self>,
23245    ) {
23246        self.insert_uuid(UuidVersion::V7, window, cx);
23247    }
23248
23249    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23250        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23251        self.transact(window, cx, |this, window, cx| {
23252            let edits = this
23253                .selections
23254                .all::<Point>(&this.display_snapshot(cx))
23255                .into_iter()
23256                .map(|selection| {
23257                    let uuid = match version {
23258                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23259                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23260                    };
23261
23262                    (selection.range(), uuid.to_string())
23263                });
23264            this.edit(edits, cx);
23265            this.refresh_edit_prediction(true, false, window, cx);
23266        });
23267    }
23268
23269    pub fn open_selections_in_multibuffer(
23270        &mut self,
23271        _: &OpenSelectionsInMultibuffer,
23272        window: &mut Window,
23273        cx: &mut Context<Self>,
23274    ) {
23275        let multibuffer = self.buffer.read(cx);
23276
23277        let Some(buffer) = multibuffer.as_singleton() else {
23278            return;
23279        };
23280
23281        let Some(workspace) = self.workspace() else {
23282            return;
23283        };
23284
23285        let title = multibuffer.title(cx).to_string();
23286
23287        let locations = self
23288            .selections
23289            .all_anchors(&self.display_snapshot(cx))
23290            .iter()
23291            .map(|selection| {
23292                (
23293                    buffer.clone(),
23294                    (selection.start.text_anchor..selection.end.text_anchor)
23295                        .to_point(buffer.read(cx)),
23296                )
23297            })
23298            .into_group_map();
23299
23300        cx.spawn_in(window, async move |_, cx| {
23301            workspace.update_in(cx, |workspace, window, cx| {
23302                Self::open_locations_in_multibuffer(
23303                    workspace,
23304                    locations,
23305                    format!("Selections for '{title}'"),
23306                    false,
23307                    false,
23308                    MultibufferSelectionMode::All,
23309                    window,
23310                    cx,
23311                );
23312            })
23313        })
23314        .detach();
23315    }
23316
23317    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23318    /// last highlight added will be used.
23319    ///
23320    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23321    pub fn highlight_rows<T: 'static>(
23322        &mut self,
23323        range: Range<Anchor>,
23324        color: Hsla,
23325        options: RowHighlightOptions,
23326        cx: &mut Context<Self>,
23327    ) {
23328        let snapshot = self.buffer().read(cx).snapshot(cx);
23329        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23330        let ix = row_highlights.binary_search_by(|highlight| {
23331            Ordering::Equal
23332                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23333                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23334        });
23335
23336        if let Err(mut ix) = ix {
23337            let index = post_inc(&mut self.highlight_order);
23338
23339            // If this range intersects with the preceding highlight, then merge it with
23340            // the preceding highlight. Otherwise insert a new highlight.
23341            let mut merged = false;
23342            if ix > 0 {
23343                let prev_highlight = &mut row_highlights[ix - 1];
23344                if prev_highlight
23345                    .range
23346                    .end
23347                    .cmp(&range.start, &snapshot)
23348                    .is_ge()
23349                {
23350                    ix -= 1;
23351                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23352                        prev_highlight.range.end = range.end;
23353                    }
23354                    merged = true;
23355                    prev_highlight.index = index;
23356                    prev_highlight.color = color;
23357                    prev_highlight.options = options;
23358                }
23359            }
23360
23361            if !merged {
23362                row_highlights.insert(
23363                    ix,
23364                    RowHighlight {
23365                        range,
23366                        index,
23367                        color,
23368                        options,
23369                        type_id: TypeId::of::<T>(),
23370                    },
23371                );
23372            }
23373
23374            // If any of the following highlights intersect with this one, merge them.
23375            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23376                let highlight = &row_highlights[ix];
23377                if next_highlight
23378                    .range
23379                    .start
23380                    .cmp(&highlight.range.end, &snapshot)
23381                    .is_le()
23382                {
23383                    if next_highlight
23384                        .range
23385                        .end
23386                        .cmp(&highlight.range.end, &snapshot)
23387                        .is_gt()
23388                    {
23389                        row_highlights[ix].range.end = next_highlight.range.end;
23390                    }
23391                    row_highlights.remove(ix + 1);
23392                } else {
23393                    break;
23394                }
23395            }
23396        }
23397    }
23398
23399    /// Remove any highlighted row ranges of the given type that intersect the
23400    /// given ranges.
23401    pub fn remove_highlighted_rows<T: 'static>(
23402        &mut self,
23403        ranges_to_remove: Vec<Range<Anchor>>,
23404        cx: &mut Context<Self>,
23405    ) {
23406        let snapshot = self.buffer().read(cx).snapshot(cx);
23407        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23408        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23409        row_highlights.retain(|highlight| {
23410            while let Some(range_to_remove) = ranges_to_remove.peek() {
23411                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23412                    Ordering::Less | Ordering::Equal => {
23413                        ranges_to_remove.next();
23414                    }
23415                    Ordering::Greater => {
23416                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23417                            Ordering::Less | Ordering::Equal => {
23418                                return false;
23419                            }
23420                            Ordering::Greater => break,
23421                        }
23422                    }
23423                }
23424            }
23425
23426            true
23427        })
23428    }
23429
23430    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23431    pub fn clear_row_highlights<T: 'static>(&mut self) {
23432        self.highlighted_rows.remove(&TypeId::of::<T>());
23433    }
23434
23435    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23436    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23437        self.highlighted_rows
23438            .get(&TypeId::of::<T>())
23439            .map_or(&[] as &[_], |vec| vec.as_slice())
23440            .iter()
23441            .map(|highlight| (highlight.range.clone(), highlight.color))
23442    }
23443
23444    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23445    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23446    /// Allows to ignore certain kinds of highlights.
23447    pub fn highlighted_display_rows(
23448        &self,
23449        window: &mut Window,
23450        cx: &mut App,
23451    ) -> BTreeMap<DisplayRow, LineHighlight> {
23452        let snapshot = self.snapshot(window, cx);
23453        let mut used_highlight_orders = HashMap::default();
23454        self.highlighted_rows
23455            .iter()
23456            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23457            .fold(
23458                BTreeMap::<DisplayRow, LineHighlight>::new(),
23459                |mut unique_rows, highlight| {
23460                    let start = highlight.range.start.to_display_point(&snapshot);
23461                    let end = highlight.range.end.to_display_point(&snapshot);
23462                    let start_row = start.row().0;
23463                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23464                    {
23465                        end.row().0.saturating_sub(1)
23466                    } else {
23467                        end.row().0
23468                    };
23469                    for row in start_row..=end_row {
23470                        let used_index =
23471                            used_highlight_orders.entry(row).or_insert(highlight.index);
23472                        if highlight.index >= *used_index {
23473                            *used_index = highlight.index;
23474                            unique_rows.insert(
23475                                DisplayRow(row),
23476                                LineHighlight {
23477                                    include_gutter: highlight.options.include_gutter,
23478                                    border: None,
23479                                    background: highlight.color.into(),
23480                                    type_id: Some(highlight.type_id),
23481                                },
23482                            );
23483                        }
23484                    }
23485                    unique_rows
23486                },
23487            )
23488    }
23489
23490    pub fn highlighted_display_row_for_autoscroll(
23491        &self,
23492        snapshot: &DisplaySnapshot,
23493    ) -> Option<DisplayRow> {
23494        self.highlighted_rows
23495            .values()
23496            .flat_map(|highlighted_rows| highlighted_rows.iter())
23497            .filter_map(|highlight| {
23498                if highlight.options.autoscroll {
23499                    Some(highlight.range.start.to_display_point(snapshot).row())
23500                } else {
23501                    None
23502                }
23503            })
23504            .min()
23505    }
23506
23507    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23508        self.highlight_background(
23509            HighlightKey::SearchWithinRange,
23510            ranges,
23511            |_, colors| colors.colors().editor_document_highlight_read_background,
23512            cx,
23513        )
23514    }
23515
23516    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23517        self.breadcrumb_header = Some(new_header);
23518    }
23519
23520    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23521        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23522    }
23523
23524    pub fn highlight_background(
23525        &mut self,
23526        key: HighlightKey,
23527        ranges: &[Range<Anchor>],
23528        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23529        cx: &mut Context<Self>,
23530    ) {
23531        self.background_highlights
23532            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23533        self.scrollbar_marker_state.dirty = true;
23534        cx.notify();
23535    }
23536
23537    pub fn clear_background_highlights(
23538        &mut self,
23539        key: HighlightKey,
23540        cx: &mut Context<Self>,
23541    ) -> Option<BackgroundHighlight> {
23542        let text_highlights = self.background_highlights.remove(&key)?;
23543        if !text_highlights.1.is_empty() {
23544            self.scrollbar_marker_state.dirty = true;
23545            cx.notify();
23546        }
23547        Some(text_highlights)
23548    }
23549
23550    pub fn highlight_gutter<T: 'static>(
23551        &mut self,
23552        ranges: impl Into<Vec<Range<Anchor>>>,
23553        color_fetcher: fn(&App) -> Hsla,
23554        cx: &mut Context<Self>,
23555    ) {
23556        self.gutter_highlights
23557            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23558        cx.notify();
23559    }
23560
23561    pub fn clear_gutter_highlights<T: 'static>(
23562        &mut self,
23563        cx: &mut Context<Self>,
23564    ) -> Option<GutterHighlight> {
23565        cx.notify();
23566        self.gutter_highlights.remove(&TypeId::of::<T>())
23567    }
23568
23569    pub fn insert_gutter_highlight<T: 'static>(
23570        &mut self,
23571        range: Range<Anchor>,
23572        color_fetcher: fn(&App) -> Hsla,
23573        cx: &mut Context<Self>,
23574    ) {
23575        let snapshot = self.buffer().read(cx).snapshot(cx);
23576        let mut highlights = self
23577            .gutter_highlights
23578            .remove(&TypeId::of::<T>())
23579            .map(|(_, highlights)| highlights)
23580            .unwrap_or_default();
23581        let ix = highlights.binary_search_by(|highlight| {
23582            Ordering::Equal
23583                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23584                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23585        });
23586        if let Err(ix) = ix {
23587            highlights.insert(ix, range);
23588        }
23589        self.gutter_highlights
23590            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23591    }
23592
23593    pub fn remove_gutter_highlights<T: 'static>(
23594        &mut self,
23595        ranges_to_remove: Vec<Range<Anchor>>,
23596        cx: &mut Context<Self>,
23597    ) {
23598        let snapshot = self.buffer().read(cx).snapshot(cx);
23599        let Some((color_fetcher, mut gutter_highlights)) =
23600            self.gutter_highlights.remove(&TypeId::of::<T>())
23601        else {
23602            return;
23603        };
23604        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23605        gutter_highlights.retain(|highlight| {
23606            while let Some(range_to_remove) = ranges_to_remove.peek() {
23607                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23608                    Ordering::Less | Ordering::Equal => {
23609                        ranges_to_remove.next();
23610                    }
23611                    Ordering::Greater => {
23612                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23613                            Ordering::Less | Ordering::Equal => {
23614                                return false;
23615                            }
23616                            Ordering::Greater => break,
23617                        }
23618                    }
23619                }
23620            }
23621
23622            true
23623        });
23624        self.gutter_highlights
23625            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23626    }
23627
23628    #[cfg(any(test, feature = "test-support"))]
23629    pub fn all_text_highlights(
23630        &self,
23631        window: &mut Window,
23632        cx: &mut Context<Self>,
23633    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23634        let snapshot = self.snapshot(window, cx);
23635        self.display_map.update(cx, |display_map, _| {
23636            display_map
23637                .all_text_highlights()
23638                .map(|(_, highlight)| {
23639                    let (style, ranges) = highlight.as_ref();
23640                    (
23641                        *style,
23642                        ranges
23643                            .iter()
23644                            .map(|range| range.clone().to_display_points(&snapshot))
23645                            .collect(),
23646                    )
23647                })
23648                .collect()
23649        })
23650    }
23651
23652    #[cfg(any(test, feature = "test-support"))]
23653    pub fn all_text_background_highlights(
23654        &self,
23655        window: &mut Window,
23656        cx: &mut Context<Self>,
23657    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23658        let snapshot = self.snapshot(window, cx);
23659        let buffer = &snapshot.buffer_snapshot();
23660        let start = buffer.anchor_before(MultiBufferOffset(0));
23661        let end = buffer.anchor_after(buffer.len());
23662        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23663    }
23664
23665    #[cfg(any(test, feature = "test-support"))]
23666    pub fn sorted_background_highlights_in_range(
23667        &self,
23668        search_range: Range<Anchor>,
23669        display_snapshot: &DisplaySnapshot,
23670        theme: &Theme,
23671    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23672        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23673        res.sort_by(|a, b| {
23674            a.0.start
23675                .cmp(&b.0.start)
23676                .then_with(|| a.0.end.cmp(&b.0.end))
23677                .then_with(|| a.1.cmp(&b.1))
23678        });
23679        res
23680    }
23681
23682    #[cfg(any(test, feature = "test-support"))]
23683    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23684        let snapshot = self.buffer().read(cx).snapshot(cx);
23685
23686        let highlights = self
23687            .background_highlights
23688            .get(&HighlightKey::BufferSearchHighlights);
23689
23690        if let Some((_color, ranges)) = highlights {
23691            ranges
23692                .iter()
23693                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23694                .collect_vec()
23695        } else {
23696            vec![]
23697        }
23698    }
23699
23700    fn document_highlights_for_position<'a>(
23701        &'a self,
23702        position: Anchor,
23703        buffer: &'a MultiBufferSnapshot,
23704    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23705        let read_highlights = self
23706            .background_highlights
23707            .get(&HighlightKey::DocumentHighlightRead)
23708            .map(|h| &h.1);
23709        let write_highlights = self
23710            .background_highlights
23711            .get(&HighlightKey::DocumentHighlightWrite)
23712            .map(|h| &h.1);
23713        let left_position = position.bias_left(buffer);
23714        let right_position = position.bias_right(buffer);
23715        read_highlights
23716            .into_iter()
23717            .chain(write_highlights)
23718            .flat_map(move |ranges| {
23719                let start_ix = match ranges.binary_search_by(|probe| {
23720                    let cmp = probe.end.cmp(&left_position, buffer);
23721                    if cmp.is_ge() {
23722                        Ordering::Greater
23723                    } else {
23724                        Ordering::Less
23725                    }
23726                }) {
23727                    Ok(i) | Err(i) => i,
23728                };
23729
23730                ranges[start_ix..]
23731                    .iter()
23732                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23733            })
23734    }
23735
23736    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23737        self.background_highlights
23738            .get(&key)
23739            .is_some_and(|(_, highlights)| !highlights.is_empty())
23740    }
23741
23742    /// Returns all background highlights for a given range.
23743    ///
23744    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23745    pub fn background_highlights_in_range(
23746        &self,
23747        search_range: Range<Anchor>,
23748        display_snapshot: &DisplaySnapshot,
23749        theme: &Theme,
23750    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23751        let mut results = Vec::new();
23752        for (color_fetcher, ranges) in self.background_highlights.values() {
23753            let start_ix = match ranges.binary_search_by(|probe| {
23754                let cmp = probe
23755                    .end
23756                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23757                if cmp.is_gt() {
23758                    Ordering::Greater
23759                } else {
23760                    Ordering::Less
23761                }
23762            }) {
23763                Ok(i) | Err(i) => i,
23764            };
23765            for (index, range) in ranges[start_ix..].iter().enumerate() {
23766                if range
23767                    .start
23768                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23769                    .is_ge()
23770                {
23771                    break;
23772                }
23773
23774                let color = color_fetcher(&(start_ix + index), theme);
23775                let start = range.start.to_display_point(display_snapshot);
23776                let end = range.end.to_display_point(display_snapshot);
23777                results.push((start..end, color))
23778            }
23779        }
23780        results
23781    }
23782
23783    pub fn gutter_highlights_in_range(
23784        &self,
23785        search_range: Range<Anchor>,
23786        display_snapshot: &DisplaySnapshot,
23787        cx: &App,
23788    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23789        let mut results = Vec::new();
23790        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23791            let color = color_fetcher(cx);
23792            let start_ix = match ranges.binary_search_by(|probe| {
23793                let cmp = probe
23794                    .end
23795                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23796                if cmp.is_gt() {
23797                    Ordering::Greater
23798                } else {
23799                    Ordering::Less
23800                }
23801            }) {
23802                Ok(i) | Err(i) => i,
23803            };
23804            for range in &ranges[start_ix..] {
23805                if range
23806                    .start
23807                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23808                    .is_ge()
23809                {
23810                    break;
23811                }
23812
23813                let start = range.start.to_display_point(display_snapshot);
23814                let end = range.end.to_display_point(display_snapshot);
23815                results.push((start..end, color))
23816            }
23817        }
23818        results
23819    }
23820
23821    /// Get the text ranges corresponding to the redaction query
23822    pub fn redacted_ranges(
23823        &self,
23824        search_range: Range<Anchor>,
23825        display_snapshot: &DisplaySnapshot,
23826        cx: &App,
23827    ) -> Vec<Range<DisplayPoint>> {
23828        display_snapshot
23829            .buffer_snapshot()
23830            .redacted_ranges(search_range, |file| {
23831                if let Some(file) = file {
23832                    file.is_private()
23833                        && EditorSettings::get(
23834                            Some(SettingsLocation {
23835                                worktree_id: file.worktree_id(cx),
23836                                path: file.path().as_ref(),
23837                            }),
23838                            cx,
23839                        )
23840                        .redact_private_values
23841                } else {
23842                    false
23843                }
23844            })
23845            .map(|range| {
23846                range.start.to_display_point(display_snapshot)
23847                    ..range.end.to_display_point(display_snapshot)
23848            })
23849            .collect()
23850    }
23851
23852    pub fn highlight_text_key(
23853        &mut self,
23854        key: HighlightKey,
23855        ranges: Vec<Range<Anchor>>,
23856        style: HighlightStyle,
23857        merge: bool,
23858        cx: &mut Context<Self>,
23859    ) {
23860        self.display_map.update(cx, |map, cx| {
23861            map.highlight_text(key, ranges, style, merge, cx);
23862        });
23863        cx.notify();
23864    }
23865
23866    pub fn highlight_text(
23867        &mut self,
23868        key: HighlightKey,
23869        ranges: Vec<Range<Anchor>>,
23870        style: HighlightStyle,
23871        cx: &mut Context<Self>,
23872    ) {
23873        self.display_map.update(cx, |map, cx| {
23874            map.highlight_text(key, ranges, style, false, cx)
23875        });
23876        cx.notify();
23877    }
23878
23879    pub fn text_highlights<'a>(
23880        &'a self,
23881        key: HighlightKey,
23882        cx: &'a App,
23883    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23884        self.display_map.read(cx).text_highlights(key)
23885    }
23886
23887    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23888        let cleared = self
23889            .display_map
23890            .update(cx, |map, _| map.clear_highlights(key));
23891        if cleared {
23892            cx.notify();
23893        }
23894    }
23895
23896    pub fn clear_highlights_with(
23897        &mut self,
23898        f: &mut dyn FnMut(&HighlightKey) -> bool,
23899        cx: &mut Context<Self>,
23900    ) {
23901        let cleared = self
23902            .display_map
23903            .update(cx, |map, _| map.clear_highlights_with(f));
23904        if cleared {
23905            cx.notify();
23906        }
23907    }
23908
23909    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23910        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23911            && self.focus_handle.is_focused(window)
23912    }
23913
23914    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23915        self.show_cursor_when_unfocused = is_enabled;
23916        cx.notify();
23917    }
23918
23919    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23920        cx.notify();
23921    }
23922
23923    fn on_debug_session_event(
23924        &mut self,
23925        _session: Entity<Session>,
23926        event: &SessionEvent,
23927        cx: &mut Context<Self>,
23928    ) {
23929        if let SessionEvent::InvalidateInlineValue = event {
23930            self.refresh_inline_values(cx);
23931        }
23932    }
23933
23934    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23935        let Some(project) = self.project.clone() else {
23936            return;
23937        };
23938
23939        if !self.inline_value_cache.enabled {
23940            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23941            self.splice_inlays(&inlays, Vec::new(), cx);
23942            return;
23943        }
23944
23945        let current_execution_position = self
23946            .highlighted_rows
23947            .get(&TypeId::of::<ActiveDebugLine>())
23948            .and_then(|lines| lines.last().map(|line| line.range.end));
23949
23950        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23951            let inline_values = editor
23952                .update(cx, |editor, cx| {
23953                    let Some(current_execution_position) = current_execution_position else {
23954                        return Some(Task::ready(Ok(Vec::new())));
23955                    };
23956
23957                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23958                        let snapshot = buffer.snapshot(cx);
23959
23960                        let excerpt = snapshot.excerpt_containing(
23961                            current_execution_position..current_execution_position,
23962                        )?;
23963
23964                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23965                    })?;
23966
23967                    let range =
23968                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23969
23970                    project.inline_values(buffer, range, cx)
23971                })
23972                .ok()
23973                .flatten()?
23974                .await
23975                .context("refreshing debugger inlays")
23976                .log_err()?;
23977
23978            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23979
23980            for (buffer_id, inline_value) in inline_values
23981                .into_iter()
23982                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23983            {
23984                buffer_inline_values
23985                    .entry(buffer_id)
23986                    .or_default()
23987                    .push(inline_value);
23988            }
23989
23990            editor
23991                .update(cx, |editor, cx| {
23992                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23993                    let mut new_inlays = Vec::default();
23994
23995                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23996                        let buffer_id = buffer_snapshot.remote_id();
23997                        buffer_inline_values
23998                            .get(&buffer_id)
23999                            .into_iter()
24000                            .flatten()
24001                            .for_each(|hint| {
24002                                let inlay = Inlay::debugger(
24003                                    post_inc(&mut editor.next_inlay_id),
24004                                    Anchor::in_buffer(excerpt_id, hint.position),
24005                                    hint.text(),
24006                                );
24007                                if !inlay.text().chars().contains(&'\n') {
24008                                    new_inlays.push(inlay);
24009                                }
24010                            });
24011                    }
24012
24013                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24014                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24015
24016                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24017                })
24018                .ok()?;
24019            Some(())
24020        });
24021    }
24022
24023    fn on_buffer_event(
24024        &mut self,
24025        multibuffer: &Entity<MultiBuffer>,
24026        event: &multi_buffer::Event,
24027        window: &mut Window,
24028        cx: &mut Context<Self>,
24029    ) {
24030        match event {
24031            multi_buffer::Event::Edited { edited_buffer } => {
24032                self.scrollbar_marker_state.dirty = true;
24033                self.active_indent_guides_state.dirty = true;
24034                self.refresh_active_diagnostics(cx);
24035                self.refresh_code_actions(window, cx);
24036                self.refresh_single_line_folds(window, cx);
24037                let snapshot = self.snapshot(window, cx);
24038                self.refresh_matching_bracket_highlights(&snapshot, cx);
24039                self.refresh_outline_symbols_at_cursor(cx);
24040                self.refresh_sticky_headers(&snapshot, cx);
24041                if self.has_active_edit_prediction() {
24042                    self.update_visible_edit_prediction(window, cx);
24043                }
24044
24045                // Clean up orphaned review comments after edits
24046                self.cleanup_orphaned_review_comments(cx);
24047
24048                if let Some(buffer) = edited_buffer {
24049                    if buffer.read(cx).file().is_none() {
24050                        cx.emit(EditorEvent::TitleChanged);
24051                    }
24052
24053                    if self.project.is_some() {
24054                        let buffer_id = buffer.read(cx).remote_id();
24055                        self.register_buffer(buffer_id, cx);
24056                        self.update_lsp_data(Some(buffer_id), window, cx);
24057                        self.refresh_inlay_hints(
24058                            InlayHintRefreshReason::BufferEdited(buffer_id),
24059                            cx,
24060                        );
24061                    }
24062                }
24063
24064                cx.emit(EditorEvent::BufferEdited);
24065                cx.emit(SearchEvent::MatchesInvalidated);
24066
24067                let Some(project) = &self.project else { return };
24068                let (telemetry, is_via_ssh) = {
24069                    let project = project.read(cx);
24070                    let telemetry = project.client().telemetry().clone();
24071                    let is_via_ssh = project.is_via_remote_server();
24072                    (telemetry, is_via_ssh)
24073                };
24074                telemetry.log_edit_event("editor", is_via_ssh);
24075            }
24076            multi_buffer::Event::ExcerptsAdded {
24077                buffer,
24078                predecessor,
24079                excerpts,
24080            } => {
24081                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24082                let buffer_id = buffer.read(cx).remote_id();
24083                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24084                    && let Some(project) = &self.project
24085                {
24086                    update_uncommitted_diff_for_buffer(
24087                        cx.entity(),
24088                        project,
24089                        [buffer.clone()],
24090                        self.buffer.clone(),
24091                        cx,
24092                    )
24093                    .detach();
24094                }
24095                self.semantic_token_state
24096                    .invalidate_buffer(&buffer.read(cx).remote_id());
24097                self.update_lsp_data(Some(buffer_id), window, cx);
24098                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24099                self.colorize_brackets(false, cx);
24100                self.refresh_selected_text_highlights(true, window, cx);
24101                cx.emit(EditorEvent::ExcerptsAdded {
24102                    buffer: buffer.clone(),
24103                    predecessor: *predecessor,
24104                    excerpts: excerpts.clone(),
24105                });
24106            }
24107            multi_buffer::Event::ExcerptsRemoved {
24108                ids,
24109                removed_buffer_ids,
24110            } => {
24111                if let Some(inlay_hints) = &mut self.inlay_hints {
24112                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24113                }
24114                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24115                for buffer_id in removed_buffer_ids {
24116                    self.registered_buffers.remove(buffer_id);
24117                    self.tasks
24118                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24119                    self.semantic_token_state.invalidate_buffer(buffer_id);
24120                    self.display_map.update(cx, |display_map, cx| {
24121                        display_map.invalidate_semantic_highlights(*buffer_id);
24122                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24123                    });
24124                }
24125                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24126                cx.emit(EditorEvent::ExcerptsRemoved {
24127                    ids: ids.clone(),
24128                    removed_buffer_ids: removed_buffer_ids.clone(),
24129                });
24130            }
24131            multi_buffer::Event::ExcerptsEdited {
24132                excerpt_ids,
24133                buffer_ids,
24134            } => {
24135                self.display_map.update(cx, |map, cx| {
24136                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24137                });
24138                cx.emit(EditorEvent::ExcerptsEdited {
24139                    ids: excerpt_ids.clone(),
24140                });
24141            }
24142            multi_buffer::Event::ExcerptsExpanded { ids } => {
24143                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24144                self.refresh_document_highlights(cx);
24145                let snapshot = multibuffer.read(cx).snapshot(cx);
24146                for id in ids {
24147                    self.fetched_tree_sitter_chunks.remove(id);
24148                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24149                        self.semantic_token_state
24150                            .invalidate_buffer(&buffer.remote_id());
24151                    }
24152                }
24153                self.colorize_brackets(false, cx);
24154                self.update_lsp_data(None, window, cx);
24155                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24156            }
24157            multi_buffer::Event::Reparsed(buffer_id) => {
24158                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24159                self.refresh_selected_text_highlights(true, window, cx);
24160                self.colorize_brackets(true, cx);
24161                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24162
24163                cx.emit(EditorEvent::Reparsed(*buffer_id));
24164            }
24165            multi_buffer::Event::DiffHunksToggled => {
24166                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24167            }
24168            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24169                if !is_fresh_language {
24170                    self.registered_buffers.remove(&buffer_id);
24171                }
24172                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24173                cx.emit(EditorEvent::Reparsed(*buffer_id));
24174                cx.notify();
24175            }
24176            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24177            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24178            multi_buffer::Event::FileHandleChanged
24179            | multi_buffer::Event::Reloaded
24180            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24181            multi_buffer::Event::DiagnosticsUpdated => {
24182                self.update_diagnostics_state(window, cx);
24183            }
24184            _ => {}
24185        };
24186    }
24187
24188    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24189        if !self.diagnostics_enabled() {
24190            return;
24191        }
24192        self.refresh_active_diagnostics(cx);
24193        self.refresh_inline_diagnostics(true, window, cx);
24194        self.scrollbar_marker_state.dirty = true;
24195        cx.notify();
24196    }
24197
24198    pub fn start_temporary_diff_override(&mut self) {
24199        self.load_diff_task.take();
24200        self.temporary_diff_override = true;
24201    }
24202
24203    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24204        self.temporary_diff_override = false;
24205        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24206        self.buffer.update(cx, |buffer, cx| {
24207            buffer.set_all_diff_hunks_collapsed(cx);
24208        });
24209
24210        if let Some(project) = self.project.clone() {
24211            self.load_diff_task = Some(
24212                update_uncommitted_diff_for_buffer(
24213                    cx.entity(),
24214                    &project,
24215                    self.buffer.read(cx).all_buffers(),
24216                    self.buffer.clone(),
24217                    cx,
24218                )
24219                .shared(),
24220            );
24221        }
24222    }
24223
24224    fn on_display_map_changed(
24225        &mut self,
24226        _: Entity<DisplayMap>,
24227        _: &mut Window,
24228        cx: &mut Context<Self>,
24229    ) {
24230        cx.notify();
24231    }
24232
24233    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24234        if !self.mode.is_full() {
24235            return None;
24236        }
24237
24238        let theme_settings = theme::ThemeSettings::get_global(cx);
24239        let theme = cx.theme();
24240        let accent_colors = theme.accents().clone();
24241
24242        let accent_overrides = theme_settings
24243            .theme_overrides
24244            .get(theme.name.as_ref())
24245            .map(|theme_style| &theme_style.accents)
24246            .into_iter()
24247            .flatten()
24248            .chain(
24249                theme_settings
24250                    .experimental_theme_overrides
24251                    .as_ref()
24252                    .map(|overrides| &overrides.accents)
24253                    .into_iter()
24254                    .flatten(),
24255            )
24256            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24257            .collect();
24258
24259        Some(AccentData {
24260            colors: accent_colors,
24261            overrides: accent_overrides,
24262        })
24263    }
24264
24265    fn fetch_applicable_language_settings(
24266        &self,
24267        cx: &App,
24268    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24269        if !self.mode.is_full() {
24270            return HashMap::default();
24271        }
24272
24273        self.buffer().read(cx).all_buffers().into_iter().fold(
24274            HashMap::default(),
24275            |mut acc, buffer| {
24276                let buffer = buffer.read(cx);
24277                let language = buffer.language().map(|language| language.name());
24278                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24279                    let file = buffer.file();
24280                    v.insert(language_settings(language, file, cx).into_owned());
24281                }
24282                acc
24283            },
24284        )
24285    }
24286
24287    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24288        let new_language_settings = self.fetch_applicable_language_settings(cx);
24289        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24290        self.applicable_language_settings = new_language_settings;
24291
24292        let new_accents = self.fetch_accent_data(cx);
24293        let accents_changed = new_accents != self.accent_data;
24294        self.accent_data = new_accents;
24295
24296        if self.diagnostics_enabled() {
24297            let new_severity = EditorSettings::get_global(cx)
24298                .diagnostics_max_severity
24299                .unwrap_or(DiagnosticSeverity::Hint);
24300            self.set_max_diagnostics_severity(new_severity, cx);
24301        }
24302        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24303        self.update_edit_prediction_settings(cx);
24304        self.refresh_edit_prediction(true, false, window, cx);
24305        self.refresh_inline_values(cx);
24306
24307        let old_cursor_shape = self.cursor_shape;
24308        let old_show_breadcrumbs = self.show_breadcrumbs;
24309
24310        {
24311            let editor_settings = EditorSettings::get_global(cx);
24312            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24313            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24314            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24315            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24316        }
24317
24318        if old_cursor_shape != self.cursor_shape {
24319            cx.emit(EditorEvent::CursorShapeChanged);
24320        }
24321
24322        if old_show_breadcrumbs != self.show_breadcrumbs {
24323            cx.emit(EditorEvent::BreadcrumbsChanged);
24324        }
24325
24326        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24327            let project_settings = ProjectSettings::get_global(cx);
24328            (
24329                project_settings.session.restore_unsaved_buffers,
24330                project_settings.diagnostics.inline.enabled,
24331                project_settings.git.inline_blame.enabled,
24332            )
24333        };
24334        self.buffer_serialization = self
24335            .should_serialize_buffer()
24336            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24337
24338        if self.mode.is_full() {
24339            if self.show_inline_diagnostics != show_inline_diagnostics {
24340                self.show_inline_diagnostics = show_inline_diagnostics;
24341                self.refresh_inline_diagnostics(false, window, cx);
24342            }
24343
24344            if self.git_blame_inline_enabled != inline_blame_enabled {
24345                self.toggle_git_blame_inline_internal(false, window, cx);
24346            }
24347
24348            let minimap_settings = EditorSettings::get_global(cx).minimap;
24349            if self.minimap_visibility != MinimapVisibility::Disabled {
24350                if self.minimap_visibility.settings_visibility()
24351                    != minimap_settings.minimap_enabled()
24352                {
24353                    self.set_minimap_visibility(
24354                        MinimapVisibility::for_mode(self.mode(), cx),
24355                        window,
24356                        cx,
24357                    );
24358                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24359                    minimap_entity.update(cx, |minimap_editor, cx| {
24360                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24361                    })
24362                }
24363            }
24364
24365            if language_settings_changed || accents_changed {
24366                self.colorize_brackets(true, cx);
24367            }
24368
24369            if language_settings_changed {
24370                self.clear_disabled_lsp_folding_ranges(window, cx);
24371                self.refresh_document_symbols(None, cx);
24372            }
24373
24374            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24375                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24376            }) {
24377                if !inlay_splice.is_empty() {
24378                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24379                }
24380                self.refresh_document_colors(None, window, cx);
24381            }
24382
24383            self.refresh_inlay_hints(
24384                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24385                    self.selections.newest_anchor().head(),
24386                    &self.buffer.read(cx).snapshot(cx),
24387                    cx,
24388                )),
24389                cx,
24390            );
24391
24392            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24393                .global_lsp_settings
24394                .semantic_token_rules
24395                .clone();
24396            let semantic_token_rules_changed = self
24397                .semantic_token_state
24398                .update_rules(new_semantic_token_rules);
24399            if language_settings_changed || semantic_token_rules_changed {
24400                self.invalidate_semantic_tokens(None);
24401                self.refresh_semantic_tokens(None, None, cx);
24402            }
24403        }
24404
24405        cx.notify();
24406    }
24407
24408    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24409        if !self.mode.is_full() {
24410            return;
24411        }
24412
24413        let new_accents = self.fetch_accent_data(cx);
24414        if new_accents != self.accent_data {
24415            self.accent_data = new_accents;
24416            self.colorize_brackets(true, cx);
24417        }
24418
24419        self.invalidate_semantic_tokens(None);
24420        self.refresh_semantic_tokens(None, None, cx);
24421    }
24422
24423    pub fn set_searchable(&mut self, searchable: bool) {
24424        self.searchable = searchable;
24425    }
24426
24427    pub fn searchable(&self) -> bool {
24428        self.searchable
24429    }
24430
24431    pub fn open_excerpts_in_split(
24432        &mut self,
24433        _: &OpenExcerptsSplit,
24434        window: &mut Window,
24435        cx: &mut Context<Self>,
24436    ) {
24437        self.open_excerpts_common(None, true, window, cx)
24438    }
24439
24440    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24441        self.open_excerpts_common(None, false, window, cx)
24442    }
24443
24444    pub(crate) fn open_excerpts_common(
24445        &mut self,
24446        jump_data: Option<JumpData>,
24447        split: bool,
24448        window: &mut Window,
24449        cx: &mut Context<Self>,
24450    ) {
24451        if self.buffer.read(cx).is_singleton() {
24452            cx.propagate();
24453            return;
24454        }
24455
24456        let mut new_selections_by_buffer = HashMap::default();
24457        match &jump_data {
24458            Some(JumpData::MultiBufferPoint {
24459                excerpt_id,
24460                position,
24461                anchor,
24462                line_offset_from_top,
24463            }) => {
24464                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24465                if let Some(buffer) = multi_buffer_snapshot
24466                    .buffer_id_for_excerpt(*excerpt_id)
24467                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24468                {
24469                    let buffer_snapshot = buffer.read(cx).snapshot();
24470                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24471                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24472                    } else {
24473                        buffer_snapshot.clip_point(*position, Bias::Left)
24474                    };
24475                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24476                    new_selections_by_buffer.insert(
24477                        buffer,
24478                        (
24479                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24480                            Some(*line_offset_from_top),
24481                        ),
24482                    );
24483                }
24484            }
24485            Some(JumpData::MultiBufferRow {
24486                row,
24487                line_offset_from_top,
24488            }) => {
24489                let point = MultiBufferPoint::new(row.0, 0);
24490                if let Some((buffer, buffer_point, _)) =
24491                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24492                {
24493                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24494                    new_selections_by_buffer
24495                        .entry(buffer)
24496                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24497                        .0
24498                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24499                }
24500            }
24501            None => {
24502                let selections = self
24503                    .selections
24504                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24505                let multi_buffer = self.buffer.read(cx);
24506                for selection in selections {
24507                    for (snapshot, range, _, anchor) in multi_buffer
24508                        .snapshot(cx)
24509                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24510                    {
24511                        if let Some(anchor) = anchor {
24512                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24513                            else {
24514                                continue;
24515                            };
24516                            let offset = text::ToOffset::to_offset(
24517                                &anchor.text_anchor,
24518                                &buffer_handle.read(cx).snapshot(),
24519                            );
24520                            let range = BufferOffset(offset)..BufferOffset(offset);
24521                            new_selections_by_buffer
24522                                .entry(buffer_handle)
24523                                .or_insert((Vec::new(), None))
24524                                .0
24525                                .push(range)
24526                        } else {
24527                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24528                            else {
24529                                continue;
24530                            };
24531                            new_selections_by_buffer
24532                                .entry(buffer_handle)
24533                                .or_insert((Vec::new(), None))
24534                                .0
24535                                .push(range)
24536                        }
24537                    }
24538                }
24539            }
24540        }
24541
24542        if self.delegate_open_excerpts {
24543            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24544                .into_iter()
24545                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24546                .collect();
24547            if !selections_by_buffer.is_empty() {
24548                cx.emit(EditorEvent::OpenExcerptsRequested {
24549                    selections_by_buffer,
24550                    split,
24551                });
24552            }
24553            return;
24554        }
24555
24556        let Some(workspace) = self.workspace() else {
24557            cx.propagate();
24558            return;
24559        };
24560
24561        new_selections_by_buffer
24562            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24563
24564        if new_selections_by_buffer.is_empty() {
24565            return;
24566        }
24567
24568        Self::open_buffers_in_workspace(
24569            workspace.downgrade(),
24570            new_selections_by_buffer,
24571            split,
24572            window,
24573            cx,
24574        );
24575    }
24576
24577    pub(crate) fn open_buffers_in_workspace(
24578        workspace: WeakEntity<Workspace>,
24579        new_selections_by_buffer: HashMap<
24580            Entity<language::Buffer>,
24581            (Vec<Range<BufferOffset>>, Option<u32>),
24582        >,
24583        split: bool,
24584        window: &mut Window,
24585        cx: &mut App,
24586    ) {
24587        // We defer the pane interaction because we ourselves are a workspace item
24588        // and activating a new item causes the pane to call a method on us reentrantly,
24589        // which panics if we're on the stack.
24590        window.defer(cx, move |window, cx| {
24591            workspace
24592                .update(cx, |workspace, cx| {
24593                    let pane = if split {
24594                        workspace.adjacent_pane(window, cx)
24595                    } else {
24596                        workspace.active_pane().clone()
24597                    };
24598
24599                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24600                        let buffer_read = buffer.read(cx);
24601                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24602                            (true, project::File::from_dyn(Some(file)).is_some())
24603                        } else {
24604                            (false, false)
24605                        };
24606
24607                        // If project file is none workspace.open_project_item will fail to open the excerpt
24608                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24609                        // so we check if there's a tab match in that case first
24610                        let editor = (!has_file || !is_project_file)
24611                            .then(|| {
24612                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24613                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24614                                // Instead, we try to activate the existing editor in the pane first.
24615                                let (editor, pane_item_index, pane_item_id) =
24616                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24617                                        let editor = item.downcast::<Editor>()?;
24618                                        let singleton_buffer =
24619                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24620                                        if singleton_buffer == buffer {
24621                                            Some((editor, i, item.item_id()))
24622                                        } else {
24623                                            None
24624                                        }
24625                                    })?;
24626                                pane.update(cx, |pane, cx| {
24627                                    pane.activate_item(pane_item_index, true, true, window, cx);
24628                                    if !PreviewTabsSettings::get_global(cx)
24629                                        .enable_preview_from_multibuffer
24630                                    {
24631                                        pane.unpreview_item_if_preview(pane_item_id);
24632                                    }
24633                                });
24634                                Some(editor)
24635                            })
24636                            .flatten()
24637                            .unwrap_or_else(|| {
24638                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24639                                    .enable_keep_preview_on_code_navigation;
24640                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24641                                    .enable_preview_from_multibuffer;
24642                                workspace.open_project_item::<Self>(
24643                                    pane.clone(),
24644                                    buffer,
24645                                    true,
24646                                    true,
24647                                    keep_old_preview,
24648                                    allow_new_preview,
24649                                    window,
24650                                    cx,
24651                                )
24652                            });
24653
24654                        editor.update(cx, |editor, cx| {
24655                            if has_file && !is_project_file {
24656                                editor.set_read_only(true);
24657                            }
24658                            let autoscroll = match scroll_offset {
24659                                Some(scroll_offset) => {
24660                                    Autoscroll::top_relative(scroll_offset as usize)
24661                                }
24662                                None => Autoscroll::newest(),
24663                            };
24664                            let nav_history = editor.nav_history.take();
24665                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24666                            let Some((excerpt_id, _, buffer_snapshot)) =
24667                                multibuffer_snapshot.as_singleton()
24668                            else {
24669                                return;
24670                            };
24671                            editor.change_selections(
24672                                SelectionEffects::scroll(autoscroll),
24673                                window,
24674                                cx,
24675                                |s| {
24676                                    s.select_ranges(ranges.into_iter().map(|range| {
24677                                        let range = buffer_snapshot.anchor_before(range.start)
24678                                            ..buffer_snapshot.anchor_after(range.end);
24679                                        multibuffer_snapshot
24680                                            .anchor_range_in_excerpt(excerpt_id, range)
24681                                            .unwrap()
24682                                    }));
24683                                },
24684                            );
24685                            editor.nav_history = nav_history;
24686                        });
24687                    }
24688                })
24689                .ok();
24690        });
24691    }
24692
24693    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24694        let snapshot = self.buffer.read(cx).read(cx);
24695        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24696        Some(
24697            ranges
24698                .iter()
24699                .map(move |range| {
24700                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24701                })
24702                .collect(),
24703        )
24704    }
24705
24706    fn selection_replacement_ranges(
24707        &self,
24708        range: Range<MultiBufferOffsetUtf16>,
24709        cx: &mut App,
24710    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24711        let selections = self
24712            .selections
24713            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24714        let newest_selection = selections
24715            .iter()
24716            .max_by_key(|selection| selection.id)
24717            .unwrap();
24718        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24719        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24720        let snapshot = self.buffer.read(cx).read(cx);
24721        selections
24722            .into_iter()
24723            .map(|mut selection| {
24724                selection.start.0.0 =
24725                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24726                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24727                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24728                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24729            })
24730            .collect()
24731    }
24732
24733    fn report_editor_event(
24734        &self,
24735        reported_event: ReportEditorEvent,
24736        file_extension: Option<String>,
24737        cx: &App,
24738    ) {
24739        if cfg!(any(test, feature = "test-support")) {
24740            return;
24741        }
24742
24743        let Some(project) = &self.project else { return };
24744
24745        // If None, we are in a file without an extension
24746        let file = self
24747            .buffer
24748            .read(cx)
24749            .as_singleton()
24750            .and_then(|b| b.read(cx).file());
24751        let file_extension = file_extension.or(file
24752            .as_ref()
24753            .and_then(|file| Path::new(file.file_name(cx)).extension())
24754            .and_then(|e| e.to_str())
24755            .map(|a| a.to_string()));
24756
24757        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24758            .map(|vim_mode| vim_mode.0)
24759            .unwrap_or(false);
24760
24761        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24762        let copilot_enabled = edit_predictions_provider
24763            == language::language_settings::EditPredictionProvider::Copilot;
24764        let copilot_enabled_for_language = self
24765            .buffer
24766            .read(cx)
24767            .language_settings(cx)
24768            .show_edit_predictions;
24769
24770        let project = project.read(cx);
24771        let event_type = reported_event.event_type();
24772
24773        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24774            telemetry::event!(
24775                event_type,
24776                type = if auto_saved {"autosave"} else {"manual"},
24777                file_extension,
24778                vim_mode,
24779                copilot_enabled,
24780                copilot_enabled_for_language,
24781                edit_predictions_provider,
24782                is_via_ssh = project.is_via_remote_server(),
24783            );
24784        } else {
24785            telemetry::event!(
24786                event_type,
24787                file_extension,
24788                vim_mode,
24789                copilot_enabled,
24790                copilot_enabled_for_language,
24791                edit_predictions_provider,
24792                is_via_ssh = project.is_via_remote_server(),
24793            );
24794        };
24795    }
24796
24797    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24798    /// with each line being an array of {text, highlight} objects.
24799    fn copy_highlight_json(
24800        &mut self,
24801        _: &CopyHighlightJson,
24802        window: &mut Window,
24803        cx: &mut Context<Self>,
24804    ) {
24805        #[derive(Serialize)]
24806        struct Chunk<'a> {
24807            text: String,
24808            highlight: Option<&'a str>,
24809        }
24810
24811        let snapshot = self.buffer.read(cx).snapshot(cx);
24812        let range = self
24813            .selected_text_range(false, window, cx)
24814            .and_then(|selection| {
24815                if selection.range.is_empty() {
24816                    None
24817                } else {
24818                    Some(
24819                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24820                            selection.range.start,
24821                        )))
24822                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24823                                selection.range.end,
24824                            ))),
24825                    )
24826                }
24827            })
24828            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24829
24830        let chunks = snapshot.chunks(range, true);
24831        let mut lines = Vec::new();
24832        let mut line: VecDeque<Chunk> = VecDeque::new();
24833
24834        let Some(style) = self.style.as_ref() else {
24835            return;
24836        };
24837
24838        for chunk in chunks {
24839            let highlight = chunk
24840                .syntax_highlight_id
24841                .and_then(|id| id.name(&style.syntax));
24842            let mut chunk_lines = chunk.text.split('\n').peekable();
24843            while let Some(text) = chunk_lines.next() {
24844                let mut merged_with_last_token = false;
24845                if let Some(last_token) = line.back_mut()
24846                    && last_token.highlight == highlight
24847                {
24848                    last_token.text.push_str(text);
24849                    merged_with_last_token = true;
24850                }
24851
24852                if !merged_with_last_token {
24853                    line.push_back(Chunk {
24854                        text: text.into(),
24855                        highlight,
24856                    });
24857                }
24858
24859                if chunk_lines.peek().is_some() {
24860                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24861                        line.pop_front();
24862                    }
24863                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24864                        line.pop_back();
24865                    }
24866
24867                    lines.push(mem::take(&mut line));
24868                }
24869            }
24870        }
24871
24872        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24873            return;
24874        };
24875        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24876    }
24877
24878    pub fn open_context_menu(
24879        &mut self,
24880        _: &OpenContextMenu,
24881        window: &mut Window,
24882        cx: &mut Context<Self>,
24883    ) {
24884        self.request_autoscroll(Autoscroll::newest(), cx);
24885        let position = self
24886            .selections
24887            .newest_display(&self.display_snapshot(cx))
24888            .start;
24889        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24890    }
24891
24892    pub fn replay_insert_event(
24893        &mut self,
24894        text: &str,
24895        relative_utf16_range: Option<Range<isize>>,
24896        window: &mut Window,
24897        cx: &mut Context<Self>,
24898    ) {
24899        if !self.input_enabled {
24900            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24901            return;
24902        }
24903        if let Some(relative_utf16_range) = relative_utf16_range {
24904            let selections = self
24905                .selections
24906                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24907            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24908                let new_ranges = selections.into_iter().map(|range| {
24909                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24910                        range
24911                            .head()
24912                            .0
24913                            .0
24914                            .saturating_add_signed(relative_utf16_range.start),
24915                    ));
24916                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24917                        range
24918                            .head()
24919                            .0
24920                            .0
24921                            .saturating_add_signed(relative_utf16_range.end),
24922                    ));
24923                    start..end
24924                });
24925                s.select_ranges(new_ranges);
24926            });
24927        }
24928
24929        self.handle_input(text, window, cx);
24930    }
24931
24932    pub fn is_focused(&self, window: &Window) -> bool {
24933        self.focus_handle.is_focused(window)
24934    }
24935
24936    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24937        cx.emit(EditorEvent::Focused);
24938
24939        if let Some(descendant) = self
24940            .last_focused_descendant
24941            .take()
24942            .and_then(|descendant| descendant.upgrade())
24943        {
24944            window.focus(&descendant, cx);
24945        } else {
24946            if let Some(blame) = self.blame.as_ref() {
24947                blame.update(cx, GitBlame::focus)
24948            }
24949
24950            self.blink_manager.update(cx, BlinkManager::enable);
24951            self.show_cursor_names(window, cx);
24952            self.buffer.update(cx, |buffer, cx| {
24953                buffer.finalize_last_transaction(cx);
24954                if self.leader_id.is_none() {
24955                    buffer.set_active_selections(
24956                        &self.selections.disjoint_anchors_arc(),
24957                        self.selections.line_mode(),
24958                        self.cursor_shape,
24959                        cx,
24960                    );
24961                }
24962            });
24963
24964            if let Some(position_map) = self.last_position_map.clone() {
24965                EditorElement::mouse_moved(
24966                    self,
24967                    &MouseMoveEvent {
24968                        position: window.mouse_position(),
24969                        pressed_button: None,
24970                        modifiers: window.modifiers(),
24971                    },
24972                    &position_map,
24973                    None,
24974                    window,
24975                    cx,
24976                );
24977            }
24978        }
24979    }
24980
24981    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24982        cx.emit(EditorEvent::FocusedIn)
24983    }
24984
24985    fn handle_focus_out(
24986        &mut self,
24987        event: FocusOutEvent,
24988        _window: &mut Window,
24989        cx: &mut Context<Self>,
24990    ) {
24991        if event.blurred != self.focus_handle {
24992            self.last_focused_descendant = Some(event.blurred);
24993        }
24994        self.selection_drag_state = SelectionDragState::None;
24995        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24996    }
24997
24998    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24999        self.blink_manager.update(cx, BlinkManager::disable);
25000        self.buffer
25001            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25002
25003        if let Some(blame) = self.blame.as_ref() {
25004            blame.update(cx, GitBlame::blur)
25005        }
25006        if !self.hover_state.focused(window, cx) {
25007            hide_hover(self, cx);
25008        }
25009        if !self
25010            .context_menu
25011            .borrow()
25012            .as_ref()
25013            .is_some_and(|context_menu| context_menu.focused(window, cx))
25014        {
25015            self.hide_context_menu(window, cx);
25016        }
25017        self.take_active_edit_prediction(cx);
25018        cx.emit(EditorEvent::Blurred);
25019        cx.notify();
25020    }
25021
25022    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25023        let mut pending: String = window
25024            .pending_input_keystrokes()
25025            .into_iter()
25026            .flatten()
25027            .filter_map(|keystroke| keystroke.key_char.clone())
25028            .collect();
25029
25030        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25031            pending = "".to_string();
25032        }
25033
25034        let existing_pending = self
25035            .text_highlights(HighlightKey::PendingInput, cx)
25036            .map(|(_, ranges)| ranges.to_vec());
25037        if existing_pending.is_none() && pending.is_empty() {
25038            return;
25039        }
25040        let transaction =
25041            self.transact(window, cx, |this, window, cx| {
25042                let selections = this
25043                    .selections
25044                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25045                let edits = selections
25046                    .iter()
25047                    .map(|selection| (selection.end..selection.end, pending.clone()));
25048                this.edit(edits, cx);
25049                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25050                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25051                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25052                    }));
25053                });
25054                if let Some(existing_ranges) = existing_pending {
25055                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25056                    this.edit(edits, cx);
25057                }
25058            });
25059
25060        let snapshot = self.snapshot(window, cx);
25061        let ranges = self
25062            .selections
25063            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25064            .into_iter()
25065            .map(|selection| {
25066                snapshot.buffer_snapshot().anchor_after(selection.end)
25067                    ..snapshot
25068                        .buffer_snapshot()
25069                        .anchor_before(selection.end + pending.len())
25070            })
25071            .collect();
25072
25073        if pending.is_empty() {
25074            self.clear_highlights(HighlightKey::PendingInput, cx);
25075        } else {
25076            self.highlight_text(
25077                HighlightKey::PendingInput,
25078                ranges,
25079                HighlightStyle {
25080                    underline: Some(UnderlineStyle {
25081                        thickness: px(1.),
25082                        color: None,
25083                        wavy: false,
25084                    }),
25085                    ..Default::default()
25086                },
25087                cx,
25088            );
25089        }
25090
25091        self.ime_transaction = self.ime_transaction.or(transaction);
25092        if let Some(transaction) = self.ime_transaction {
25093            self.buffer.update(cx, |buffer, cx| {
25094                buffer.group_until_transaction(transaction, cx);
25095            });
25096        }
25097
25098        if self
25099            .text_highlights(HighlightKey::PendingInput, cx)
25100            .is_none()
25101        {
25102            self.ime_transaction.take();
25103        }
25104    }
25105
25106    pub fn register_action_renderer(
25107        &mut self,
25108        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25109    ) -> Subscription {
25110        let id = self.next_editor_action_id.post_inc();
25111        self.editor_actions
25112            .borrow_mut()
25113            .insert(id, Box::new(listener));
25114
25115        let editor_actions = self.editor_actions.clone();
25116        Subscription::new(move || {
25117            editor_actions.borrow_mut().remove(&id);
25118        })
25119    }
25120
25121    pub fn register_action<A: Action>(
25122        &mut self,
25123        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25124    ) -> Subscription {
25125        let id = self.next_editor_action_id.post_inc();
25126        let listener = Arc::new(listener);
25127        self.editor_actions.borrow_mut().insert(
25128            id,
25129            Box::new(move |_, window, _| {
25130                let listener = listener.clone();
25131                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25132                    let action = action.downcast_ref().unwrap();
25133                    if phase == DispatchPhase::Bubble {
25134                        listener(action, window, cx)
25135                    }
25136                })
25137            }),
25138        );
25139
25140        let editor_actions = self.editor_actions.clone();
25141        Subscription::new(move || {
25142            editor_actions.borrow_mut().remove(&id);
25143        })
25144    }
25145
25146    pub fn file_header_size(&self) -> u32 {
25147        FILE_HEADER_HEIGHT
25148    }
25149
25150    pub fn restore(
25151        &mut self,
25152        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25153        window: &mut Window,
25154        cx: &mut Context<Self>,
25155    ) {
25156        self.buffer().update(cx, |multi_buffer, cx| {
25157            for (buffer_id, changes) in revert_changes {
25158                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25159                    buffer.update(cx, |buffer, cx| {
25160                        buffer.edit(
25161                            changes
25162                                .into_iter()
25163                                .map(|(range, text)| (range, text.to_string())),
25164                            None,
25165                            cx,
25166                        );
25167                    });
25168                }
25169            }
25170        });
25171        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25172            selections.refresh()
25173        });
25174    }
25175
25176    pub fn to_pixel_point(
25177        &mut self,
25178        source: Anchor,
25179        editor_snapshot: &EditorSnapshot,
25180        window: &mut Window,
25181        cx: &mut App,
25182    ) -> Option<gpui::Point<Pixels>> {
25183        let source_point = source.to_display_point(editor_snapshot);
25184        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25185    }
25186
25187    pub fn display_to_pixel_point(
25188        &mut self,
25189        source: DisplayPoint,
25190        editor_snapshot: &EditorSnapshot,
25191        window: &mut Window,
25192        cx: &mut App,
25193    ) -> Option<gpui::Point<Pixels>> {
25194        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25195        let text_layout_details = self.text_layout_details(window, cx);
25196        let scroll_top = text_layout_details
25197            .scroll_anchor
25198            .scroll_position(editor_snapshot)
25199            .y;
25200
25201        if source.row().as_f64() < scroll_top.floor() {
25202            return None;
25203        }
25204        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25205        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25206        Some(gpui::Point::new(source_x, source_y))
25207    }
25208
25209    pub fn has_visible_completions_menu(&self) -> bool {
25210        !self.edit_prediction_preview_is_active()
25211            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25212                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25213            })
25214    }
25215
25216    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25217        if self.mode.is_minimap() {
25218            return;
25219        }
25220        self.addons
25221            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25222    }
25223
25224    pub fn unregister_addon<T: Addon>(&mut self) {
25225        self.addons.remove(&std::any::TypeId::of::<T>());
25226    }
25227
25228    pub fn addon<T: Addon>(&self) -> Option<&T> {
25229        let type_id = std::any::TypeId::of::<T>();
25230        self.addons
25231            .get(&type_id)
25232            .and_then(|item| item.to_any().downcast_ref::<T>())
25233    }
25234
25235    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25236        let type_id = std::any::TypeId::of::<T>();
25237        self.addons
25238            .get_mut(&type_id)
25239            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25240    }
25241
25242    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25243        let text_layout_details = self.text_layout_details(window, cx);
25244        let style = &text_layout_details.editor_style;
25245        let font_id = window.text_system().resolve_font(&style.text.font());
25246        let font_size = style.text.font_size.to_pixels(window.rem_size());
25247        let line_height = style.text.line_height_in_pixels(window.rem_size());
25248        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25249        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25250
25251        CharacterDimensions {
25252            em_width,
25253            em_advance,
25254            line_height,
25255        }
25256    }
25257
25258    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25259        self.load_diff_task.clone()
25260    }
25261
25262    fn read_metadata_from_db(
25263        &mut self,
25264        item_id: u64,
25265        workspace_id: WorkspaceId,
25266        window: &mut Window,
25267        cx: &mut Context<Editor>,
25268    ) {
25269        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25270            && !self.mode.is_minimap()
25271            && WorkspaceSettings::get(None, cx).restore_on_startup
25272                != RestoreOnStartupBehavior::EmptyTab
25273        {
25274            let buffer_snapshot = OnceCell::new();
25275
25276            // Get file path for path-based fold lookup
25277            let file_path: Option<Arc<Path>> =
25278                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25279                    project::File::from_dyn(buffer.read(cx).file())
25280                        .map(|file| Arc::from(file.abs_path(cx)))
25281                });
25282
25283            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25284            let (folds, needs_migration) = if let Some(ref path) = file_path {
25285                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
25286                    && !folds.is_empty()
25287                {
25288                    (Some(folds), false)
25289                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25290                    && !folds.is_empty()
25291                {
25292                    // Found old editor_folds data, will migrate to file_folds
25293                    (Some(folds), true)
25294                } else {
25295                    (None, false)
25296                }
25297            } else {
25298                // No file path, try editor_folds as fallback
25299                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
25300                (folds.filter(|f| !f.is_empty()), false)
25301            };
25302
25303            if let Some(folds) = folds {
25304                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25305                let snapshot_len = snapshot.len().0;
25306
25307                // Helper: search for fingerprint in buffer, return offset if found
25308                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25309                    // Ensure we start at a character boundary (defensive)
25310                    let search_start = snapshot
25311                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25312                        .0;
25313                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25314
25315                    let mut byte_offset = search_start;
25316                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25317                        if byte_offset > search_end {
25318                            break;
25319                        }
25320                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25321                            return Some(byte_offset);
25322                        }
25323                        byte_offset += ch.len_utf8();
25324                    }
25325                    None
25326                };
25327
25328                // Track search position to handle duplicate fingerprints correctly.
25329                // Folds are stored in document order, so we advance after each match.
25330                let mut search_start = 0usize;
25331
25332                // Collect db_folds for migration (only folds with valid fingerprints)
25333                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25334
25335                let valid_folds: Vec<_> = folds
25336                    .into_iter()
25337                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25338                        // Skip folds without fingerprints (old data before migration)
25339                        let sfp = start_fp?;
25340                        let efp = end_fp?;
25341                        let efp_len = efp.len();
25342
25343                        // Fast path: check if fingerprints match at stored offsets
25344                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25345                        let start_matches = stored_start < snapshot_len
25346                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25347                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25348                        let end_matches = efp_check_pos >= stored_start
25349                            && stored_end <= snapshot_len
25350                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25351
25352                        let (new_start, new_end) = if start_matches && end_matches {
25353                            // Offsets unchanged, use stored values
25354                            (stored_start, stored_end)
25355                        } else if sfp == efp {
25356                            // Short fold: identical fingerprints can only match once per search
25357                            // Use stored fold length to compute new_end
25358                            let new_start = find_fingerprint(&sfp, search_start)?;
25359                            let fold_len = stored_end - stored_start;
25360                            let new_end = new_start + fold_len;
25361                            (new_start, new_end)
25362                        } else {
25363                            // Slow path: search for fingerprints in buffer
25364                            let new_start = find_fingerprint(&sfp, search_start)?;
25365                            // Search for end_fp after start, then add efp_len to get actual fold end
25366                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25367                            let new_end = efp_pos + efp_len;
25368                            (new_start, new_end)
25369                        };
25370
25371                        // Advance search position for next fold
25372                        search_start = new_end;
25373
25374                        // Validate fold makes sense (end must be after start)
25375                        if new_end <= new_start {
25376                            return None;
25377                        }
25378
25379                        // Collect for migration if needed
25380                        if needs_migration {
25381                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25382                        }
25383
25384                        Some(
25385                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25386                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25387                        )
25388                    })
25389                    .collect();
25390
25391                if !valid_folds.is_empty() {
25392                    self.fold_ranges(valid_folds, false, window, cx);
25393
25394                    // Migrate from editor_folds to file_folds if we loaded from old table
25395                    if needs_migration {
25396                        if let Some(ref path) = file_path {
25397                            let path = path.clone();
25398                            cx.spawn(async move |_, _| {
25399                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25400                                    .await
25401                                    .log_err();
25402                            })
25403                            .detach();
25404                        }
25405                    }
25406                }
25407            }
25408
25409            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25410                && !selections.is_empty()
25411            {
25412                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25413                // skip adding the initial selection to selection history
25414                self.selection_history.mode = SelectionHistoryMode::Skipping;
25415                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25416                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25417                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25418                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25419                    }));
25420                });
25421                self.selection_history.mode = SelectionHistoryMode::Normal;
25422            };
25423        }
25424
25425        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25426    }
25427
25428    /// Load folds from the file_folds database table by file path.
25429    /// Used when manually opening a file that was previously closed.
25430    fn load_folds_from_db(
25431        &mut self,
25432        workspace_id: WorkspaceId,
25433        file_path: PathBuf,
25434        window: &mut Window,
25435        cx: &mut Context<Editor>,
25436    ) {
25437        if self.mode.is_minimap()
25438            || WorkspaceSettings::get(None, cx).restore_on_startup
25439                == RestoreOnStartupBehavior::EmptyTab
25440        {
25441            return;
25442        }
25443
25444        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25445            return;
25446        };
25447        if folds.is_empty() {
25448            return;
25449        }
25450
25451        let snapshot = self.buffer.read(cx).snapshot(cx);
25452        let snapshot_len = snapshot.len().0;
25453
25454        // Helper: search for fingerprint in buffer, return offset if found
25455        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25456            let search_start = snapshot
25457                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25458                .0;
25459            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25460
25461            let mut byte_offset = search_start;
25462            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25463                if byte_offset > search_end {
25464                    break;
25465                }
25466                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25467                    return Some(byte_offset);
25468                }
25469                byte_offset += ch.len_utf8();
25470            }
25471            None
25472        };
25473
25474        let mut search_start = 0usize;
25475
25476        let valid_folds: Vec<_> = folds
25477            .into_iter()
25478            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25479                let sfp = start_fp?;
25480                let efp = end_fp?;
25481                let efp_len = efp.len();
25482
25483                let start_matches = stored_start < snapshot_len
25484                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25485                let efp_check_pos = stored_end.saturating_sub(efp_len);
25486                let end_matches = efp_check_pos >= stored_start
25487                    && stored_end <= snapshot_len
25488                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25489
25490                let (new_start, new_end) = if start_matches && end_matches {
25491                    (stored_start, stored_end)
25492                } else if sfp == efp {
25493                    let new_start = find_fingerprint(&sfp, search_start)?;
25494                    let fold_len = stored_end - stored_start;
25495                    let new_end = new_start + fold_len;
25496                    (new_start, new_end)
25497                } else {
25498                    let new_start = find_fingerprint(&sfp, search_start)?;
25499                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25500                    let new_end = efp_pos + efp_len;
25501                    (new_start, new_end)
25502                };
25503
25504                search_start = new_end;
25505
25506                if new_end <= new_start {
25507                    return None;
25508                }
25509
25510                Some(
25511                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25512                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25513                )
25514            })
25515            .collect();
25516
25517        if !valid_folds.is_empty() {
25518            self.fold_ranges(valid_folds, false, window, cx);
25519        }
25520    }
25521
25522    fn update_lsp_data(
25523        &mut self,
25524        for_buffer: Option<BufferId>,
25525        window: &mut Window,
25526        cx: &mut Context<'_, Self>,
25527    ) {
25528        if !self.enable_lsp_data {
25529            return;
25530        }
25531
25532        if let Some(buffer_id) = for_buffer {
25533            self.pull_diagnostics(buffer_id, window, cx);
25534        }
25535        self.refresh_semantic_tokens(for_buffer, None, cx);
25536        self.refresh_document_colors(for_buffer, window, cx);
25537        self.refresh_folding_ranges(for_buffer, window, cx);
25538        self.refresh_document_symbols(for_buffer, cx);
25539    }
25540
25541    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25542        if !self.mode().is_full() {
25543            return;
25544        }
25545        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25546            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25547        }
25548    }
25549
25550    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25551        if !self.mode().is_full() {
25552            return;
25553        }
25554
25555        if !self.registered_buffers.contains_key(&buffer_id)
25556            && let Some(project) = self.project.as_ref()
25557        {
25558            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25559                project.update(cx, |project, cx| {
25560                    self.registered_buffers.insert(
25561                        buffer_id,
25562                        project.register_buffer_with_language_servers(&buffer, cx),
25563                    );
25564                });
25565            } else {
25566                self.registered_buffers.remove(&buffer_id);
25567            }
25568        }
25569    }
25570
25571    fn create_style(&self, cx: &App) -> EditorStyle {
25572        let settings = ThemeSettings::get_global(cx);
25573
25574        let mut text_style = match self.mode {
25575            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25576                color: cx.theme().colors().editor_foreground,
25577                font_family: settings.ui_font.family.clone(),
25578                font_features: settings.ui_font.features.clone(),
25579                font_fallbacks: settings.ui_font.fallbacks.clone(),
25580                font_size: rems(0.875).into(),
25581                font_weight: settings.ui_font.weight,
25582                line_height: relative(settings.buffer_line_height.value()),
25583                ..Default::default()
25584            },
25585            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25586                color: cx.theme().colors().editor_foreground,
25587                font_family: settings.buffer_font.family.clone(),
25588                font_features: settings.buffer_font.features.clone(),
25589                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25590                font_size: settings.buffer_font_size(cx).into(),
25591                font_weight: settings.buffer_font.weight,
25592                line_height: relative(settings.buffer_line_height.value()),
25593                ..Default::default()
25594            },
25595        };
25596        if let Some(text_style_refinement) = &self.text_style_refinement {
25597            text_style.refine(text_style_refinement)
25598        }
25599
25600        let background = match self.mode {
25601            EditorMode::SingleLine => cx.theme().system().transparent,
25602            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25603            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25604            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25605        };
25606
25607        EditorStyle {
25608            background,
25609            border: cx.theme().colors().border,
25610            local_player: cx.theme().players().local(),
25611            text: text_style,
25612            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25613            syntax: cx.theme().syntax().clone(),
25614            status: cx.theme().status().clone(),
25615            inlay_hints_style: make_inlay_hints_style(cx),
25616            edit_prediction_styles: make_suggestion_styles(cx),
25617            unnecessary_code_fade: settings.unnecessary_code_fade,
25618            show_underlines: self.diagnostics_enabled(),
25619        }
25620    }
25621
25622    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25623        let multibuffer = self.buffer().read(cx);
25624        let is_singleton = multibuffer.is_singleton();
25625        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25626        let buffer = multibuffer.buffer(*buffer_id)?;
25627
25628        let buffer = buffer.read(cx);
25629        let settings = ThemeSettings::get_global(cx);
25630        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25631        let mut breadcrumbs = if is_singleton {
25632            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25633                buffer
25634                    .snapshot()
25635                    .resolve_file_path(
25636                        self.project
25637                            .as_ref()
25638                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25639                            .unwrap_or_default(),
25640                        cx,
25641                    )
25642                    .unwrap_or_else(|| {
25643                        if multibuffer.is_singleton() {
25644                            multibuffer.title(cx).to_string()
25645                        } else {
25646                            "untitled".to_string()
25647                        }
25648                    })
25649            });
25650            vec![BreadcrumbText {
25651                text,
25652                highlights: None,
25653                font: Some(settings.buffer_font.clone()),
25654            }]
25655        } else {
25656            vec![]
25657        };
25658
25659        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25660            text: symbol.text.clone(),
25661            highlights: Some(symbol.highlight_ranges.clone()),
25662            font: Some(settings.buffer_font.clone()),
25663        }));
25664        Some(breadcrumbs)
25665    }
25666
25667    fn disable_lsp_data(&mut self) {
25668        self.enable_lsp_data = false;
25669    }
25670
25671    fn disable_runnables(&mut self) {
25672        self.enable_runnables = false;
25673    }
25674}
25675
25676fn edit_for_markdown_paste<'a>(
25677    buffer: &MultiBufferSnapshot,
25678    range: Range<MultiBufferOffset>,
25679    to_insert: &'a str,
25680    url: Option<url::Url>,
25681) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25682    if url.is_none() {
25683        return (range, Cow::Borrowed(to_insert));
25684    };
25685
25686    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25687
25688    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25689        Cow::Borrowed(to_insert)
25690    } else {
25691        Cow::Owned(format!("[{old_text}]({to_insert})"))
25692    };
25693    (range, new_text)
25694}
25695
25696fn process_completion_for_edit(
25697    completion: &Completion,
25698    intent: CompletionIntent,
25699    buffer: &Entity<Buffer>,
25700    cursor_position: &text::Anchor,
25701    cx: &mut Context<Editor>,
25702) -> CompletionEdit {
25703    let buffer = buffer.read(cx);
25704    let buffer_snapshot = buffer.snapshot();
25705    let (snippet, new_text) = if completion.is_snippet() {
25706        let mut snippet_source = completion.new_text.clone();
25707        // Workaround for typescript language server issues so that methods don't expand within
25708        // strings and functions with type expressions. The previous point is used because the query
25709        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25710        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25711        let previous_point = if previous_point.column > 0 {
25712            cursor_position.to_previous_offset(&buffer_snapshot)
25713        } else {
25714            cursor_position.to_offset(&buffer_snapshot)
25715        };
25716        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25717            && scope.prefers_label_for_snippet_in_completion()
25718            && let Some(label) = completion.label()
25719            && matches!(
25720                completion.kind(),
25721                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25722            )
25723        {
25724            snippet_source = label;
25725        }
25726        match Snippet::parse(&snippet_source).log_err() {
25727            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25728            None => (None, completion.new_text.clone()),
25729        }
25730    } else {
25731        (None, completion.new_text.clone())
25732    };
25733
25734    let mut range_to_replace = {
25735        let replace_range = &completion.replace_range;
25736        if let CompletionSource::Lsp {
25737            insert_range: Some(insert_range),
25738            ..
25739        } = &completion.source
25740        {
25741            debug_assert_eq!(
25742                insert_range.start, replace_range.start,
25743                "insert_range and replace_range should start at the same position"
25744            );
25745            debug_assert!(
25746                insert_range
25747                    .start
25748                    .cmp(cursor_position, &buffer_snapshot)
25749                    .is_le(),
25750                "insert_range should start before or at cursor position"
25751            );
25752            debug_assert!(
25753                replace_range
25754                    .start
25755                    .cmp(cursor_position, &buffer_snapshot)
25756                    .is_le(),
25757                "replace_range should start before or at cursor position"
25758            );
25759
25760            let should_replace = match intent {
25761                CompletionIntent::CompleteWithInsert => false,
25762                CompletionIntent::CompleteWithReplace => true,
25763                CompletionIntent::Complete | CompletionIntent::Compose => {
25764                    let insert_mode =
25765                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25766                            .completions
25767                            .lsp_insert_mode;
25768                    match insert_mode {
25769                        LspInsertMode::Insert => false,
25770                        LspInsertMode::Replace => true,
25771                        LspInsertMode::ReplaceSubsequence => {
25772                            let mut text_to_replace = buffer.chars_for_range(
25773                                buffer.anchor_before(replace_range.start)
25774                                    ..buffer.anchor_after(replace_range.end),
25775                            );
25776                            let mut current_needle = text_to_replace.next();
25777                            for haystack_ch in completion.label.text.chars() {
25778                                if let Some(needle_ch) = current_needle
25779                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25780                                {
25781                                    current_needle = text_to_replace.next();
25782                                }
25783                            }
25784                            current_needle.is_none()
25785                        }
25786                        LspInsertMode::ReplaceSuffix => {
25787                            if replace_range
25788                                .end
25789                                .cmp(cursor_position, &buffer_snapshot)
25790                                .is_gt()
25791                            {
25792                                let range_after_cursor = *cursor_position..replace_range.end;
25793                                let text_after_cursor = buffer
25794                                    .text_for_range(
25795                                        buffer.anchor_before(range_after_cursor.start)
25796                                            ..buffer.anchor_after(range_after_cursor.end),
25797                                    )
25798                                    .collect::<String>()
25799                                    .to_ascii_lowercase();
25800                                completion
25801                                    .label
25802                                    .text
25803                                    .to_ascii_lowercase()
25804                                    .ends_with(&text_after_cursor)
25805                            } else {
25806                                true
25807                            }
25808                        }
25809                    }
25810                }
25811            };
25812
25813            if should_replace {
25814                replace_range.clone()
25815            } else {
25816                insert_range.clone()
25817            }
25818        } else {
25819            replace_range.clone()
25820        }
25821    };
25822
25823    if range_to_replace
25824        .end
25825        .cmp(cursor_position, &buffer_snapshot)
25826        .is_lt()
25827    {
25828        range_to_replace.end = *cursor_position;
25829    }
25830
25831    let replace_range = range_to_replace.to_offset(buffer);
25832    CompletionEdit {
25833        new_text,
25834        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25835        snippet,
25836    }
25837}
25838
25839struct CompletionEdit {
25840    new_text: String,
25841    replace_range: Range<BufferOffset>,
25842    snippet: Option<Snippet>,
25843}
25844
25845fn comment_delimiter_for_newline(
25846    start_point: &Point,
25847    buffer: &MultiBufferSnapshot,
25848    language: &LanguageScope,
25849) -> Option<Arc<str>> {
25850    let delimiters = language.line_comment_prefixes();
25851    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25852    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25853
25854    let num_of_whitespaces = snapshot
25855        .chars_for_range(range.clone())
25856        .take_while(|c| c.is_whitespace())
25857        .count();
25858    let comment_candidate = snapshot
25859        .chars_for_range(range.clone())
25860        .skip(num_of_whitespaces)
25861        .take(max_len_of_delimiter + 2)
25862        .collect::<String>();
25863    let (delimiter, trimmed_len, is_repl) = delimiters
25864        .iter()
25865        .filter_map(|delimiter| {
25866            let prefix = delimiter.trim_end();
25867            if comment_candidate.starts_with(prefix) {
25868                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25869                {
25870                    stripped_comment.starts_with(" %%")
25871                } else {
25872                    false
25873                };
25874                Some((delimiter, prefix.len(), is_repl))
25875            } else {
25876                None
25877            }
25878        })
25879        .max_by_key(|(_, len, _)| *len)?;
25880
25881    if let Some(BlockCommentConfig {
25882        start: block_start, ..
25883    }) = language.block_comment()
25884    {
25885        let block_start_trimmed = block_start.trim_end();
25886        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25887            let line_content = snapshot
25888                .chars_for_range(range.clone())
25889                .skip(num_of_whitespaces)
25890                .take(block_start_trimmed.len())
25891                .collect::<String>();
25892
25893            if line_content.starts_with(block_start_trimmed) {
25894                return None;
25895            }
25896        }
25897    }
25898
25899    let cursor_is_placed_after_comment_marker =
25900        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25901    if cursor_is_placed_after_comment_marker {
25902        if !is_repl {
25903            return Some(delimiter.clone());
25904        }
25905
25906        let line_content_after_cursor: String = snapshot
25907            .chars_for_range(range)
25908            .skip(start_point.column as usize)
25909            .collect();
25910
25911        if line_content_after_cursor.trim().is_empty() {
25912            return None;
25913        } else {
25914            return Some(delimiter.clone());
25915        }
25916    } else {
25917        None
25918    }
25919}
25920
25921fn documentation_delimiter_for_newline(
25922    start_point: &Point,
25923    buffer: &MultiBufferSnapshot,
25924    language: &LanguageScope,
25925    newline_config: &mut NewlineConfig,
25926) -> Option<Arc<str>> {
25927    let BlockCommentConfig {
25928        start: start_tag,
25929        end: end_tag,
25930        prefix: delimiter,
25931        tab_size: len,
25932    } = language.documentation_comment()?;
25933    let is_within_block_comment = buffer
25934        .language_scope_at(*start_point)
25935        .is_some_and(|scope| scope.override_name() == Some("comment"));
25936    if !is_within_block_comment {
25937        return None;
25938    }
25939
25940    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25941
25942    let num_of_whitespaces = snapshot
25943        .chars_for_range(range.clone())
25944        .take_while(|c| c.is_whitespace())
25945        .count();
25946
25947    // 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.
25948    let column = start_point.column;
25949    let cursor_is_after_start_tag = {
25950        let start_tag_len = start_tag.len();
25951        let start_tag_line = snapshot
25952            .chars_for_range(range.clone())
25953            .skip(num_of_whitespaces)
25954            .take(start_tag_len)
25955            .collect::<String>();
25956        if start_tag_line.starts_with(start_tag.as_ref()) {
25957            num_of_whitespaces + start_tag_len <= column as usize
25958        } else {
25959            false
25960        }
25961    };
25962
25963    let cursor_is_after_delimiter = {
25964        let delimiter_trim = delimiter.trim_end();
25965        let delimiter_line = snapshot
25966            .chars_for_range(range.clone())
25967            .skip(num_of_whitespaces)
25968            .take(delimiter_trim.len())
25969            .collect::<String>();
25970        if delimiter_line.starts_with(delimiter_trim) {
25971            num_of_whitespaces + delimiter_trim.len() <= column as usize
25972        } else {
25973            false
25974        }
25975    };
25976
25977    let mut needs_extra_line = false;
25978    let mut extra_line_additional_indent = IndentSize::spaces(0);
25979
25980    let cursor_is_before_end_tag_if_exists = {
25981        let mut char_position = 0u32;
25982        let mut end_tag_offset = None;
25983
25984        'outer: for chunk in snapshot.text_for_range(range) {
25985            if let Some(byte_pos) = chunk.find(&**end_tag) {
25986                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25987                end_tag_offset = Some(char_position + chars_before_match);
25988                break 'outer;
25989            }
25990            char_position += chunk.chars().count() as u32;
25991        }
25992
25993        if let Some(end_tag_offset) = end_tag_offset {
25994            let cursor_is_before_end_tag = column <= end_tag_offset;
25995            if cursor_is_after_start_tag {
25996                if cursor_is_before_end_tag {
25997                    needs_extra_line = true;
25998                }
25999                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26000                if cursor_is_at_start_of_end_tag {
26001                    extra_line_additional_indent.len = *len;
26002                }
26003            }
26004            cursor_is_before_end_tag
26005        } else {
26006            true
26007        }
26008    };
26009
26010    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26011        && cursor_is_before_end_tag_if_exists
26012    {
26013        let additional_indent = if cursor_is_after_start_tag {
26014            IndentSize::spaces(*len)
26015        } else {
26016            IndentSize::spaces(0)
26017        };
26018
26019        *newline_config = NewlineConfig::Newline {
26020            additional_indent,
26021            extra_line_additional_indent: if needs_extra_line {
26022                Some(extra_line_additional_indent)
26023            } else {
26024                None
26025            },
26026            prevent_auto_indent: true,
26027        };
26028        Some(delimiter.clone())
26029    } else {
26030        None
26031    }
26032}
26033
26034const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26035
26036fn list_delimiter_for_newline(
26037    start_point: &Point,
26038    buffer: &MultiBufferSnapshot,
26039    language: &LanguageScope,
26040    newline_config: &mut NewlineConfig,
26041) -> Option<Arc<str>> {
26042    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26043
26044    let num_of_whitespaces = snapshot
26045        .chars_for_range(range.clone())
26046        .take_while(|c| c.is_whitespace())
26047        .count();
26048
26049    let task_list_entries: Vec<_> = language
26050        .task_list()
26051        .into_iter()
26052        .flat_map(|config| {
26053            config
26054                .prefixes
26055                .iter()
26056                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26057        })
26058        .collect();
26059    let unordered_list_entries: Vec<_> = language
26060        .unordered_list()
26061        .iter()
26062        .map(|marker| (marker.as_ref(), marker.as_ref()))
26063        .collect();
26064
26065    let all_entries: Vec<_> = task_list_entries
26066        .into_iter()
26067        .chain(unordered_list_entries)
26068        .collect();
26069
26070    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26071        let candidate: String = snapshot
26072            .chars_for_range(range.clone())
26073            .skip(num_of_whitespaces)
26074            .take(max_prefix_len)
26075            .collect();
26076
26077        if let Some((prefix, continuation)) = all_entries
26078            .iter()
26079            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26080            .max_by_key(|(prefix, _)| prefix.len())
26081        {
26082            let end_of_prefix = num_of_whitespaces + prefix.len();
26083            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26084            let has_content_after_marker = snapshot
26085                .chars_for_range(range)
26086                .skip(end_of_prefix)
26087                .any(|c| !c.is_whitespace());
26088
26089            if has_content_after_marker && cursor_is_after_prefix {
26090                return Some((*continuation).into());
26091            }
26092
26093            if start_point.column as usize == end_of_prefix {
26094                if num_of_whitespaces == 0 {
26095                    *newline_config = NewlineConfig::ClearCurrentLine;
26096                } else {
26097                    *newline_config = NewlineConfig::UnindentCurrentLine {
26098                        continuation: (*continuation).into(),
26099                    };
26100                }
26101            }
26102
26103            return None;
26104        }
26105    }
26106
26107    let candidate: String = snapshot
26108        .chars_for_range(range.clone())
26109        .skip(num_of_whitespaces)
26110        .take(ORDERED_LIST_MAX_MARKER_LEN)
26111        .collect();
26112
26113    for ordered_config in language.ordered_list() {
26114        let regex = match Regex::new(&ordered_config.pattern) {
26115            Ok(r) => r,
26116            Err(_) => continue,
26117        };
26118
26119        if let Some(captures) = regex.captures(&candidate) {
26120            let full_match = captures.get(0)?;
26121            let marker_len = full_match.len();
26122            let end_of_prefix = num_of_whitespaces + marker_len;
26123            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26124
26125            let has_content_after_marker = snapshot
26126                .chars_for_range(range)
26127                .skip(end_of_prefix)
26128                .any(|c| !c.is_whitespace());
26129
26130            if has_content_after_marker && cursor_is_after_prefix {
26131                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26132                let continuation = ordered_config
26133                    .format
26134                    .replace("{1}", &(number + 1).to_string());
26135                return Some(continuation.into());
26136            }
26137
26138            if start_point.column as usize == end_of_prefix {
26139                let continuation = ordered_config.format.replace("{1}", "1");
26140                if num_of_whitespaces == 0 {
26141                    *newline_config = NewlineConfig::ClearCurrentLine;
26142                } else {
26143                    *newline_config = NewlineConfig::UnindentCurrentLine {
26144                        continuation: continuation.into(),
26145                    };
26146                }
26147            }
26148
26149            return None;
26150        }
26151    }
26152
26153    None
26154}
26155
26156fn is_list_prefix_row(
26157    row: MultiBufferRow,
26158    buffer: &MultiBufferSnapshot,
26159    language: &LanguageScope,
26160) -> bool {
26161    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26162        return false;
26163    };
26164
26165    let num_of_whitespaces = snapshot
26166        .chars_for_range(range.clone())
26167        .take_while(|c| c.is_whitespace())
26168        .count();
26169
26170    let task_list_prefixes: Vec<_> = language
26171        .task_list()
26172        .into_iter()
26173        .flat_map(|config| {
26174            config
26175                .prefixes
26176                .iter()
26177                .map(|p| p.as_ref())
26178                .collect::<Vec<_>>()
26179        })
26180        .collect();
26181    let unordered_list_markers: Vec<_> = language
26182        .unordered_list()
26183        .iter()
26184        .map(|marker| marker.as_ref())
26185        .collect();
26186    let all_prefixes: Vec<_> = task_list_prefixes
26187        .into_iter()
26188        .chain(unordered_list_markers)
26189        .collect();
26190    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26191        let candidate: String = snapshot
26192            .chars_for_range(range.clone())
26193            .skip(num_of_whitespaces)
26194            .take(max_prefix_len)
26195            .collect();
26196        if all_prefixes
26197            .iter()
26198            .any(|prefix| candidate.starts_with(*prefix))
26199        {
26200            return true;
26201        }
26202    }
26203
26204    let ordered_list_candidate: String = snapshot
26205        .chars_for_range(range)
26206        .skip(num_of_whitespaces)
26207        .take(ORDERED_LIST_MAX_MARKER_LEN)
26208        .collect();
26209    for ordered_config in language.ordered_list() {
26210        let regex = match Regex::new(&ordered_config.pattern) {
26211            Ok(r) => r,
26212            Err(_) => continue,
26213        };
26214        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26215            return captures.get(0).is_some();
26216        }
26217    }
26218
26219    false
26220}
26221
26222#[derive(Debug)]
26223enum NewlineConfig {
26224    /// Insert newline with optional additional indent and optional extra blank line
26225    Newline {
26226        additional_indent: IndentSize,
26227        extra_line_additional_indent: Option<IndentSize>,
26228        prevent_auto_indent: bool,
26229    },
26230    /// Clear the current line
26231    ClearCurrentLine,
26232    /// Unindent the current line and add continuation
26233    UnindentCurrentLine { continuation: Arc<str> },
26234}
26235
26236impl NewlineConfig {
26237    fn has_extra_line(&self) -> bool {
26238        matches!(
26239            self,
26240            Self::Newline {
26241                extra_line_additional_indent: Some(_),
26242                ..
26243            }
26244        )
26245    }
26246
26247    fn insert_extra_newline_brackets(
26248        buffer: &MultiBufferSnapshot,
26249        range: Range<MultiBufferOffset>,
26250        language: &language::LanguageScope,
26251    ) -> bool {
26252        let leading_whitespace_len = buffer
26253            .reversed_chars_at(range.start)
26254            .take_while(|c| c.is_whitespace() && *c != '\n')
26255            .map(|c| c.len_utf8())
26256            .sum::<usize>();
26257        let trailing_whitespace_len = buffer
26258            .chars_at(range.end)
26259            .take_while(|c| c.is_whitespace() && *c != '\n')
26260            .map(|c| c.len_utf8())
26261            .sum::<usize>();
26262        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26263
26264        language.brackets().any(|(pair, enabled)| {
26265            let pair_start = pair.start.trim_end();
26266            let pair_end = pair.end.trim_start();
26267
26268            enabled
26269                && pair.newline
26270                && buffer.contains_str_at(range.end, pair_end)
26271                && buffer.contains_str_at(
26272                    range.start.saturating_sub_usize(pair_start.len()),
26273                    pair_start,
26274                )
26275        })
26276    }
26277
26278    fn insert_extra_newline_tree_sitter(
26279        buffer: &MultiBufferSnapshot,
26280        range: Range<MultiBufferOffset>,
26281    ) -> bool {
26282        let (buffer, range) = match buffer
26283            .range_to_buffer_ranges(range.start..=range.end)
26284            .as_slice()
26285        {
26286            [(buffer, range, _)] => (*buffer, range.clone()),
26287            _ => return false,
26288        };
26289        let pair = {
26290            let mut result: Option<BracketMatch<usize>> = None;
26291
26292            for pair in buffer
26293                .all_bracket_ranges(range.start.0..range.end.0)
26294                .filter(move |pair| {
26295                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26296                })
26297            {
26298                let len = pair.close_range.end - pair.open_range.start;
26299
26300                if let Some(existing) = &result {
26301                    let existing_len = existing.close_range.end - existing.open_range.start;
26302                    if len > existing_len {
26303                        continue;
26304                    }
26305                }
26306
26307                result = Some(pair);
26308            }
26309
26310            result
26311        };
26312        let Some(pair) = pair else {
26313            return false;
26314        };
26315        pair.newline_only
26316            && buffer
26317                .chars_for_range(pair.open_range.end..range.start.0)
26318                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26319                .all(|c| c.is_whitespace() && c != '\n')
26320    }
26321}
26322
26323fn update_uncommitted_diff_for_buffer(
26324    editor: Entity<Editor>,
26325    project: &Entity<Project>,
26326    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26327    buffer: Entity<MultiBuffer>,
26328    cx: &mut App,
26329) -> Task<()> {
26330    let mut tasks = Vec::new();
26331    project.update(cx, |project, cx| {
26332        for buffer in buffers {
26333            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26334                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26335            }
26336        }
26337    });
26338    cx.spawn(async move |cx| {
26339        let diffs = future::join_all(tasks).await;
26340        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26341            return;
26342        }
26343
26344        buffer.update(cx, |buffer, cx| {
26345            for diff in diffs.into_iter().flatten() {
26346                buffer.add_diff(diff, cx);
26347            }
26348        });
26349    })
26350}
26351
26352fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26353    let tab_size = tab_size.get() as usize;
26354    let mut width = offset;
26355
26356    for ch in text.chars() {
26357        width += if ch == '\t' {
26358            tab_size - (width % tab_size)
26359        } else {
26360            1
26361        };
26362    }
26363
26364    width - offset
26365}
26366
26367#[cfg(test)]
26368mod tests {
26369    use super::*;
26370
26371    #[test]
26372    fn test_string_size_with_expanded_tabs() {
26373        let nz = |val| NonZeroU32::new(val).unwrap();
26374        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26375        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26376        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26377        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26378        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26379        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26380        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26381        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26382    }
26383}
26384
26385/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26386struct WordBreakingTokenizer<'a> {
26387    input: &'a str,
26388}
26389
26390impl<'a> WordBreakingTokenizer<'a> {
26391    fn new(input: &'a str) -> Self {
26392        Self { input }
26393    }
26394}
26395
26396fn is_char_ideographic(ch: char) -> bool {
26397    use unicode_script::Script::*;
26398    use unicode_script::UnicodeScript;
26399    matches!(ch.script(), Han | Tangut | Yi)
26400}
26401
26402fn is_grapheme_ideographic(text: &str) -> bool {
26403    text.chars().any(is_char_ideographic)
26404}
26405
26406fn is_grapheme_whitespace(text: &str) -> bool {
26407    text.chars().any(|x| x.is_whitespace())
26408}
26409
26410fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26411    text.chars()
26412        .next()
26413        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26414}
26415
26416#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26417enum WordBreakToken<'a> {
26418    Word { token: &'a str, grapheme_len: usize },
26419    InlineWhitespace { token: &'a str, grapheme_len: usize },
26420    Newline,
26421}
26422
26423impl<'a> Iterator for WordBreakingTokenizer<'a> {
26424    /// Yields a span, the count of graphemes in the token, and whether it was
26425    /// whitespace. Note that it also breaks at word boundaries.
26426    type Item = WordBreakToken<'a>;
26427
26428    fn next(&mut self) -> Option<Self::Item> {
26429        use unicode_segmentation::UnicodeSegmentation;
26430        if self.input.is_empty() {
26431            return None;
26432        }
26433
26434        let mut iter = self.input.graphemes(true).peekable();
26435        let mut offset = 0;
26436        let mut grapheme_len = 0;
26437        if let Some(first_grapheme) = iter.next() {
26438            let is_newline = first_grapheme == "\n";
26439            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26440            offset += first_grapheme.len();
26441            grapheme_len += 1;
26442            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26443                if let Some(grapheme) = iter.peek().copied()
26444                    && should_stay_with_preceding_ideograph(grapheme)
26445                {
26446                    offset += grapheme.len();
26447                    grapheme_len += 1;
26448                }
26449            } else {
26450                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26451                let mut next_word_bound = words.peek().copied();
26452                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26453                    next_word_bound = words.next();
26454                }
26455                while let Some(grapheme) = iter.peek().copied() {
26456                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26457                        break;
26458                    };
26459                    if is_grapheme_whitespace(grapheme) != is_whitespace
26460                        || (grapheme == "\n") != is_newline
26461                    {
26462                        break;
26463                    };
26464                    offset += grapheme.len();
26465                    grapheme_len += 1;
26466                    iter.next();
26467                }
26468            }
26469            let token = &self.input[..offset];
26470            self.input = &self.input[offset..];
26471            if token == "\n" {
26472                Some(WordBreakToken::Newline)
26473            } else if is_whitespace {
26474                Some(WordBreakToken::InlineWhitespace {
26475                    token,
26476                    grapheme_len,
26477                })
26478            } else {
26479                Some(WordBreakToken::Word {
26480                    token,
26481                    grapheme_len,
26482                })
26483            }
26484        } else {
26485            None
26486        }
26487    }
26488}
26489
26490#[test]
26491fn test_word_breaking_tokenizer() {
26492    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26493        ("", &[]),
26494        ("  ", &[whitespace("  ", 2)]),
26495        ("Ʒ", &[word("Ʒ", 1)]),
26496        ("Ǽ", &[word("Ǽ", 1)]),
26497        ("", &[word("", 1)]),
26498        ("⋑⋑", &[word("⋑⋑", 2)]),
26499        (
26500            "原理,进而",
26501            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26502        ),
26503        (
26504            "hello world",
26505            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26506        ),
26507        (
26508            "hello, world",
26509            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26510        ),
26511        (
26512            "  hello world",
26513            &[
26514                whitespace("  ", 2),
26515                word("hello", 5),
26516                whitespace(" ", 1),
26517                word("world", 5),
26518            ],
26519        ),
26520        (
26521            "这是什么 \n 钢笔",
26522            &[
26523                word("", 1),
26524                word("", 1),
26525                word("", 1),
26526                word("", 1),
26527                whitespace(" ", 1),
26528                newline(),
26529                whitespace(" ", 1),
26530                word("", 1),
26531                word("", 1),
26532            ],
26533        ),
26534        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26535    ];
26536
26537    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26538        WordBreakToken::Word {
26539            token,
26540            grapheme_len,
26541        }
26542    }
26543
26544    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26545        WordBreakToken::InlineWhitespace {
26546            token,
26547            grapheme_len,
26548        }
26549    }
26550
26551    fn newline() -> WordBreakToken<'static> {
26552        WordBreakToken::Newline
26553    }
26554
26555    for (input, result) in tests {
26556        assert_eq!(
26557            WordBreakingTokenizer::new(input)
26558                .collect::<Vec<_>>()
26559                .as_slice(),
26560            *result,
26561        );
26562    }
26563}
26564
26565fn wrap_with_prefix(
26566    first_line_prefix: String,
26567    subsequent_lines_prefix: String,
26568    unwrapped_text: String,
26569    wrap_column: usize,
26570    tab_size: NonZeroU32,
26571    preserve_existing_whitespace: bool,
26572) -> String {
26573    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26574    let subsequent_lines_prefix_len =
26575        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26576    let mut wrapped_text = String::new();
26577    let mut current_line = first_line_prefix;
26578    let mut is_first_line = true;
26579
26580    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26581    let mut current_line_len = first_line_prefix_len;
26582    let mut in_whitespace = false;
26583    for token in tokenizer {
26584        let have_preceding_whitespace = in_whitespace;
26585        match token {
26586            WordBreakToken::Word {
26587                token,
26588                grapheme_len,
26589            } => {
26590                in_whitespace = false;
26591                let current_prefix_len = if is_first_line {
26592                    first_line_prefix_len
26593                } else {
26594                    subsequent_lines_prefix_len
26595                };
26596                if current_line_len + grapheme_len > wrap_column
26597                    && current_line_len != current_prefix_len
26598                {
26599                    wrapped_text.push_str(current_line.trim_end());
26600                    wrapped_text.push('\n');
26601                    is_first_line = false;
26602                    current_line = subsequent_lines_prefix.clone();
26603                    current_line_len = subsequent_lines_prefix_len;
26604                }
26605                current_line.push_str(token);
26606                current_line_len += grapheme_len;
26607            }
26608            WordBreakToken::InlineWhitespace {
26609                mut token,
26610                mut grapheme_len,
26611            } => {
26612                in_whitespace = true;
26613                if have_preceding_whitespace && !preserve_existing_whitespace {
26614                    continue;
26615                }
26616                if !preserve_existing_whitespace {
26617                    // Keep a single whitespace grapheme as-is
26618                    if let Some(first) =
26619                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26620                    {
26621                        token = first;
26622                    } else {
26623                        token = " ";
26624                    }
26625                    grapheme_len = 1;
26626                }
26627                let current_prefix_len = if is_first_line {
26628                    first_line_prefix_len
26629                } else {
26630                    subsequent_lines_prefix_len
26631                };
26632                if current_line_len + grapheme_len > wrap_column {
26633                    wrapped_text.push_str(current_line.trim_end());
26634                    wrapped_text.push('\n');
26635                    is_first_line = false;
26636                    current_line = subsequent_lines_prefix.clone();
26637                    current_line_len = subsequent_lines_prefix_len;
26638                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26639                    current_line.push_str(token);
26640                    current_line_len += grapheme_len;
26641                }
26642            }
26643            WordBreakToken::Newline => {
26644                in_whitespace = true;
26645                let current_prefix_len = if is_first_line {
26646                    first_line_prefix_len
26647                } else {
26648                    subsequent_lines_prefix_len
26649                };
26650                if preserve_existing_whitespace {
26651                    wrapped_text.push_str(current_line.trim_end());
26652                    wrapped_text.push('\n');
26653                    is_first_line = false;
26654                    current_line = subsequent_lines_prefix.clone();
26655                    current_line_len = subsequent_lines_prefix_len;
26656                } else if have_preceding_whitespace {
26657                    continue;
26658                } else if current_line_len + 1 > wrap_column
26659                    && current_line_len != current_prefix_len
26660                {
26661                    wrapped_text.push_str(current_line.trim_end());
26662                    wrapped_text.push('\n');
26663                    is_first_line = false;
26664                    current_line = subsequent_lines_prefix.clone();
26665                    current_line_len = subsequent_lines_prefix_len;
26666                } else if current_line_len != current_prefix_len {
26667                    current_line.push(' ');
26668                    current_line_len += 1;
26669                }
26670            }
26671        }
26672    }
26673
26674    if !current_line.is_empty() {
26675        wrapped_text.push_str(&current_line);
26676    }
26677    wrapped_text
26678}
26679
26680#[test]
26681fn test_wrap_with_prefix() {
26682    assert_eq!(
26683        wrap_with_prefix(
26684            "# ".to_string(),
26685            "# ".to_string(),
26686            "abcdefg".to_string(),
26687            4,
26688            NonZeroU32::new(4).unwrap(),
26689            false,
26690        ),
26691        "# abcdefg"
26692    );
26693    assert_eq!(
26694        wrap_with_prefix(
26695            "".to_string(),
26696            "".to_string(),
26697            "\thello world".to_string(),
26698            8,
26699            NonZeroU32::new(4).unwrap(),
26700            false,
26701        ),
26702        "hello\nworld"
26703    );
26704    assert_eq!(
26705        wrap_with_prefix(
26706            "// ".to_string(),
26707            "// ".to_string(),
26708            "xx \nyy zz aa bb cc".to_string(),
26709            12,
26710            NonZeroU32::new(4).unwrap(),
26711            false,
26712        ),
26713        "// xx yy zz\n// aa bb cc"
26714    );
26715    assert_eq!(
26716        wrap_with_prefix(
26717            String::new(),
26718            String::new(),
26719            "这是什么 \n 钢笔".to_string(),
26720            3,
26721            NonZeroU32::new(4).unwrap(),
26722            false,
26723        ),
26724        "这是什\n么 钢\n"
26725    );
26726    assert_eq!(
26727        wrap_with_prefix(
26728            String::new(),
26729            String::new(),
26730            format!("foo{}bar", '\u{2009}'), // thin space
26731            80,
26732            NonZeroU32::new(4).unwrap(),
26733            false,
26734        ),
26735        format!("foo{}bar", '\u{2009}')
26736    );
26737}
26738
26739pub trait CollaborationHub {
26740    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26741    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26742    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26743}
26744
26745impl CollaborationHub for Entity<Project> {
26746    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26747        self.read(cx).collaborators()
26748    }
26749
26750    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26751        self.read(cx).user_store().read(cx).participant_indices()
26752    }
26753
26754    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26755        let this = self.read(cx);
26756        let user_ids = this.collaborators().values().map(|c| c.user_id);
26757        this.user_store().read(cx).participant_names(user_ids, cx)
26758    }
26759}
26760
26761pub trait SemanticsProvider {
26762    fn hover(
26763        &self,
26764        buffer: &Entity<Buffer>,
26765        position: text::Anchor,
26766        cx: &mut App,
26767    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26768
26769    fn inline_values(
26770        &self,
26771        buffer_handle: Entity<Buffer>,
26772        range: Range<text::Anchor>,
26773        cx: &mut App,
26774    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26775
26776    fn applicable_inlay_chunks(
26777        &self,
26778        buffer: &Entity<Buffer>,
26779        ranges: &[Range<text::Anchor>],
26780        cx: &mut App,
26781    ) -> Vec<Range<BufferRow>>;
26782
26783    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26784
26785    fn inlay_hints(
26786        &self,
26787        invalidate: InvalidationStrategy,
26788        buffer: Entity<Buffer>,
26789        ranges: Vec<Range<text::Anchor>>,
26790        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26791        cx: &mut App,
26792    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26793
26794    fn semantic_tokens(
26795        &self,
26796        buffer: Entity<Buffer>,
26797        refresh: Option<RefreshForServer>,
26798        cx: &mut App,
26799    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>;
26800
26801    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26802
26803    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26804
26805    fn document_highlights(
26806        &self,
26807        buffer: &Entity<Buffer>,
26808        position: text::Anchor,
26809        cx: &mut App,
26810    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26811
26812    fn definitions(
26813        &self,
26814        buffer: &Entity<Buffer>,
26815        position: text::Anchor,
26816        kind: GotoDefinitionKind,
26817        cx: &mut App,
26818    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26819
26820    fn range_for_rename(
26821        &self,
26822        buffer: &Entity<Buffer>,
26823        position: text::Anchor,
26824        cx: &mut App,
26825    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26826
26827    fn perform_rename(
26828        &self,
26829        buffer: &Entity<Buffer>,
26830        position: text::Anchor,
26831        new_name: String,
26832        cx: &mut App,
26833    ) -> Option<Task<Result<ProjectTransaction>>>;
26834}
26835
26836pub trait CompletionProvider {
26837    fn completions(
26838        &self,
26839        excerpt_id: ExcerptId,
26840        buffer: &Entity<Buffer>,
26841        buffer_position: text::Anchor,
26842        trigger: CompletionContext,
26843        window: &mut Window,
26844        cx: &mut Context<Editor>,
26845    ) -> Task<Result<Vec<CompletionResponse>>>;
26846
26847    fn resolve_completions(
26848        &self,
26849        _buffer: Entity<Buffer>,
26850        _completion_indices: Vec<usize>,
26851        _completions: Rc<RefCell<Box<[Completion]>>>,
26852        _cx: &mut Context<Editor>,
26853    ) -> Task<Result<bool>> {
26854        Task::ready(Ok(false))
26855    }
26856
26857    fn apply_additional_edits_for_completion(
26858        &self,
26859        _buffer: Entity<Buffer>,
26860        _completions: Rc<RefCell<Box<[Completion]>>>,
26861        _completion_index: usize,
26862        _push_to_history: bool,
26863        _cx: &mut Context<Editor>,
26864    ) -> Task<Result<Option<language::Transaction>>> {
26865        Task::ready(Ok(None))
26866    }
26867
26868    fn is_completion_trigger(
26869        &self,
26870        buffer: &Entity<Buffer>,
26871        position: language::Anchor,
26872        text: &str,
26873        trigger_in_words: bool,
26874        cx: &mut Context<Editor>,
26875    ) -> bool;
26876
26877    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26878
26879    fn sort_completions(&self) -> bool {
26880        true
26881    }
26882
26883    fn filter_completions(&self) -> bool {
26884        true
26885    }
26886
26887    fn show_snippets(&self) -> bool {
26888        false
26889    }
26890}
26891
26892pub trait CodeActionProvider {
26893    fn id(&self) -> Arc<str>;
26894
26895    fn code_actions(
26896        &self,
26897        buffer: &Entity<Buffer>,
26898        range: Range<text::Anchor>,
26899        window: &mut Window,
26900        cx: &mut App,
26901    ) -> Task<Result<Vec<CodeAction>>>;
26902
26903    fn apply_code_action(
26904        &self,
26905        buffer_handle: Entity<Buffer>,
26906        action: CodeAction,
26907        excerpt_id: ExcerptId,
26908        push_to_history: bool,
26909        window: &mut Window,
26910        cx: &mut App,
26911    ) -> Task<Result<ProjectTransaction>>;
26912}
26913
26914impl CodeActionProvider for Entity<Project> {
26915    fn id(&self) -> Arc<str> {
26916        "project".into()
26917    }
26918
26919    fn code_actions(
26920        &self,
26921        buffer: &Entity<Buffer>,
26922        range: Range<text::Anchor>,
26923        _window: &mut Window,
26924        cx: &mut App,
26925    ) -> Task<Result<Vec<CodeAction>>> {
26926        self.update(cx, |project, cx| {
26927            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26928            let code_actions = project.code_actions(buffer, range, None, cx);
26929            cx.background_spawn(async move {
26930                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26931                Ok(code_lens_actions
26932                    .context("code lens fetch")?
26933                    .into_iter()
26934                    .flatten()
26935                    .chain(
26936                        code_actions
26937                            .context("code action fetch")?
26938                            .into_iter()
26939                            .flatten(),
26940                    )
26941                    .collect())
26942            })
26943        })
26944    }
26945
26946    fn apply_code_action(
26947        &self,
26948        buffer_handle: Entity<Buffer>,
26949        action: CodeAction,
26950        _excerpt_id: ExcerptId,
26951        push_to_history: bool,
26952        _window: &mut Window,
26953        cx: &mut App,
26954    ) -> Task<Result<ProjectTransaction>> {
26955        self.update(cx, |project, cx| {
26956            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26957        })
26958    }
26959}
26960
26961fn snippet_completions(
26962    project: &Project,
26963    buffer: &Entity<Buffer>,
26964    buffer_anchor: text::Anchor,
26965    classifier: CharClassifier,
26966    cx: &mut App,
26967) -> Task<Result<CompletionResponse>> {
26968    let languages = buffer.read(cx).languages_at(buffer_anchor);
26969    let snippet_store = project.snippets().read(cx);
26970
26971    let scopes: Vec<_> = languages
26972        .iter()
26973        .filter_map(|language| {
26974            let language_name = language.lsp_id();
26975            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26976
26977            if snippets.is_empty() {
26978                None
26979            } else {
26980                Some((language.default_scope(), snippets))
26981            }
26982        })
26983        .collect();
26984
26985    if scopes.is_empty() {
26986        return Task::ready(Ok(CompletionResponse {
26987            completions: vec![],
26988            display_options: CompletionDisplayOptions::default(),
26989            is_incomplete: false,
26990        }));
26991    }
26992
26993    let snapshot = buffer.read(cx).text_snapshot();
26994    let executor = cx.background_executor().clone();
26995
26996    cx.background_spawn(async move {
26997        let is_word_char = |c| classifier.is_word(c);
26998
26999        let mut is_incomplete = false;
27000        let mut completions: Vec<Completion> = Vec::new();
27001
27002        const MAX_PREFIX_LEN: usize = 128;
27003        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27004        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27005        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27006
27007        let max_buffer_window: String = snapshot
27008            .text_for_range(window_start..buffer_offset)
27009            .collect();
27010
27011        if max_buffer_window.is_empty() {
27012            return Ok(CompletionResponse {
27013                completions: vec![],
27014                display_options: CompletionDisplayOptions::default(),
27015                is_incomplete: true,
27016            });
27017        }
27018
27019        for (_scope, snippets) in scopes.into_iter() {
27020            // Sort snippets by word count to match longer snippet prefixes first.
27021            let mut sorted_snippet_candidates = snippets
27022                .iter()
27023                .enumerate()
27024                .flat_map(|(snippet_ix, snippet)| {
27025                    snippet
27026                        .prefix
27027                        .iter()
27028                        .enumerate()
27029                        .map(move |(prefix_ix, prefix)| {
27030                            let word_count =
27031                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27032                            ((snippet_ix, prefix_ix), prefix, word_count)
27033                        })
27034                })
27035                .collect_vec();
27036            sorted_snippet_candidates
27037                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27038
27039            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27040
27041            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27042                .take(
27043                    sorted_snippet_candidates
27044                        .first()
27045                        .map(|(_, _, word_count)| *word_count)
27046                        .unwrap_or_default(),
27047                )
27048                .collect_vec();
27049
27050            const MAX_RESULTS: usize = 100;
27051            // Each match also remembers how many characters from the buffer it consumed
27052            let mut matches: Vec<(StringMatch, usize)> = vec![];
27053
27054            let mut snippet_list_cutoff_index = 0;
27055            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27056                let word_count = buffer_index + 1;
27057                // Increase `snippet_list_cutoff_index` until we have all of the
27058                // snippets with sufficiently many words.
27059                while sorted_snippet_candidates
27060                    .get(snippet_list_cutoff_index)
27061                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27062                        *snippet_word_count >= word_count
27063                    })
27064                {
27065                    snippet_list_cutoff_index += 1;
27066                }
27067
27068                // Take only the candidates with at least `word_count` many words
27069                let snippet_candidates_at_word_len =
27070                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27071
27072                let candidates = snippet_candidates_at_word_len
27073                    .iter()
27074                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27075                    .enumerate() // index in `sorted_snippet_candidates`
27076                    // First char must match
27077                    .filter(|(_ix, prefix)| {
27078                        itertools::equal(
27079                            prefix
27080                                .chars()
27081                                .next()
27082                                .into_iter()
27083                                .flat_map(|c| c.to_lowercase()),
27084                            buffer_window
27085                                .chars()
27086                                .next()
27087                                .into_iter()
27088                                .flat_map(|c| c.to_lowercase()),
27089                        )
27090                    })
27091                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27092                    .collect::<Vec<StringMatchCandidate>>();
27093
27094                matches.extend(
27095                    fuzzy::match_strings(
27096                        &candidates,
27097                        &buffer_window,
27098                        buffer_window.chars().any(|c| c.is_uppercase()),
27099                        true,
27100                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27101                        &Default::default(),
27102                        executor.clone(),
27103                    )
27104                    .await
27105                    .into_iter()
27106                    .map(|string_match| (string_match, buffer_window.len())),
27107                );
27108
27109                if matches.len() >= MAX_RESULTS {
27110                    break;
27111                }
27112            }
27113
27114            let to_lsp = |point: &text::Anchor| {
27115                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27116                point_to_lsp(end)
27117            };
27118            let lsp_end = to_lsp(&buffer_anchor);
27119
27120            if matches.len() >= MAX_RESULTS {
27121                is_incomplete = true;
27122            }
27123
27124            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27125                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27126                    sorted_snippet_candidates[string_match.candidate_id];
27127                let snippet = &snippets[snippet_index];
27128                let start = buffer_offset - buffer_window_len;
27129                let start = snapshot.anchor_before(start);
27130                let range = start..buffer_anchor;
27131                let lsp_start = to_lsp(&start);
27132                let lsp_range = lsp::Range {
27133                    start: lsp_start,
27134                    end: lsp_end,
27135                };
27136                Completion {
27137                    replace_range: range,
27138                    new_text: snippet.body.clone(),
27139                    source: CompletionSource::Lsp {
27140                        insert_range: None,
27141                        server_id: LanguageServerId(usize::MAX),
27142                        resolved: true,
27143                        lsp_completion: Box::new(lsp::CompletionItem {
27144                            label: snippet.prefix.first().unwrap().clone(),
27145                            kind: Some(CompletionItemKind::SNIPPET),
27146                            label_details: snippet.description.as_ref().map(|description| {
27147                                lsp::CompletionItemLabelDetails {
27148                                    detail: Some(description.clone()),
27149                                    description: None,
27150                                }
27151                            }),
27152                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27153                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27154                                lsp::InsertReplaceEdit {
27155                                    new_text: snippet.body.clone(),
27156                                    insert: lsp_range,
27157                                    replace: lsp_range,
27158                                },
27159                            )),
27160                            filter_text: Some(snippet.body.clone()),
27161                            sort_text: Some(char::MAX.to_string()),
27162                            ..lsp::CompletionItem::default()
27163                        }),
27164                        lsp_defaults: None,
27165                    },
27166                    label: CodeLabel {
27167                        text: matching_prefix.clone(),
27168                        runs: Vec::new(),
27169                        filter_range: 0..matching_prefix.len(),
27170                    },
27171                    icon_path: None,
27172                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27173                        single_line: snippet.name.clone().into(),
27174                        plain_text: snippet
27175                            .description
27176                            .clone()
27177                            .map(|description| description.into()),
27178                    }),
27179                    insert_text_mode: None,
27180                    confirm: None,
27181                    match_start: Some(start),
27182                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27183                }
27184            }));
27185        }
27186
27187        Ok(CompletionResponse {
27188            completions,
27189            display_options: CompletionDisplayOptions::default(),
27190            is_incomplete,
27191        })
27192    })
27193}
27194
27195impl CompletionProvider for Entity<Project> {
27196    fn completions(
27197        &self,
27198        _excerpt_id: ExcerptId,
27199        buffer: &Entity<Buffer>,
27200        buffer_position: text::Anchor,
27201        options: CompletionContext,
27202        _window: &mut Window,
27203        cx: &mut Context<Editor>,
27204    ) -> Task<Result<Vec<CompletionResponse>>> {
27205        self.update(cx, |project, cx| {
27206            let task = project.completions(buffer, buffer_position, options, cx);
27207            cx.background_spawn(task)
27208        })
27209    }
27210
27211    fn resolve_completions(
27212        &self,
27213        buffer: Entity<Buffer>,
27214        completion_indices: Vec<usize>,
27215        completions: Rc<RefCell<Box<[Completion]>>>,
27216        cx: &mut Context<Editor>,
27217    ) -> Task<Result<bool>> {
27218        self.update(cx, |project, cx| {
27219            project.lsp_store().update(cx, |lsp_store, cx| {
27220                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27221            })
27222        })
27223    }
27224
27225    fn apply_additional_edits_for_completion(
27226        &self,
27227        buffer: Entity<Buffer>,
27228        completions: Rc<RefCell<Box<[Completion]>>>,
27229        completion_index: usize,
27230        push_to_history: bool,
27231        cx: &mut Context<Editor>,
27232    ) -> Task<Result<Option<language::Transaction>>> {
27233        self.update(cx, |project, cx| {
27234            project.lsp_store().update(cx, |lsp_store, cx| {
27235                lsp_store.apply_additional_edits_for_completion(
27236                    buffer,
27237                    completions,
27238                    completion_index,
27239                    push_to_history,
27240                    cx,
27241                )
27242            })
27243        })
27244    }
27245
27246    fn is_completion_trigger(
27247        &self,
27248        buffer: &Entity<Buffer>,
27249        position: language::Anchor,
27250        text: &str,
27251        trigger_in_words: bool,
27252        cx: &mut Context<Editor>,
27253    ) -> bool {
27254        let mut chars = text.chars();
27255        let char = if let Some(char) = chars.next() {
27256            char
27257        } else {
27258            return false;
27259        };
27260        if chars.next().is_some() {
27261            return false;
27262        }
27263
27264        let buffer = buffer.read(cx);
27265        let snapshot = buffer.snapshot();
27266        let classifier = snapshot
27267            .char_classifier_at(position)
27268            .scope_context(Some(CharScopeContext::Completion));
27269        if trigger_in_words && classifier.is_word(char) {
27270            return true;
27271        }
27272
27273        buffer.completion_triggers().contains(text)
27274    }
27275
27276    fn show_snippets(&self) -> bool {
27277        true
27278    }
27279}
27280
27281impl SemanticsProvider for Entity<Project> {
27282    fn hover(
27283        &self,
27284        buffer: &Entity<Buffer>,
27285        position: text::Anchor,
27286        cx: &mut App,
27287    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27288        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
27289    }
27290
27291    fn document_highlights(
27292        &self,
27293        buffer: &Entity<Buffer>,
27294        position: text::Anchor,
27295        cx: &mut App,
27296    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27297        Some(self.update(cx, |project, cx| {
27298            project.document_highlights(buffer, position, cx)
27299        }))
27300    }
27301
27302    fn definitions(
27303        &self,
27304        buffer: &Entity<Buffer>,
27305        position: text::Anchor,
27306        kind: GotoDefinitionKind,
27307        cx: &mut App,
27308    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27309        Some(self.update(cx, |project, cx| match kind {
27310            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27311            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27312            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27313            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27314        }))
27315    }
27316
27317    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27318        self.update(cx, |project, cx| {
27319            if project
27320                .active_debug_session(cx)
27321                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27322            {
27323                return true;
27324            }
27325
27326            buffer.update(cx, |buffer, cx| {
27327                project.any_language_server_supports_inlay_hints(buffer, cx)
27328            })
27329        })
27330    }
27331
27332    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27333        self.update(cx, |project, cx| {
27334            buffer.update(cx, |buffer, cx| {
27335                project.any_language_server_supports_semantic_tokens(buffer, cx)
27336            })
27337        })
27338    }
27339
27340    fn inline_values(
27341        &self,
27342        buffer_handle: Entity<Buffer>,
27343        range: Range<text::Anchor>,
27344        cx: &mut App,
27345    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27346        self.update(cx, |project, cx| {
27347            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27348
27349            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27350        })
27351    }
27352
27353    fn applicable_inlay_chunks(
27354        &self,
27355        buffer: &Entity<Buffer>,
27356        ranges: &[Range<text::Anchor>],
27357        cx: &mut App,
27358    ) -> Vec<Range<BufferRow>> {
27359        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27360            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27361        })
27362    }
27363
27364    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27365        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
27366            lsp_store.invalidate_inlay_hints(for_buffers)
27367        });
27368    }
27369
27370    fn inlay_hints(
27371        &self,
27372        invalidate: InvalidationStrategy,
27373        buffer: Entity<Buffer>,
27374        ranges: Vec<Range<text::Anchor>>,
27375        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27376        cx: &mut App,
27377    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27378        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27379            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27380        }))
27381    }
27382
27383    fn semantic_tokens(
27384        &self,
27385        buffer: Entity<Buffer>,
27386        refresh: Option<RefreshForServer>,
27387        cx: &mut App,
27388    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>> {
27389        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27390            lsp_store.semantic_tokens(buffer, refresh, cx)
27391        })
27392    }
27393
27394    fn range_for_rename(
27395        &self,
27396        buffer: &Entity<Buffer>,
27397        position: text::Anchor,
27398        cx: &mut App,
27399    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27400        Some(self.update(cx, |project, cx| {
27401            let buffer = buffer.clone();
27402            let task = project.prepare_rename(buffer.clone(), position, cx);
27403            cx.spawn(async move |_, cx| {
27404                Ok(match task.await? {
27405                    PrepareRenameResponse::Success(range) => Some(range),
27406                    PrepareRenameResponse::InvalidPosition => None,
27407                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27408                        // Fallback on using TreeSitter info to determine identifier range
27409                        buffer.read_with(cx, |buffer, _| {
27410                            let snapshot = buffer.snapshot();
27411                            let (range, kind) = snapshot.surrounding_word(position, None);
27412                            if kind != Some(CharKind::Word) {
27413                                return None;
27414                            }
27415                            Some(
27416                                snapshot.anchor_before(range.start)
27417                                    ..snapshot.anchor_after(range.end),
27418                            )
27419                        })
27420                    }
27421                })
27422            })
27423        }))
27424    }
27425
27426    fn perform_rename(
27427        &self,
27428        buffer: &Entity<Buffer>,
27429        position: text::Anchor,
27430        new_name: String,
27431        cx: &mut App,
27432    ) -> Option<Task<Result<ProjectTransaction>>> {
27433        Some(self.update(cx, |project, cx| {
27434            project.perform_rename(buffer.clone(), position, new_name, cx)
27435        }))
27436    }
27437}
27438
27439fn consume_contiguous_rows(
27440    contiguous_row_selections: &mut Vec<Selection<Point>>,
27441    selection: &Selection<Point>,
27442    display_map: &DisplaySnapshot,
27443    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27444) -> (MultiBufferRow, MultiBufferRow) {
27445    contiguous_row_selections.push(selection.clone());
27446    let start_row = starting_row(selection, display_map);
27447    let mut end_row = ending_row(selection, display_map);
27448
27449    while let Some(next_selection) = selections.peek() {
27450        if next_selection.start.row <= end_row.0 {
27451            end_row = ending_row(next_selection, display_map);
27452            contiguous_row_selections.push(selections.next().unwrap().clone());
27453        } else {
27454            break;
27455        }
27456    }
27457    (start_row, end_row)
27458}
27459
27460fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27461    if selection.start.column > 0 {
27462        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27463    } else {
27464        MultiBufferRow(selection.start.row)
27465    }
27466}
27467
27468fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27469    if next_selection.end.column > 0 || next_selection.is_empty() {
27470        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27471    } else {
27472        MultiBufferRow(next_selection.end.row)
27473    }
27474}
27475
27476impl EditorSnapshot {
27477    pub fn remote_selections_in_range<'a>(
27478        &'a self,
27479        range: &'a Range<Anchor>,
27480        collaboration_hub: &dyn CollaborationHub,
27481        cx: &'a App,
27482    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27483        let participant_names = collaboration_hub.user_names(cx);
27484        let participant_indices = collaboration_hub.user_participant_indices(cx);
27485        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27486        let collaborators_by_replica_id = collaborators_by_peer_id
27487            .values()
27488            .map(|collaborator| (collaborator.replica_id, collaborator))
27489            .collect::<HashMap<_, _>>();
27490        self.buffer_snapshot()
27491            .selections_in_range(range, false)
27492            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27493                if replica_id == ReplicaId::AGENT {
27494                    Some(RemoteSelection {
27495                        replica_id,
27496                        selection,
27497                        cursor_shape,
27498                        line_mode,
27499                        collaborator_id: CollaboratorId::Agent,
27500                        user_name: Some("Agent".into()),
27501                        color: cx.theme().players().agent(),
27502                    })
27503                } else {
27504                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27505                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27506                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27507                    Some(RemoteSelection {
27508                        replica_id,
27509                        selection,
27510                        cursor_shape,
27511                        line_mode,
27512                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27513                        user_name,
27514                        color: if let Some(index) = participant_index {
27515                            cx.theme().players().color_for_participant(index.0)
27516                        } else {
27517                            cx.theme().players().absent()
27518                        },
27519                    })
27520                }
27521            })
27522    }
27523
27524    pub fn hunks_for_ranges(
27525        &self,
27526        ranges: impl IntoIterator<Item = Range<Point>>,
27527    ) -> Vec<MultiBufferDiffHunk> {
27528        let mut hunks = Vec::new();
27529        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27530            HashMap::default();
27531        for query_range in ranges {
27532            let query_rows =
27533                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27534            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27535                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27536            ) {
27537                // Include deleted hunks that are adjacent to the query range, because
27538                // otherwise they would be missed.
27539                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27540                if hunk.status().is_deleted() {
27541                    intersects_range |= hunk.row_range.start == query_rows.end;
27542                    intersects_range |= hunk.row_range.end == query_rows.start;
27543                }
27544                if intersects_range {
27545                    if !processed_buffer_rows
27546                        .entry(hunk.buffer_id)
27547                        .or_default()
27548                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27549                    {
27550                        continue;
27551                    }
27552                    hunks.push(hunk);
27553                }
27554            }
27555        }
27556
27557        hunks
27558    }
27559
27560    fn display_diff_hunks_for_rows<'a>(
27561        &'a self,
27562        display_rows: Range<DisplayRow>,
27563        folded_buffers: &'a HashSet<BufferId>,
27564    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27565        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27566        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27567
27568        self.buffer_snapshot()
27569            .diff_hunks_in_range(buffer_start..buffer_end)
27570            .filter_map(|hunk| {
27571                if folded_buffers.contains(&hunk.buffer_id)
27572                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27573                {
27574                    return None;
27575                }
27576
27577                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27578                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27579                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27580                    let line_len = self.buffer_snapshot().line_len(last_row);
27581                    Point::new(last_row.0, line_len)
27582                } else {
27583                    Point::new(hunk.row_range.end.0, 0)
27584                };
27585
27586                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27587                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27588
27589                let display_hunk = if hunk_display_start.column() != 0 {
27590                    DisplayDiffHunk::Folded {
27591                        display_row: hunk_display_start.row(),
27592                    }
27593                } else {
27594                    let mut end_row = hunk_display_end.row();
27595                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27596                        end_row.0 += 1;
27597                    }
27598                    let is_created_file = hunk.is_created_file();
27599
27600                    DisplayDiffHunk::Unfolded {
27601                        status: hunk.status(),
27602                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27603                            ..hunk.diff_base_byte_range.end.0,
27604                        word_diffs: hunk.word_diffs,
27605                        display_row_range: hunk_display_start.row()..end_row,
27606                        multi_buffer_range: Anchor::range_in_buffer(
27607                            hunk.excerpt_id,
27608                            hunk.buffer_range,
27609                        ),
27610                        is_created_file,
27611                    }
27612                };
27613
27614                Some(display_hunk)
27615            })
27616    }
27617
27618    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27619        self.display_snapshot
27620            .buffer_snapshot()
27621            .language_at(position)
27622    }
27623
27624    pub fn is_focused(&self) -> bool {
27625        self.is_focused
27626    }
27627
27628    pub fn placeholder_text(&self) -> Option<String> {
27629        self.placeholder_display_snapshot
27630            .as_ref()
27631            .map(|display_map| display_map.text())
27632    }
27633
27634    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27635        self.scroll_anchor.scroll_position(&self.display_snapshot)
27636    }
27637
27638    pub fn gutter_dimensions(
27639        &self,
27640        font_id: FontId,
27641        font_size: Pixels,
27642        style: &EditorStyle,
27643        window: &mut Window,
27644        cx: &App,
27645    ) -> GutterDimensions {
27646        if self.show_gutter
27647            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27648            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27649        {
27650            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27651                matches!(
27652                    ProjectSettings::get_global(cx).git.git_gutter,
27653                    GitGutterSetting::TrackedFiles
27654                )
27655            });
27656            let gutter_settings = EditorSettings::get_global(cx).gutter;
27657            let show_line_numbers = self
27658                .show_line_numbers
27659                .unwrap_or(gutter_settings.line_numbers);
27660            let line_gutter_width = if show_line_numbers {
27661                // Avoid flicker-like gutter resizes when the line number gains another digit by
27662                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27663                let min_width_for_number_on_gutter =
27664                    ch_advance * gutter_settings.min_line_number_digits as f32;
27665                self.max_line_number_width(style, window)
27666                    .max(min_width_for_number_on_gutter)
27667            } else {
27668                0.0.into()
27669            };
27670
27671            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27672            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27673
27674            let git_blame_entries_width =
27675                self.git_blame_gutter_max_author_length
27676                    .map(|max_author_length| {
27677                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27678                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27679
27680                        /// The number of characters to dedicate to gaps and margins.
27681                        const SPACING_WIDTH: usize = 4;
27682
27683                        let max_char_count = max_author_length.min(renderer.max_author_length())
27684                            + ::git::SHORT_SHA_LENGTH
27685                            + MAX_RELATIVE_TIMESTAMP.len()
27686                            + SPACING_WIDTH;
27687
27688                        ch_advance * max_char_count
27689                    });
27690
27691            let is_singleton = self.buffer_snapshot().is_singleton();
27692
27693            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27694            left_padding += if !is_singleton {
27695                ch_width * 4.0
27696            } else if show_runnables || show_breakpoints {
27697                ch_width * 3.0
27698            } else if show_git_gutter && show_line_numbers {
27699                ch_width * 2.0
27700            } else if show_git_gutter || show_line_numbers {
27701                ch_width
27702            } else {
27703                px(0.)
27704            };
27705
27706            let shows_folds = is_singleton && gutter_settings.folds;
27707
27708            let right_padding = if shows_folds && show_line_numbers {
27709                ch_width * 4.0
27710            } else if shows_folds || (!is_singleton && show_line_numbers) {
27711                ch_width * 3.0
27712            } else if show_line_numbers {
27713                ch_width
27714            } else {
27715                px(0.)
27716            };
27717
27718            GutterDimensions {
27719                left_padding,
27720                right_padding,
27721                width: line_gutter_width + left_padding + right_padding,
27722                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27723                git_blame_entries_width,
27724            }
27725        } else if self.offset_content {
27726            GutterDimensions::default_with_margin(font_id, font_size, cx)
27727        } else {
27728            GutterDimensions::default()
27729        }
27730    }
27731
27732    pub fn render_crease_toggle(
27733        &self,
27734        buffer_row: MultiBufferRow,
27735        row_contains_cursor: bool,
27736        editor: Entity<Editor>,
27737        window: &mut Window,
27738        cx: &mut App,
27739    ) -> Option<AnyElement> {
27740        let folded = self.is_line_folded(buffer_row);
27741        let mut is_foldable = false;
27742
27743        if let Some(crease) = self
27744            .crease_snapshot
27745            .query_row(buffer_row, self.buffer_snapshot())
27746        {
27747            is_foldable = true;
27748            match crease {
27749                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27750                    if let Some(render_toggle) = render_toggle {
27751                        let toggle_callback =
27752                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27753                                if folded {
27754                                    editor.update(cx, |editor, cx| {
27755                                        editor.fold_at(buffer_row, window, cx)
27756                                    });
27757                                } else {
27758                                    editor.update(cx, |editor, cx| {
27759                                        editor.unfold_at(buffer_row, window, cx)
27760                                    });
27761                                }
27762                            });
27763                        return Some((render_toggle)(
27764                            buffer_row,
27765                            folded,
27766                            toggle_callback,
27767                            window,
27768                            cx,
27769                        ));
27770                    }
27771                }
27772            }
27773        }
27774
27775        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27776
27777        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27778            Some(
27779                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27780                    .toggle_state(folded)
27781                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27782                        if folded {
27783                            this.unfold_at(buffer_row, window, cx);
27784                        } else {
27785                            this.fold_at(buffer_row, window, cx);
27786                        }
27787                    }))
27788                    .into_any_element(),
27789            )
27790        } else {
27791            None
27792        }
27793    }
27794
27795    pub fn render_crease_trailer(
27796        &self,
27797        buffer_row: MultiBufferRow,
27798        window: &mut Window,
27799        cx: &mut App,
27800    ) -> Option<AnyElement> {
27801        let folded = self.is_line_folded(buffer_row);
27802        if let Crease::Inline { render_trailer, .. } = self
27803            .crease_snapshot
27804            .query_row(buffer_row, self.buffer_snapshot())?
27805        {
27806            let render_trailer = render_trailer.as_ref()?;
27807            Some(render_trailer(buffer_row, folded, window, cx))
27808        } else {
27809            None
27810        }
27811    }
27812
27813    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27814        let digit_count = self.widest_line_number().ilog10() + 1;
27815        column_pixels(style, digit_count as usize, window)
27816    }
27817
27818    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27819    ///
27820    /// This is positive if `base` is before `line`.
27821    fn relative_line_delta(
27822        &self,
27823        current_selection_head: DisplayRow,
27824        first_visible_row: DisplayRow,
27825        consider_wrapped_lines: bool,
27826    ) -> i64 {
27827        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27828        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27829
27830        if consider_wrapped_lines {
27831            let wrap_snapshot = self.wrap_snapshot();
27832            let base_wrap_row = wrap_snapshot
27833                .make_wrap_point(current_selection_head, Bias::Left)
27834                .row();
27835            let wrap_row = wrap_snapshot
27836                .make_wrap_point(first_visible_row, Bias::Left)
27837                .row();
27838
27839            wrap_row.0 as i64 - base_wrap_row.0 as i64
27840        } else {
27841            let fold_snapshot = self.fold_snapshot();
27842            let base_fold_row = fold_snapshot
27843                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27844                .row();
27845            let fold_row = fold_snapshot
27846                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27847                .row();
27848
27849            fold_row as i64 - base_fold_row as i64
27850        }
27851    }
27852
27853    /// Returns the unsigned relative line number to display for each row in `rows`.
27854    ///
27855    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27856    pub fn calculate_relative_line_numbers(
27857        &self,
27858        rows: &Range<DisplayRow>,
27859        current_selection_head: DisplayRow,
27860        count_wrapped_lines: bool,
27861    ) -> HashMap<DisplayRow, u32> {
27862        let initial_offset =
27863            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27864
27865        self.row_infos(rows.start)
27866            .take(rows.len())
27867            .enumerate()
27868            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27869            .filter(|(_row, row_info)| {
27870                row_info.buffer_row.is_some()
27871                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27872            })
27873            .enumerate()
27874            .filter_map(|(i, (row, row_info))| {
27875                // We want to ensure here that the current line has absolute
27876                // numbering, even if we are in a soft-wrapped line. With the
27877                // exception that if we are in a deleted line, we should number this
27878                // relative with 0, as otherwise it would have no line number at all
27879                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27880
27881                (relative_line_number != 0
27882                    || row_info
27883                        .diff_status
27884                        .is_some_and(|status| status.is_deleted()))
27885                .then_some((row, relative_line_number))
27886            })
27887            .collect()
27888    }
27889}
27890
27891pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27892    let font_size = style.text.font_size.to_pixels(window.rem_size());
27893    let layout = window.text_system().shape_line(
27894        SharedString::from(" ".repeat(column)),
27895        font_size,
27896        &[TextRun {
27897            len: column,
27898            font: style.text.font(),
27899            color: Hsla::default(),
27900            ..Default::default()
27901        }],
27902        None,
27903    );
27904
27905    layout.width
27906}
27907
27908impl Deref for EditorSnapshot {
27909    type Target = DisplaySnapshot;
27910
27911    fn deref(&self) -> &Self::Target {
27912        &self.display_snapshot
27913    }
27914}
27915
27916#[derive(Clone, Debug, PartialEq, Eq)]
27917pub enum EditorEvent {
27918    /// Emitted when the stored review comments change (added, removed, or updated).
27919    ReviewCommentsChanged {
27920        /// The new total count of review comments.
27921        total_count: usize,
27922    },
27923    InputIgnored {
27924        text: Arc<str>,
27925    },
27926    InputHandled {
27927        utf16_range_to_replace: Option<Range<isize>>,
27928        text: Arc<str>,
27929    },
27930    ExcerptsAdded {
27931        buffer: Entity<Buffer>,
27932        predecessor: ExcerptId,
27933        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27934    },
27935    ExcerptsRemoved {
27936        ids: Vec<ExcerptId>,
27937        removed_buffer_ids: Vec<BufferId>,
27938    },
27939    BufferFoldToggled {
27940        ids: Vec<ExcerptId>,
27941        folded: bool,
27942    },
27943    ExcerptsEdited {
27944        ids: Vec<ExcerptId>,
27945    },
27946    ExcerptsExpanded {
27947        ids: Vec<ExcerptId>,
27948    },
27949    ExpandExcerptsRequested {
27950        excerpt_ids: Vec<ExcerptId>,
27951        lines: u32,
27952        direction: ExpandExcerptDirection,
27953    },
27954    StageOrUnstageRequested {
27955        stage: bool,
27956        hunks: Vec<MultiBufferDiffHunk>,
27957    },
27958    OpenExcerptsRequested {
27959        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27960        split: bool,
27961    },
27962    RestoreRequested {
27963        hunks: Vec<MultiBufferDiffHunk>,
27964    },
27965    BufferEdited,
27966    Edited {
27967        transaction_id: clock::Lamport,
27968    },
27969    Reparsed(BufferId),
27970    Focused,
27971    FocusedIn,
27972    Blurred,
27973    DirtyChanged,
27974    Saved,
27975    TitleChanged,
27976    SelectionsChanged {
27977        local: bool,
27978    },
27979    ScrollPositionChanged {
27980        local: bool,
27981        autoscroll: bool,
27982    },
27983    TransactionUndone {
27984        transaction_id: clock::Lamport,
27985    },
27986    TransactionBegun {
27987        transaction_id: clock::Lamport,
27988    },
27989    CursorShapeChanged,
27990    BreadcrumbsChanged,
27991    OutlineSymbolsChanged,
27992    PushedToNavHistory {
27993        anchor: Anchor,
27994        is_deactivate: bool,
27995    },
27996}
27997
27998impl EventEmitter<EditorEvent> for Editor {}
27999
28000impl Focusable for Editor {
28001    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28002        self.focus_handle.clone()
28003    }
28004}
28005
28006impl Render for Editor {
28007    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28008        EditorElement::new(&cx.entity(), self.create_style(cx))
28009    }
28010}
28011
28012impl EntityInputHandler for Editor {
28013    fn text_for_range(
28014        &mut self,
28015        range_utf16: Range<usize>,
28016        adjusted_range: &mut Option<Range<usize>>,
28017        _: &mut Window,
28018        cx: &mut Context<Self>,
28019    ) -> Option<String> {
28020        let snapshot = self.buffer.read(cx).read(cx);
28021        let start = snapshot.clip_offset_utf16(
28022            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28023            Bias::Left,
28024        );
28025        let end = snapshot.clip_offset_utf16(
28026            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28027            Bias::Right,
28028        );
28029        if (start.0.0..end.0.0) != range_utf16 {
28030            adjusted_range.replace(start.0.0..end.0.0);
28031        }
28032        Some(snapshot.text_for_range(start..end).collect())
28033    }
28034
28035    fn selected_text_range(
28036        &mut self,
28037        ignore_disabled_input: bool,
28038        _: &mut Window,
28039        cx: &mut Context<Self>,
28040    ) -> Option<UTF16Selection> {
28041        // Prevent the IME menu from appearing when holding down an alphabetic key
28042        // while input is disabled.
28043        if !ignore_disabled_input && !self.input_enabled {
28044            return None;
28045        }
28046
28047        let selection = self
28048            .selections
28049            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28050        let range = selection.range();
28051
28052        Some(UTF16Selection {
28053            range: range.start.0.0..range.end.0.0,
28054            reversed: selection.reversed,
28055        })
28056    }
28057
28058    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28059        let snapshot = self.buffer.read(cx).read(cx);
28060        let range = self
28061            .text_highlights(HighlightKey::InputComposition, cx)?
28062            .1
28063            .first()?;
28064        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28065    }
28066
28067    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28068        self.clear_highlights(HighlightKey::InputComposition, cx);
28069        self.ime_transaction.take();
28070    }
28071
28072    fn replace_text_in_range(
28073        &mut self,
28074        range_utf16: Option<Range<usize>>,
28075        text: &str,
28076        window: &mut Window,
28077        cx: &mut Context<Self>,
28078    ) {
28079        if !self.input_enabled {
28080            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28081            return;
28082        }
28083
28084        self.transact(window, cx, |this, window, cx| {
28085            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28086                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28087                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28088                Some(this.selection_replacement_ranges(range_utf16, cx))
28089            } else {
28090                this.marked_text_ranges(cx)
28091            };
28092
28093            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28094                let newest_selection_id = this.selections.newest_anchor().id;
28095                this.selections
28096                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28097                    .iter()
28098                    .zip(ranges_to_replace.iter())
28099                    .find_map(|(selection, range)| {
28100                        if selection.id == newest_selection_id {
28101                            Some(
28102                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28103                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28104                            )
28105                        } else {
28106                            None
28107                        }
28108                    })
28109            });
28110
28111            cx.emit(EditorEvent::InputHandled {
28112                utf16_range_to_replace: range_to_replace,
28113                text: text.into(),
28114            });
28115
28116            if let Some(new_selected_ranges) = new_selected_ranges {
28117                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28118                    selections.select_ranges(new_selected_ranges)
28119                });
28120                this.backspace(&Default::default(), window, cx);
28121            }
28122
28123            this.handle_input(text, window, cx);
28124        });
28125
28126        if let Some(transaction) = self.ime_transaction {
28127            self.buffer.update(cx, |buffer, cx| {
28128                buffer.group_until_transaction(transaction, cx);
28129            });
28130        }
28131
28132        self.unmark_text(window, cx);
28133    }
28134
28135    fn replace_and_mark_text_in_range(
28136        &mut self,
28137        range_utf16: Option<Range<usize>>,
28138        text: &str,
28139        new_selected_range_utf16: Option<Range<usize>>,
28140        window: &mut Window,
28141        cx: &mut Context<Self>,
28142    ) {
28143        if !self.input_enabled {
28144            return;
28145        }
28146
28147        let transaction = self.transact(window, cx, |this, window, cx| {
28148            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28149                let snapshot = this.buffer.read(cx).read(cx);
28150                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28151                    for marked_range in &mut marked_ranges {
28152                        marked_range.end = marked_range.start + relative_range_utf16.end;
28153                        marked_range.start += relative_range_utf16.start;
28154                        marked_range.start =
28155                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28156                        marked_range.end =
28157                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28158                    }
28159                }
28160                Some(marked_ranges)
28161            } else if let Some(range_utf16) = range_utf16 {
28162                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28163                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28164                Some(this.selection_replacement_ranges(range_utf16, cx))
28165            } else {
28166                None
28167            };
28168
28169            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28170                let newest_selection_id = this.selections.newest_anchor().id;
28171                this.selections
28172                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28173                    .iter()
28174                    .zip(ranges_to_replace.iter())
28175                    .find_map(|(selection, range)| {
28176                        if selection.id == newest_selection_id {
28177                            Some(
28178                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28179                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28180                            )
28181                        } else {
28182                            None
28183                        }
28184                    })
28185            });
28186
28187            cx.emit(EditorEvent::InputHandled {
28188                utf16_range_to_replace: range_to_replace,
28189                text: text.into(),
28190            });
28191
28192            if let Some(ranges) = ranges_to_replace {
28193                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28194                    s.select_ranges(ranges)
28195                });
28196            }
28197
28198            let marked_ranges = {
28199                let snapshot = this.buffer.read(cx).read(cx);
28200                this.selections
28201                    .disjoint_anchors_arc()
28202                    .iter()
28203                    .map(|selection| {
28204                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28205                    })
28206                    .collect::<Vec<_>>()
28207            };
28208
28209            if text.is_empty() {
28210                this.unmark_text(window, cx);
28211            } else {
28212                this.highlight_text(
28213                    HighlightKey::InputComposition,
28214                    marked_ranges.clone(),
28215                    HighlightStyle {
28216                        underline: Some(UnderlineStyle {
28217                            thickness: px(1.),
28218                            color: None,
28219                            wavy: false,
28220                        }),
28221                        ..Default::default()
28222                    },
28223                    cx,
28224                );
28225            }
28226
28227            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28228            let use_autoclose = this.use_autoclose;
28229            let use_auto_surround = this.use_auto_surround;
28230            this.set_use_autoclose(false);
28231            this.set_use_auto_surround(false);
28232            this.handle_input(text, window, cx);
28233            this.set_use_autoclose(use_autoclose);
28234            this.set_use_auto_surround(use_auto_surround);
28235
28236            if let Some(new_selected_range) = new_selected_range_utf16 {
28237                let snapshot = this.buffer.read(cx).read(cx);
28238                let new_selected_ranges = marked_ranges
28239                    .into_iter()
28240                    .map(|marked_range| {
28241                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28242                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28243                            insertion_start.0 + new_selected_range.start,
28244                        ));
28245                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28246                            insertion_start.0 + new_selected_range.end,
28247                        ));
28248                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28249                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28250                    })
28251                    .collect::<Vec<_>>();
28252
28253                drop(snapshot);
28254                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28255                    selections.select_ranges(new_selected_ranges)
28256                });
28257            }
28258        });
28259
28260        self.ime_transaction = self.ime_transaction.or(transaction);
28261        if let Some(transaction) = self.ime_transaction {
28262            self.buffer.update(cx, |buffer, cx| {
28263                buffer.group_until_transaction(transaction, cx);
28264            });
28265        }
28266
28267        if self
28268            .text_highlights(HighlightKey::InputComposition, cx)
28269            .is_none()
28270        {
28271            self.ime_transaction.take();
28272        }
28273    }
28274
28275    fn bounds_for_range(
28276        &mut self,
28277        range_utf16: Range<usize>,
28278        element_bounds: gpui::Bounds<Pixels>,
28279        window: &mut Window,
28280        cx: &mut Context<Self>,
28281    ) -> Option<gpui::Bounds<Pixels>> {
28282        let text_layout_details = self.text_layout_details(window, cx);
28283        let CharacterDimensions {
28284            em_width,
28285            em_advance,
28286            line_height,
28287        } = self.character_dimensions(window, cx);
28288
28289        let snapshot = self.snapshot(window, cx);
28290        let scroll_position = snapshot.scroll_position();
28291        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28292
28293        let start =
28294            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28295        let x = Pixels::from(
28296            ScrollOffset::from(
28297                snapshot.x_for_display_point(start, &text_layout_details)
28298                    + self.gutter_dimensions.full_width(),
28299            ) - scroll_left,
28300        );
28301        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28302
28303        Some(Bounds {
28304            origin: element_bounds.origin + point(x, y),
28305            size: size(em_width, line_height),
28306        })
28307    }
28308
28309    fn character_index_for_point(
28310        &mut self,
28311        point: gpui::Point<Pixels>,
28312        _window: &mut Window,
28313        _cx: &mut Context<Self>,
28314    ) -> Option<usize> {
28315        let position_map = self.last_position_map.as_ref()?;
28316        if !position_map.text_hitbox.contains(&point) {
28317            return None;
28318        }
28319        let display_point = position_map.point_for_position(point).previous_valid;
28320        let anchor = position_map
28321            .snapshot
28322            .display_point_to_anchor(display_point, Bias::Left);
28323        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28324        Some(utf16_offset.0.0)
28325    }
28326
28327    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28328        self.input_enabled
28329    }
28330}
28331
28332trait SelectionExt {
28333    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28334    fn spanned_rows(
28335        &self,
28336        include_end_if_at_line_start: bool,
28337        map: &DisplaySnapshot,
28338    ) -> Range<MultiBufferRow>;
28339}
28340
28341impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28342    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28343        let start = self
28344            .start
28345            .to_point(map.buffer_snapshot())
28346            .to_display_point(map);
28347        let end = self
28348            .end
28349            .to_point(map.buffer_snapshot())
28350            .to_display_point(map);
28351        if self.reversed {
28352            end..start
28353        } else {
28354            start..end
28355        }
28356    }
28357
28358    fn spanned_rows(
28359        &self,
28360        include_end_if_at_line_start: bool,
28361        map: &DisplaySnapshot,
28362    ) -> Range<MultiBufferRow> {
28363        let start = self.start.to_point(map.buffer_snapshot());
28364        let mut end = self.end.to_point(map.buffer_snapshot());
28365        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28366            end.row -= 1;
28367        }
28368
28369        let buffer_start = map.prev_line_boundary(start).0;
28370        let buffer_end = map.next_line_boundary(end).0;
28371        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28372    }
28373}
28374
28375impl<T: InvalidationRegion> InvalidationStack<T> {
28376    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28377    where
28378        S: Clone + ToOffset,
28379    {
28380        while let Some(region) = self.last() {
28381            let all_selections_inside_invalidation_ranges =
28382                if selections.len() == region.ranges().len() {
28383                    selections
28384                        .iter()
28385                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28386                        .all(|(selection, invalidation_range)| {
28387                            let head = selection.head().to_offset(buffer);
28388                            invalidation_range.start <= head && invalidation_range.end >= head
28389                        })
28390                } else {
28391                    false
28392                };
28393
28394            if all_selections_inside_invalidation_ranges {
28395                break;
28396            } else {
28397                self.pop();
28398            }
28399        }
28400    }
28401}
28402
28403#[derive(Clone)]
28404struct ErasedEditorImpl(Entity<Editor>);
28405
28406impl ui_input::ErasedEditor for ErasedEditorImpl {
28407    fn text(&self, cx: &App) -> String {
28408        self.0.read(cx).text(cx)
28409    }
28410
28411    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28412        self.0.update(cx, |this, cx| {
28413            this.set_text(text, window, cx);
28414        })
28415    }
28416
28417    fn clear(&self, window: &mut Window, cx: &mut App) {
28418        self.0.update(cx, |this, cx| this.clear(window, cx));
28419    }
28420
28421    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28422        self.0.update(cx, |this, cx| {
28423            this.set_placeholder_text(text, window, cx);
28424        });
28425    }
28426
28427    fn focus_handle(&self, cx: &App) -> FocusHandle {
28428        self.0.read(cx).focus_handle(cx)
28429    }
28430
28431    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28432        let settings = ThemeSettings::get_global(cx);
28433        let theme_color = cx.theme().colors();
28434
28435        let text_style = TextStyle {
28436            font_family: settings.ui_font.family.clone(),
28437            font_features: settings.ui_font.features.clone(),
28438            font_size: rems(0.875).into(),
28439            font_weight: settings.ui_font.weight,
28440            font_style: FontStyle::Normal,
28441            line_height: relative(1.2),
28442            color: theme_color.text,
28443            ..Default::default()
28444        };
28445        let editor_style = EditorStyle {
28446            background: theme_color.ghost_element_background,
28447            local_player: cx.theme().players().local(),
28448            syntax: cx.theme().syntax().clone(),
28449            text: text_style,
28450            ..Default::default()
28451        };
28452        EditorElement::new(&self.0, editor_style).into_any()
28453    }
28454
28455    fn as_any(&self) -> &dyn Any {
28456        &self.0
28457    }
28458
28459    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28460        self.0.update(cx, |editor, cx| {
28461            let editor_offset = editor.buffer().read(cx).len(cx);
28462            editor.change_selections(
28463                SelectionEffects::scroll(Autoscroll::Next),
28464                window,
28465                cx,
28466                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28467            );
28468        });
28469    }
28470
28471    fn subscribe(
28472        &self,
28473        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28474        window: &mut Window,
28475        cx: &mut App,
28476    ) -> Subscription {
28477        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28478            let event = match event {
28479                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28480                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28481                _ => return,
28482            };
28483            (callback)(event, window, cx);
28484        })
28485    }
28486
28487    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28488        self.0.update(cx, |editor, cx| {
28489            editor.set_masked(masked, cx);
28490        });
28491    }
28492}
28493impl<T> Default for InvalidationStack<T> {
28494    fn default() -> Self {
28495        Self(Default::default())
28496    }
28497}
28498
28499impl<T> Deref for InvalidationStack<T> {
28500    type Target = Vec<T>;
28501
28502    fn deref(&self) -> &Self::Target {
28503        &self.0
28504    }
28505}
28506
28507impl<T> DerefMut for InvalidationStack<T> {
28508    fn deref_mut(&mut self) -> &mut Self::Target {
28509        &mut self.0
28510    }
28511}
28512
28513impl InvalidationRegion for SnippetState {
28514    fn ranges(&self) -> &[Range<Anchor>] {
28515        &self.ranges[self.active_index]
28516    }
28517}
28518
28519fn edit_prediction_edit_text(
28520    current_snapshot: &BufferSnapshot,
28521    edits: &[(Range<Anchor>, impl AsRef<str>)],
28522    edit_preview: &EditPreview,
28523    include_deletions: bool,
28524    cx: &App,
28525) -> HighlightedText {
28526    let edits = edits
28527        .iter()
28528        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28529        .collect::<Vec<_>>();
28530
28531    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28532}
28533
28534fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28535    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
28536    // Just show the raw edit text with basic styling
28537    let mut text = String::new();
28538    let mut highlights = Vec::new();
28539
28540    let insertion_highlight_style = HighlightStyle {
28541        color: Some(cx.theme().colors().text),
28542        ..Default::default()
28543    };
28544
28545    for (_, edit_text) in edits {
28546        let start_offset = text.len();
28547        text.push_str(edit_text);
28548        let end_offset = text.len();
28549
28550        if start_offset < end_offset {
28551            highlights.push((start_offset..end_offset, insertion_highlight_style));
28552        }
28553    }
28554
28555    HighlightedText {
28556        text: text.into(),
28557        highlights,
28558    }
28559}
28560
28561pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28562    match severity {
28563        lsp::DiagnosticSeverity::ERROR => colors.error,
28564        lsp::DiagnosticSeverity::WARNING => colors.warning,
28565        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28566        lsp::DiagnosticSeverity::HINT => colors.info,
28567        _ => colors.ignored,
28568    }
28569}
28570
28571pub fn styled_runs_for_code_label<'a>(
28572    label: &'a CodeLabel,
28573    syntax_theme: &'a theme::SyntaxTheme,
28574    local_player: &'a theme::PlayerColor,
28575) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28576    let fade_out = HighlightStyle {
28577        fade_out: Some(0.35),
28578        ..Default::default()
28579    };
28580
28581    let mut prev_end = label.filter_range.end;
28582    label
28583        .runs
28584        .iter()
28585        .enumerate()
28586        .flat_map(move |(ix, (range, highlight_id))| {
28587            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28588                HighlightStyle {
28589                    color: Some(local_player.cursor),
28590                    ..Default::default()
28591                }
28592            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28593                HighlightStyle {
28594                    background_color: Some(local_player.selection),
28595                    ..Default::default()
28596                }
28597            } else if let Some(style) = highlight_id.style(syntax_theme) {
28598                style
28599            } else {
28600                return Default::default();
28601            };
28602            let muted_style = style.highlight(fade_out);
28603
28604            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28605            if range.start >= label.filter_range.end {
28606                if range.start > prev_end {
28607                    runs.push((prev_end..range.start, fade_out));
28608                }
28609                runs.push((range.clone(), muted_style));
28610            } else if range.end <= label.filter_range.end {
28611                runs.push((range.clone(), style));
28612            } else {
28613                runs.push((range.start..label.filter_range.end, style));
28614                runs.push((label.filter_range.end..range.end, muted_style));
28615            }
28616            prev_end = cmp::max(prev_end, range.end);
28617
28618            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28619                runs.push((prev_end..label.text.len(), fade_out));
28620            }
28621
28622            runs
28623        })
28624}
28625
28626pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28627    let mut prev_index = 0;
28628    let mut prev_codepoint: Option<char> = None;
28629    text.char_indices()
28630        .chain([(text.len(), '\0')])
28631        .filter_map(move |(index, codepoint)| {
28632            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28633            let is_boundary = index == text.len()
28634                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28635                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28636            if is_boundary {
28637                let chunk = &text[prev_index..index];
28638                prev_index = index;
28639                Some(chunk)
28640            } else {
28641                None
28642            }
28643        })
28644}
28645
28646/// Given a string of text immediately before the cursor, iterates over possible
28647/// strings a snippet could match to. More precisely: returns an iterator over
28648/// suffixes of `text` created by splitting at word boundaries (before & after
28649/// every non-word character).
28650///
28651/// Shorter suffixes are returned first.
28652pub(crate) fn snippet_candidate_suffixes<'a>(
28653    text: &'a str,
28654    is_word_char: &'a dyn Fn(char) -> bool,
28655) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28656    let mut prev_index = text.len();
28657    let mut prev_codepoint = None;
28658    text.char_indices()
28659        .rev()
28660        .chain([(0, '\0')])
28661        .filter_map(move |(index, codepoint)| {
28662            let prev_index = std::mem::replace(&mut prev_index, index);
28663            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28664            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28665                None
28666            } else {
28667                let chunk = &text[prev_index..]; // go to end of string
28668                Some(chunk)
28669            }
28670        })
28671}
28672
28673pub trait RangeToAnchorExt: Sized {
28674    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28675
28676    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28677        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28678        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28679    }
28680}
28681
28682impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28683    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28684        let start_offset = self.start.to_offset(snapshot);
28685        let end_offset = self.end.to_offset(snapshot);
28686        if start_offset == end_offset {
28687            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28688        } else {
28689            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28690        }
28691    }
28692}
28693
28694pub trait RowExt {
28695    fn as_f64(&self) -> f64;
28696
28697    fn next_row(&self) -> Self;
28698
28699    fn previous_row(&self) -> Self;
28700
28701    fn minus(&self, other: Self) -> u32;
28702}
28703
28704impl RowExt for DisplayRow {
28705    fn as_f64(&self) -> f64 {
28706        self.0 as _
28707    }
28708
28709    fn next_row(&self) -> Self {
28710        Self(self.0 + 1)
28711    }
28712
28713    fn previous_row(&self) -> Self {
28714        Self(self.0.saturating_sub(1))
28715    }
28716
28717    fn minus(&self, other: Self) -> u32 {
28718        self.0 - other.0
28719    }
28720}
28721
28722impl RowExt for MultiBufferRow {
28723    fn as_f64(&self) -> f64 {
28724        self.0 as _
28725    }
28726
28727    fn next_row(&self) -> Self {
28728        Self(self.0 + 1)
28729    }
28730
28731    fn previous_row(&self) -> Self {
28732        Self(self.0.saturating_sub(1))
28733    }
28734
28735    fn minus(&self, other: Self) -> u32 {
28736        self.0 - other.0
28737    }
28738}
28739
28740trait RowRangeExt {
28741    type Row;
28742
28743    fn len(&self) -> usize;
28744
28745    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28746}
28747
28748impl RowRangeExt for Range<MultiBufferRow> {
28749    type Row = MultiBufferRow;
28750
28751    fn len(&self) -> usize {
28752        (self.end.0 - self.start.0) as usize
28753    }
28754
28755    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28756        (self.start.0..self.end.0).map(MultiBufferRow)
28757    }
28758}
28759
28760impl RowRangeExt for Range<DisplayRow> {
28761    type Row = DisplayRow;
28762
28763    fn len(&self) -> usize {
28764        (self.end.0 - self.start.0) as usize
28765    }
28766
28767    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28768        (self.start.0..self.end.0).map(DisplayRow)
28769    }
28770}
28771
28772/// If select range has more than one line, we
28773/// just point the cursor to range.start.
28774fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28775    if range.start.row == range.end.row {
28776        range
28777    } else {
28778        range.start..range.start
28779    }
28780}
28781pub struct KillRing(ClipboardItem);
28782impl Global for KillRing {}
28783
28784const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28785
28786enum BreakpointPromptEditAction {
28787    Log,
28788    Condition,
28789    HitCondition,
28790}
28791
28792struct BreakpointPromptEditor {
28793    pub(crate) prompt: Entity<Editor>,
28794    editor: WeakEntity<Editor>,
28795    breakpoint_anchor: Anchor,
28796    breakpoint: Breakpoint,
28797    edit_action: BreakpointPromptEditAction,
28798    block_ids: HashSet<CustomBlockId>,
28799    editor_margins: Arc<Mutex<EditorMargins>>,
28800    _subscriptions: Vec<Subscription>,
28801}
28802
28803impl BreakpointPromptEditor {
28804    const MAX_LINES: u8 = 4;
28805
28806    fn new(
28807        editor: WeakEntity<Editor>,
28808        breakpoint_anchor: Anchor,
28809        breakpoint: Breakpoint,
28810        edit_action: BreakpointPromptEditAction,
28811        window: &mut Window,
28812        cx: &mut Context<Self>,
28813    ) -> Self {
28814        let base_text = match edit_action {
28815            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28816            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28817            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28818        }
28819        .map(|msg| msg.to_string())
28820        .unwrap_or_default();
28821
28822        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28823        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28824
28825        let prompt = cx.new(|cx| {
28826            let mut prompt = Editor::new(
28827                EditorMode::AutoHeight {
28828                    min_lines: 1,
28829                    max_lines: Some(Self::MAX_LINES as usize),
28830                },
28831                buffer,
28832                None,
28833                window,
28834                cx,
28835            );
28836            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28837            prompt.set_show_cursor_when_unfocused(false, cx);
28838            prompt.set_placeholder_text(
28839                match edit_action {
28840                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28841                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28842                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28843                },
28844                window,
28845                cx,
28846            );
28847
28848            prompt
28849        });
28850
28851        Self {
28852            prompt,
28853            editor,
28854            breakpoint_anchor,
28855            breakpoint,
28856            edit_action,
28857            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28858            block_ids: Default::default(),
28859            _subscriptions: vec![],
28860        }
28861    }
28862
28863    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28864        self.block_ids.extend(block_ids)
28865    }
28866
28867    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28868        if let Some(editor) = self.editor.upgrade() {
28869            let message = self
28870                .prompt
28871                .read(cx)
28872                .buffer
28873                .read(cx)
28874                .as_singleton()
28875                .expect("A multi buffer in breakpoint prompt isn't possible")
28876                .read(cx)
28877                .as_rope()
28878                .to_string();
28879
28880            editor.update(cx, |editor, cx| {
28881                editor.edit_breakpoint_at_anchor(
28882                    self.breakpoint_anchor,
28883                    self.breakpoint.clone(),
28884                    match self.edit_action {
28885                        BreakpointPromptEditAction::Log => {
28886                            BreakpointEditAction::EditLogMessage(message.into())
28887                        }
28888                        BreakpointPromptEditAction::Condition => {
28889                            BreakpointEditAction::EditCondition(message.into())
28890                        }
28891                        BreakpointPromptEditAction::HitCondition => {
28892                            BreakpointEditAction::EditHitCondition(message.into())
28893                        }
28894                    },
28895                    cx,
28896                );
28897
28898                editor.remove_blocks(self.block_ids.clone(), None, cx);
28899                cx.focus_self(window);
28900            });
28901        }
28902    }
28903
28904    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28905        self.editor
28906            .update(cx, |editor, cx| {
28907                editor.remove_blocks(self.block_ids.clone(), None, cx);
28908                window.focus(&editor.focus_handle, cx);
28909            })
28910            .log_err();
28911    }
28912
28913    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28914        let settings = ThemeSettings::get_global(cx);
28915        let text_style = TextStyle {
28916            color: if self.prompt.read(cx).read_only(cx) {
28917                cx.theme().colors().text_disabled
28918            } else {
28919                cx.theme().colors().text
28920            },
28921            font_family: settings.buffer_font.family.clone(),
28922            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28923            font_size: settings.buffer_font_size(cx).into(),
28924            font_weight: settings.buffer_font.weight,
28925            line_height: relative(settings.buffer_line_height.value()),
28926            ..Default::default()
28927        };
28928        EditorElement::new(
28929            &self.prompt,
28930            EditorStyle {
28931                background: cx.theme().colors().editor_background,
28932                local_player: cx.theme().players().local(),
28933                text: text_style,
28934                ..Default::default()
28935            },
28936        )
28937    }
28938}
28939
28940impl Render for BreakpointPromptEditor {
28941    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28942        let editor_margins = *self.editor_margins.lock();
28943        let gutter_dimensions = editor_margins.gutter;
28944        h_flex()
28945            .key_context("Editor")
28946            .bg(cx.theme().colors().editor_background)
28947            .border_y_1()
28948            .border_color(cx.theme().status().info_border)
28949            .size_full()
28950            .py(window.line_height() / 2.5)
28951            .on_action(cx.listener(Self::confirm))
28952            .on_action(cx.listener(Self::cancel))
28953            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28954            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28955    }
28956}
28957
28958impl Focusable for BreakpointPromptEditor {
28959    fn focus_handle(&self, cx: &App) -> FocusHandle {
28960        self.prompt.focus_handle(cx)
28961    }
28962}
28963
28964fn all_edits_insertions_or_deletions(
28965    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28966    snapshot: &MultiBufferSnapshot,
28967) -> bool {
28968    let mut all_insertions = true;
28969    let mut all_deletions = true;
28970
28971    for (range, new_text) in edits.iter() {
28972        let range_is_empty = range.to_offset(snapshot).is_empty();
28973        let text_is_empty = new_text.is_empty();
28974
28975        if range_is_empty != text_is_empty {
28976            if range_is_empty {
28977                all_deletions = false;
28978            } else {
28979                all_insertions = false;
28980            }
28981        } else {
28982            return false;
28983        }
28984
28985        if !all_insertions && !all_deletions {
28986            return false;
28987        }
28988    }
28989    all_insertions || all_deletions
28990}
28991
28992struct MissingEditPredictionKeybindingTooltip;
28993
28994impl Render for MissingEditPredictionKeybindingTooltip {
28995    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28996        ui::tooltip_container(cx, |container, cx| {
28997            container
28998                .flex_shrink_0()
28999                .max_w_80()
29000                .min_h(rems_from_px(124.))
29001                .justify_between()
29002                .child(
29003                    v_flex()
29004                        .flex_1()
29005                        .text_ui_sm(cx)
29006                        .child(Label::new("Conflict with Accept Keybinding"))
29007                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29008                )
29009                .child(
29010                    h_flex()
29011                        .pb_1()
29012                        .gap_1()
29013                        .items_end()
29014                        .w_full()
29015                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29016                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29017                        }))
29018                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29019                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29020                        })),
29021                )
29022        })
29023    }
29024}
29025
29026#[derive(Debug, Clone, Copy, PartialEq)]
29027pub struct LineHighlight {
29028    pub background: Background,
29029    pub border: Option<gpui::Hsla>,
29030    pub include_gutter: bool,
29031    pub type_id: Option<TypeId>,
29032}
29033
29034struct LineManipulationResult {
29035    pub new_text: String,
29036    pub line_count_before: usize,
29037    pub line_count_after: usize,
29038}
29039
29040fn render_diff_hunk_controls(
29041    row: u32,
29042    status: &DiffHunkStatus,
29043    hunk_range: Range<Anchor>,
29044    is_created_file: bool,
29045    line_height: Pixels,
29046    editor: &Entity<Editor>,
29047    _window: &mut Window,
29048    cx: &mut App,
29049) -> AnyElement {
29050    h_flex()
29051        .h(line_height)
29052        .mr_1()
29053        .gap_1()
29054        .px_0p5()
29055        .pb_1()
29056        .border_x_1()
29057        .border_b_1()
29058        .border_color(cx.theme().colors().border_variant)
29059        .rounded_b_lg()
29060        .bg(cx.theme().colors().editor_background)
29061        .gap_1()
29062        .block_mouse_except_scroll()
29063        .shadow_md()
29064        .child(if status.has_secondary_hunk() {
29065            Button::new(("stage", row as u64), "Stage")
29066                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29067                .tooltip({
29068                    let focus_handle = editor.focus_handle(cx);
29069                    move |_window, cx| {
29070                        Tooltip::for_action_in(
29071                            "Stage Hunk",
29072                            &::git::ToggleStaged,
29073                            &focus_handle,
29074                            cx,
29075                        )
29076                    }
29077                })
29078                .on_click({
29079                    let editor = editor.clone();
29080                    move |_event, _window, cx| {
29081                        editor.update(cx, |editor, cx| {
29082                            editor.stage_or_unstage_diff_hunks(
29083                                true,
29084                                vec![hunk_range.start..hunk_range.start],
29085                                cx,
29086                            );
29087                        });
29088                    }
29089                })
29090        } else {
29091            Button::new(("unstage", row as u64), "Unstage")
29092                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29093                .tooltip({
29094                    let focus_handle = editor.focus_handle(cx);
29095                    move |_window, cx| {
29096                        Tooltip::for_action_in(
29097                            "Unstage Hunk",
29098                            &::git::ToggleStaged,
29099                            &focus_handle,
29100                            cx,
29101                        )
29102                    }
29103                })
29104                .on_click({
29105                    let editor = editor.clone();
29106                    move |_event, _window, cx| {
29107                        editor.update(cx, |editor, cx| {
29108                            editor.stage_or_unstage_diff_hunks(
29109                                false,
29110                                vec![hunk_range.start..hunk_range.start],
29111                                cx,
29112                            );
29113                        });
29114                    }
29115                })
29116        })
29117        .child(
29118            Button::new(("restore", row as u64), "Restore")
29119                .tooltip({
29120                    let focus_handle = editor.focus_handle(cx);
29121                    move |_window, cx| {
29122                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29123                    }
29124                })
29125                .on_click({
29126                    let editor = editor.clone();
29127                    move |_event, window, cx| {
29128                        editor.update(cx, |editor, cx| {
29129                            let snapshot = editor.snapshot(window, cx);
29130                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29131                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29132                        });
29133                    }
29134                })
29135                .disabled(is_created_file),
29136        )
29137        .when(
29138            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29139            |el| {
29140                el.child(
29141                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29142                        .shape(IconButtonShape::Square)
29143                        .icon_size(IconSize::Small)
29144                        // .disabled(!has_multiple_hunks)
29145                        .tooltip({
29146                            let focus_handle = editor.focus_handle(cx);
29147                            move |_window, cx| {
29148                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29149                            }
29150                        })
29151                        .on_click({
29152                            let editor = editor.clone();
29153                            move |_event, window, cx| {
29154                                editor.update(cx, |editor, cx| {
29155                                    let snapshot = editor.snapshot(window, cx);
29156                                    let position =
29157                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29158                                    editor.go_to_hunk_before_or_after_position(
29159                                        &snapshot,
29160                                        position,
29161                                        Direction::Next,
29162                                        window,
29163                                        cx,
29164                                    );
29165                                    editor.expand_selected_diff_hunks(cx);
29166                                });
29167                            }
29168                        }),
29169                )
29170                .child(
29171                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29172                        .shape(IconButtonShape::Square)
29173                        .icon_size(IconSize::Small)
29174                        // .disabled(!has_multiple_hunks)
29175                        .tooltip({
29176                            let focus_handle = editor.focus_handle(cx);
29177                            move |_window, cx| {
29178                                Tooltip::for_action_in(
29179                                    "Previous Hunk",
29180                                    &GoToPreviousHunk,
29181                                    &focus_handle,
29182                                    cx,
29183                                )
29184                            }
29185                        })
29186                        .on_click({
29187                            let editor = editor.clone();
29188                            move |_event, window, cx| {
29189                                editor.update(cx, |editor, cx| {
29190                                    let snapshot = editor.snapshot(window, cx);
29191                                    let point =
29192                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29193                                    editor.go_to_hunk_before_or_after_position(
29194                                        &snapshot,
29195                                        point,
29196                                        Direction::Prev,
29197                                        window,
29198                                        cx,
29199                                    );
29200                                    editor.expand_selected_diff_hunks(cx);
29201                                });
29202                            }
29203                        }),
29204                )
29205            },
29206        )
29207        .into_any_element()
29208}
29209
29210pub fn multibuffer_context_lines(cx: &App) -> u32 {
29211    EditorSettings::try_get(cx)
29212        .map(|settings| settings.excerpt_context_lines)
29213        .unwrap_or(2)
29214        .min(32)
29215}