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    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224use zed_actions::editor::{MoveDown, MoveUp};
  225
  226use crate::{
  227    code_context_menus::CompletionsMenuSource,
  228    editor_settings::MultiCursorModifier,
  229    hover_links::{find_url, find_url_from_range},
  230    inlays::{
  231        InlineValueCache,
  232        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  233    },
  234    scroll::{ScrollOffset, ScrollPixelOffset},
  235    selections_collection::resolve_selections_wrapping_blocks,
  236    semantic_tokens::SemanticTokenState,
  237    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  238};
  239
  240pub const FILE_HEADER_HEIGHT: u32 = 2;
  241pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  242pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  243const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  244const MAX_LINE_LEN: usize = 1024;
  245const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  246const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  247pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  248#[doc(hidden)]
  249pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  250pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  251
  252pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  253pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  254pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  255pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  256
  257pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  258pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  259pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  260
  261pub type RenderDiffHunkControlsFn = Arc<
  262    dyn Fn(
  263        u32,
  264        &DiffHunkStatus,
  265        Range<Anchor>,
  266        bool,
  267        Pixels,
  268        &Entity<Editor>,
  269        &mut Window,
  270        &mut App,
  271    ) -> AnyElement,
  272>;
  273
  274enum ReportEditorEvent {
  275    Saved { auto_saved: bool },
  276    EditorOpened,
  277    Closed,
  278}
  279
  280impl ReportEditorEvent {
  281    pub fn event_type(&self) -> &'static str {
  282        match self {
  283            Self::Saved { .. } => "Editor Saved",
  284            Self::EditorOpened => "Editor Opened",
  285            Self::Closed => "Editor Closed",
  286        }
  287    }
  288}
  289
  290pub enum ActiveDebugLine {}
  291pub enum DebugStackFrameLine {}
  292
  293pub enum ConflictsOuter {}
  294pub enum ConflictsOurs {}
  295pub enum ConflictsTheirs {}
  296pub enum ConflictsOursMarker {}
  297pub enum ConflictsTheirsMarker {}
  298
  299pub struct HunkAddedColor;
  300pub struct HunkRemovedColor;
  301
  302#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  303pub enum Navigated {
  304    Yes,
  305    No,
  306}
  307
  308impl Navigated {
  309    pub fn from_bool(yes: bool) -> Navigated {
  310        if yes { Navigated::Yes } else { Navigated::No }
  311    }
  312}
  313
  314#[derive(Debug, Clone, PartialEq, Eq)]
  315enum DisplayDiffHunk {
  316    Folded {
  317        display_row: DisplayRow,
  318    },
  319    Unfolded {
  320        is_created_file: bool,
  321        diff_base_byte_range: Range<usize>,
  322        display_row_range: Range<DisplayRow>,
  323        multi_buffer_range: Range<Anchor>,
  324        status: DiffHunkStatus,
  325        word_diffs: Vec<Range<MultiBufferOffset>>,
  326    },
  327}
  328
  329pub enum HideMouseCursorOrigin {
  330    TypingAction,
  331    MovementAction,
  332}
  333
  334pub fn init(cx: &mut App) {
  335    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  336    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  337
  338    workspace::register_project_item::<Editor>(cx);
  339    workspace::FollowableViewRegistry::register::<Editor>(cx);
  340    workspace::register_serializable_item::<Editor>(cx);
  341
  342    cx.observe_new(
  343        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  344            workspace.register_action(Editor::new_file);
  345            workspace.register_action(Editor::new_file_split);
  346            workspace.register_action(Editor::new_file_vertical);
  347            workspace.register_action(Editor::new_file_horizontal);
  348            workspace.register_action(Editor::cancel_language_server_work);
  349            workspace.register_action(Editor::toggle_focus);
  350        },
  351    )
  352    .detach();
  353
  354    cx.on_action(move |_: &workspace::NewFile, cx| {
  355        let app_state = workspace::AppState::global(cx);
  356        if let Some(app_state) = app_state.upgrade() {
  357            workspace::open_new(
  358                Default::default(),
  359                app_state,
  360                cx,
  361                |workspace, window, cx| {
  362                    Editor::new_file(workspace, &Default::default(), window, cx)
  363                },
  364            )
  365            .detach_and_log_err(cx);
  366        }
  367    })
  368    .on_action(move |_: &workspace::NewWindow, cx| {
  369        let app_state = workspace::AppState::global(cx);
  370        if let Some(app_state) = app_state.upgrade() {
  371            workspace::open_new(
  372                Default::default(),
  373                app_state,
  374                cx,
  375                |workspace, window, cx| {
  376                    cx.activate(true);
  377                    Editor::new_file(workspace, &Default::default(), window, cx)
  378                },
  379            )
  380            .detach_and_log_err(cx);
  381        }
  382    });
  383    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  384        Arc::new(ErasedEditorImpl(
  385            cx.new(|cx| Editor::single_line(window, cx)),
  386        )) as Arc<dyn ErasedEditor>
  387    });
  388    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  389}
  390
  391pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  392    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  393}
  394
  395pub trait DiagnosticRenderer {
  396    fn render_group(
  397        &self,
  398        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  399        buffer_id: BufferId,
  400        snapshot: EditorSnapshot,
  401        editor: WeakEntity<Editor>,
  402        language_registry: Option<Arc<LanguageRegistry>>,
  403        cx: &mut App,
  404    ) -> Vec<BlockProperties<Anchor>>;
  405
  406    fn render_hover(
  407        &self,
  408        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  409        range: Range<Point>,
  410        buffer_id: BufferId,
  411        language_registry: Option<Arc<LanguageRegistry>>,
  412        cx: &mut App,
  413    ) -> Option<Entity<markdown::Markdown>>;
  414
  415    fn open_link(
  416        &self,
  417        editor: &mut Editor,
  418        link: SharedString,
  419        window: &mut Window,
  420        cx: &mut Context<Editor>,
  421    );
  422}
  423
  424pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  425
  426impl GlobalDiagnosticRenderer {
  427    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  428        cx.try_global::<Self>().map(|g| g.0.clone())
  429    }
  430}
  431
  432impl gpui::Global for GlobalDiagnosticRenderer {}
  433pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  434    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  435}
  436
  437pub struct SearchWithinRange;
  438
  439trait InvalidationRegion {
  440    fn ranges(&self) -> &[Range<Anchor>];
  441}
  442
  443#[derive(Clone, Debug, PartialEq)]
  444pub enum SelectPhase {
  445    Begin {
  446        position: DisplayPoint,
  447        add: bool,
  448        click_count: usize,
  449    },
  450    BeginColumnar {
  451        position: DisplayPoint,
  452        reset: bool,
  453        mode: ColumnarMode,
  454        goal_column: u32,
  455    },
  456    Extend {
  457        position: DisplayPoint,
  458        click_count: usize,
  459    },
  460    Update {
  461        position: DisplayPoint,
  462        goal_column: u32,
  463        scroll_delta: gpui::Point<f32>,
  464    },
  465    End,
  466}
  467
  468#[derive(Clone, Debug, PartialEq)]
  469pub enum ColumnarMode {
  470    FromMouse,
  471    FromSelection,
  472}
  473
  474#[derive(Clone, Debug)]
  475pub enum SelectMode {
  476    Character,
  477    Word(Range<Anchor>),
  478    Line(Range<Anchor>),
  479    All,
  480}
  481
  482#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  483pub enum SizingBehavior {
  484    /// The editor will layout itself using `size_full` and will include the vertical
  485    /// scroll margin as requested by user settings.
  486    #[default]
  487    Default,
  488    /// The editor will layout itself using `size_full`, but will not have any
  489    /// vertical overscroll.
  490    ExcludeOverscrollMargin,
  491    /// The editor will request a vertical size according to its content and will be
  492    /// layouted without a vertical scroll margin.
  493    SizeByContent,
  494}
  495
  496#[derive(Clone, PartialEq, Eq, Debug)]
  497pub enum EditorMode {
  498    SingleLine,
  499    AutoHeight {
  500        min_lines: usize,
  501        max_lines: Option<usize>,
  502    },
  503    Full {
  504        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  505        scale_ui_elements_with_buffer_font_size: bool,
  506        /// When set to `true`, the editor will render a background for the active line.
  507        show_active_line_background: bool,
  508        /// Determines the sizing behavior for this editor
  509        sizing_behavior: SizingBehavior,
  510    },
  511    Minimap {
  512        parent: WeakEntity<Editor>,
  513    },
  514}
  515
  516impl EditorMode {
  517    pub fn full() -> Self {
  518        Self::Full {
  519            scale_ui_elements_with_buffer_font_size: true,
  520            show_active_line_background: true,
  521            sizing_behavior: SizingBehavior::Default,
  522        }
  523    }
  524
  525    #[inline]
  526    pub fn is_full(&self) -> bool {
  527        matches!(self, Self::Full { .. })
  528    }
  529
  530    #[inline]
  531    pub fn is_single_line(&self) -> bool {
  532        matches!(self, Self::SingleLine { .. })
  533    }
  534
  535    #[inline]
  536    fn is_minimap(&self) -> bool {
  537        matches!(self, Self::Minimap { .. })
  538    }
  539}
  540
  541#[derive(Copy, Clone, Debug)]
  542pub enum SoftWrap {
  543    /// Prefer not to wrap at all.
  544    ///
  545    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  546    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  547    GitDiff,
  548    /// Prefer a single line generally, unless an overly long line is encountered.
  549    None,
  550    /// Soft wrap lines that exceed the editor width.
  551    EditorWidth,
  552    /// Soft wrap lines at the preferred line length.
  553    Column(u32),
  554    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  555    Bounded(u32),
  556}
  557
  558#[derive(Clone)]
  559pub struct EditorStyle {
  560    pub background: Hsla,
  561    pub border: Hsla,
  562    pub local_player: PlayerColor,
  563    pub text: TextStyle,
  564    pub scrollbar_width: Pixels,
  565    pub syntax: Arc<SyntaxTheme>,
  566    pub status: StatusColors,
  567    pub inlay_hints_style: HighlightStyle,
  568    pub edit_prediction_styles: EditPredictionStyles,
  569    pub unnecessary_code_fade: f32,
  570    pub show_underlines: bool,
  571}
  572
  573impl Default for EditorStyle {
  574    fn default() -> Self {
  575        Self {
  576            background: Hsla::default(),
  577            border: Hsla::default(),
  578            local_player: PlayerColor::default(),
  579            text: TextStyle::default(),
  580            scrollbar_width: Pixels::default(),
  581            syntax: Default::default(),
  582            // HACK: Status colors don't have a real default.
  583            // We should look into removing the status colors from the editor
  584            // style and retrieve them directly from the theme.
  585            status: StatusColors::dark(),
  586            inlay_hints_style: HighlightStyle::default(),
  587            edit_prediction_styles: EditPredictionStyles {
  588                insertion: HighlightStyle::default(),
  589                whitespace: HighlightStyle::default(),
  590            },
  591            unnecessary_code_fade: Default::default(),
  592            show_underlines: true,
  593        }
  594    }
  595}
  596
  597pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  598    let show_background = language_settings::language_settings(None, None, cx)
  599        .inlay_hints
  600        .show_background;
  601
  602    let mut style = cx.theme().syntax().get("hint");
  603
  604    if style.color.is_none() {
  605        style.color = Some(cx.theme().status().hint);
  606    }
  607
  608    if !show_background {
  609        style.background_color = None;
  610        return style;
  611    }
  612
  613    if style.background_color.is_none() {
  614        style.background_color = Some(cx.theme().status().hint_background);
  615    }
  616
  617    style
  618}
  619
  620pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  621    EditPredictionStyles {
  622        insertion: HighlightStyle {
  623            color: Some(cx.theme().status().predictive),
  624            ..HighlightStyle::default()
  625        },
  626        whitespace: HighlightStyle {
  627            background_color: Some(cx.theme().status().created_background),
  628            ..HighlightStyle::default()
  629        },
  630    }
  631}
  632
  633type CompletionId = usize;
  634
  635pub(crate) enum EditDisplayMode {
  636    TabAccept,
  637    DiffPopover,
  638    Inline,
  639}
  640
  641enum EditPrediction {
  642    Edit {
  643        edits: Vec<(Range<Anchor>, Arc<str>)>,
  644        /// Predicted cursor position as (anchor, offset_from_anchor).
  645        /// The anchor is in multibuffer coordinates; after applying edits,
  646        /// resolve the anchor and add the offset to get the final cursor position.
  647        cursor_position: Option<(Anchor, usize)>,
  648        edit_preview: Option<EditPreview>,
  649        display_mode: EditDisplayMode,
  650        snapshot: BufferSnapshot,
  651    },
  652    /// Move to a specific location in the active editor
  653    MoveWithin {
  654        target: Anchor,
  655        snapshot: BufferSnapshot,
  656    },
  657    /// Move to a specific location in a different editor (not the active one)
  658    MoveOutside {
  659        target: language::Anchor,
  660        snapshot: BufferSnapshot,
  661    },
  662}
  663
  664struct EditPredictionState {
  665    inlay_ids: Vec<InlayId>,
  666    completion: EditPrediction,
  667    completion_id: Option<SharedString>,
  668    invalidation_range: Option<Range<Anchor>>,
  669}
  670
  671enum EditPredictionSettings {
  672    Disabled,
  673    Enabled {
  674        show_in_menu: bool,
  675        preview_requires_modifier: bool,
  676    },
  677}
  678
  679#[derive(Debug, Clone)]
  680struct InlineDiagnostic {
  681    message: SharedString,
  682    group_id: usize,
  683    is_primary: bool,
  684    start: Point,
  685    severity: lsp::DiagnosticSeverity,
  686}
  687
  688pub enum MenuEditPredictionsPolicy {
  689    Never,
  690    ByProvider,
  691}
  692
  693pub enum EditPredictionPreview {
  694    /// Modifier is not pressed
  695    Inactive { released_too_fast: bool },
  696    /// Modifier pressed
  697    Active {
  698        since: Instant,
  699        previous_scroll_position: Option<SharedScrollAnchor>,
  700    },
  701}
  702
  703impl EditPredictionPreview {
  704    pub fn released_too_fast(&self) -> bool {
  705        match self {
  706            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  707            EditPredictionPreview::Active { .. } => false,
  708        }
  709    }
  710
  711    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  712        if let EditPredictionPreview::Active {
  713            previous_scroll_position,
  714            ..
  715        } = self
  716        {
  717            *previous_scroll_position = scroll_position;
  718        }
  719    }
  720}
  721
  722pub struct ContextMenuOptions {
  723    pub min_entries_visible: usize,
  724    pub max_entries_visible: usize,
  725    pub placement: Option<ContextMenuPlacement>,
  726}
  727
  728#[derive(Debug, Clone, PartialEq, Eq)]
  729pub enum ContextMenuPlacement {
  730    Above,
  731    Below,
  732}
  733
  734#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  735struct EditorActionId(usize);
  736
  737impl EditorActionId {
  738    pub fn post_inc(&mut self) -> Self {
  739        let answer = self.0;
  740
  741        *self = Self(answer + 1);
  742
  743        Self(answer)
  744    }
  745}
  746
  747// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  748// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  749
  750type BackgroundHighlight = (
  751    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  752    Arc<[Range<Anchor>]>,
  753);
  754type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  755
  756#[derive(Default)]
  757struct ScrollbarMarkerState {
  758    scrollbar_size: Size<Pixels>,
  759    dirty: bool,
  760    markers: Arc<[PaintQuad]>,
  761    pending_refresh: Option<Task<Result<()>>>,
  762}
  763
  764impl ScrollbarMarkerState {
  765    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  766        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  767    }
  768}
  769
  770#[derive(Clone, Copy, PartialEq, Eq)]
  771pub enum MinimapVisibility {
  772    Disabled,
  773    Enabled {
  774        /// The configuration currently present in the users settings.
  775        setting_configuration: bool,
  776        /// Whether to override the currently set visibility from the users setting.
  777        toggle_override: bool,
  778    },
  779}
  780
  781impl MinimapVisibility {
  782    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  783        if mode.is_full() {
  784            Self::Enabled {
  785                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  786                toggle_override: false,
  787            }
  788        } else {
  789            Self::Disabled
  790        }
  791    }
  792
  793    fn hidden(&self) -> Self {
  794        match *self {
  795            Self::Enabled {
  796                setting_configuration,
  797                ..
  798            } => Self::Enabled {
  799                setting_configuration,
  800                toggle_override: setting_configuration,
  801            },
  802            Self::Disabled => Self::Disabled,
  803        }
  804    }
  805
  806    fn disabled(&self) -> bool {
  807        matches!(*self, Self::Disabled)
  808    }
  809
  810    fn settings_visibility(&self) -> bool {
  811        match *self {
  812            Self::Enabled {
  813                setting_configuration,
  814                ..
  815            } => setting_configuration,
  816            _ => false,
  817        }
  818    }
  819
  820    fn visible(&self) -> bool {
  821        match *self {
  822            Self::Enabled {
  823                setting_configuration,
  824                toggle_override,
  825            } => setting_configuration ^ toggle_override,
  826            _ => false,
  827        }
  828    }
  829
  830    fn toggle_visibility(&self) -> Self {
  831        match *self {
  832            Self::Enabled {
  833                toggle_override,
  834                setting_configuration,
  835            } => Self::Enabled {
  836                setting_configuration,
  837                toggle_override: !toggle_override,
  838            },
  839            Self::Disabled => Self::Disabled,
  840        }
  841    }
  842}
  843
  844#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  845pub enum BufferSerialization {
  846    All,
  847    NonDirtyBuffers,
  848}
  849
  850impl BufferSerialization {
  851    fn new(restore_unsaved_buffers: bool) -> Self {
  852        if restore_unsaved_buffers {
  853            Self::All
  854        } else {
  855            Self::NonDirtyBuffers
  856        }
  857    }
  858}
  859
  860#[derive(Clone, Debug)]
  861struct RunnableTasks {
  862    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  863    offset: multi_buffer::Anchor,
  864    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  865    column: u32,
  866    // Values of all named captures, including those starting with '_'
  867    extra_variables: HashMap<String, String>,
  868    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  869    context_range: Range<BufferOffset>,
  870}
  871
  872impl RunnableTasks {
  873    fn resolve<'a>(
  874        &'a self,
  875        cx: &'a task::TaskContext,
  876    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  877        self.templates.iter().filter_map(|(kind, template)| {
  878            template
  879                .resolve_task(&kind.to_id_base(), cx)
  880                .map(|task| (kind.clone(), task))
  881        })
  882    }
  883}
  884
  885#[derive(Clone)]
  886pub struct ResolvedTasks {
  887    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  888    position: Anchor,
  889}
  890
  891/// Addons allow storing per-editor state in other crates (e.g. Vim)
  892pub trait Addon: 'static {
  893    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  894
  895    fn render_buffer_header_controls(
  896        &self,
  897        _: &ExcerptInfo,
  898        _: &Window,
  899        _: &App,
  900    ) -> Option<AnyElement> {
  901        None
  902    }
  903
  904    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  905        None
  906    }
  907
  908    fn to_any(&self) -> &dyn std::any::Any;
  909
  910    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  911        None
  912    }
  913}
  914
  915struct ChangeLocation {
  916    current: Option<Vec<Anchor>>,
  917    original: Vec<Anchor>,
  918}
  919impl ChangeLocation {
  920    fn locations(&self) -> &[Anchor] {
  921        self.current.as_ref().unwrap_or(&self.original)
  922    }
  923}
  924
  925/// A set of caret positions, registered when the editor was edited.
  926pub struct ChangeList {
  927    changes: Vec<ChangeLocation>,
  928    /// Currently "selected" change.
  929    position: Option<usize>,
  930}
  931
  932impl ChangeList {
  933    pub fn new() -> Self {
  934        Self {
  935            changes: Vec::new(),
  936            position: None,
  937        }
  938    }
  939
  940    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  941    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  942    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  943        if self.changes.is_empty() {
  944            return None;
  945        }
  946
  947        let prev = self.position.unwrap_or(self.changes.len());
  948        let next = if direction == Direction::Prev {
  949            prev.saturating_sub(count)
  950        } else {
  951            (prev + count).min(self.changes.len() - 1)
  952        };
  953        self.position = Some(next);
  954        self.changes.get(next).map(|change| change.locations())
  955    }
  956
  957    /// Adds a new change to the list, resetting the change list position.
  958    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  959        self.position.take();
  960        if let Some(last) = self.changes.last_mut()
  961            && group
  962        {
  963            last.current = Some(new_positions)
  964        } else {
  965            self.changes.push(ChangeLocation {
  966                original: new_positions,
  967                current: None,
  968            });
  969        }
  970    }
  971
  972    pub fn last(&self) -> Option<&[Anchor]> {
  973        self.changes.last().map(|change| change.locations())
  974    }
  975
  976    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  977        self.changes.last().map(|change| change.original.as_slice())
  978    }
  979
  980    pub fn invert_last_group(&mut self) {
  981        if let Some(last) = self.changes.last_mut()
  982            && let Some(current) = last.current.as_mut()
  983        {
  984            mem::swap(&mut last.original, current);
  985        }
  986    }
  987}
  988
  989#[derive(Clone)]
  990struct InlineBlamePopoverState {
  991    scroll_handle: ScrollHandle,
  992    commit_message: Option<ParsedCommitMessage>,
  993    markdown: Entity<Markdown>,
  994}
  995
  996struct InlineBlamePopover {
  997    position: gpui::Point<Pixels>,
  998    hide_task: Option<Task<()>>,
  999    popover_bounds: Option<Bounds<Pixels>>,
 1000    popover_state: InlineBlamePopoverState,
 1001    keyboard_grace: bool,
 1002}
 1003
 1004enum SelectionDragState {
 1005    /// State when no drag related activity is detected.
 1006    None,
 1007    /// State when the mouse is down on a selection that is about to be dragged.
 1008    ReadyToDrag {
 1009        selection: Selection<Anchor>,
 1010        click_position: gpui::Point<Pixels>,
 1011        mouse_down_time: Instant,
 1012    },
 1013    /// State when the mouse is dragging the selection in the editor.
 1014    Dragging {
 1015        selection: Selection<Anchor>,
 1016        drop_cursor: Selection<Anchor>,
 1017        hide_drop_cursor: bool,
 1018    },
 1019}
 1020
 1021enum ColumnarSelectionState {
 1022    FromMouse {
 1023        selection_tail: Anchor,
 1024        display_point: Option<DisplayPoint>,
 1025    },
 1026    FromSelection {
 1027        selection_tail: Anchor,
 1028    },
 1029}
 1030
 1031/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1032/// a breakpoint on them.
 1033#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1034struct PhantomBreakpointIndicator {
 1035    display_row: DisplayRow,
 1036    /// There's a small debounce between hovering over the line and showing the indicator.
 1037    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1038    is_active: bool,
 1039    collides_with_existing_breakpoint: bool,
 1040}
 1041
 1042/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1043/// in diff view mode.
 1044#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1045pub(crate) struct PhantomDiffReviewIndicator {
 1046    /// The starting anchor of the selection (or the only row if not dragging).
 1047    pub start: Anchor,
 1048    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1049    pub end: Anchor,
 1050    /// There's a small debounce between hovering over the line and showing the indicator.
 1051    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1052    pub is_active: bool,
 1053}
 1054
 1055#[derive(Clone, Debug)]
 1056pub(crate) struct DiffReviewDragState {
 1057    pub start_anchor: Anchor,
 1058    pub current_anchor: Anchor,
 1059}
 1060
 1061impl DiffReviewDragState {
 1062    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1063        let start = self.start_anchor.to_display_point(snapshot).row();
 1064        let current = self.current_anchor.to_display_point(snapshot).row();
 1065
 1066        (start..=current).sorted()
 1067    }
 1068}
 1069
 1070/// Identifies a specific hunk in the diff buffer.
 1071/// Used as a key to group comments by their location.
 1072#[derive(Clone, Debug)]
 1073pub struct DiffHunkKey {
 1074    /// The file path (relative to worktree) this hunk belongs to.
 1075    pub file_path: Arc<util::rel_path::RelPath>,
 1076    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1077    pub hunk_start_anchor: Anchor,
 1078}
 1079
 1080/// A review comment stored locally before being sent to the Agent panel.
 1081#[derive(Clone)]
 1082pub struct StoredReviewComment {
 1083    /// Unique identifier for this comment (for edit/delete operations).
 1084    pub id: usize,
 1085    /// The comment text entered by the user.
 1086    pub comment: String,
 1087    /// Anchors for the code range being reviewed.
 1088    pub range: Range<Anchor>,
 1089    /// Timestamp when the comment was created (for chronological ordering).
 1090    pub created_at: Instant,
 1091    /// Whether this comment is currently being edited inline.
 1092    pub is_editing: bool,
 1093}
 1094
 1095impl StoredReviewComment {
 1096    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1097        Self {
 1098            id,
 1099            comment,
 1100            range: anchor_range,
 1101            created_at: Instant::now(),
 1102            is_editing: false,
 1103        }
 1104    }
 1105}
 1106
 1107/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1108pub(crate) struct DiffReviewOverlay {
 1109    pub anchor_range: Range<Anchor>,
 1110    /// The block ID for the overlay.
 1111    pub block_id: CustomBlockId,
 1112    /// The editor entity for the review input.
 1113    pub prompt_editor: Entity<Editor>,
 1114    /// The hunk key this overlay belongs to.
 1115    pub hunk_key: DiffHunkKey,
 1116    /// Whether the comments section is expanded.
 1117    pub comments_expanded: bool,
 1118    /// Editors for comments currently being edited inline.
 1119    /// Key: comment ID, Value: Editor entity for inline editing.
 1120    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1121    /// Subscriptions for inline edit editors' action handlers.
 1122    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1123    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1124    /// The current user's avatar URI for display in comment rows.
 1125    pub user_avatar_uri: Option<SharedUri>,
 1126    /// Subscription to keep the action handler alive.
 1127    _subscription: Subscription,
 1128}
 1129
 1130/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1131///
 1132/// See the [module level documentation](self) for more information.
 1133pub struct Editor {
 1134    focus_handle: FocusHandle,
 1135    last_focused_descendant: Option<WeakFocusHandle>,
 1136    /// The text buffer being edited
 1137    buffer: Entity<MultiBuffer>,
 1138    /// Map of how text in the buffer should be displayed.
 1139    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1140    pub display_map: Entity<DisplayMap>,
 1141    placeholder_display_map: Option<Entity<DisplayMap>>,
 1142    pub selections: SelectionsCollection,
 1143    pub scroll_manager: ScrollManager,
 1144    /// When inline assist editors are linked, they all render cursors because
 1145    /// typing enters text into each of them, even the ones that aren't focused.
 1146    pub(crate) show_cursor_when_unfocused: bool,
 1147    columnar_selection_state: Option<ColumnarSelectionState>,
 1148    add_selections_state: Option<AddSelectionsState>,
 1149    select_next_state: Option<SelectNextState>,
 1150    select_prev_state: Option<SelectNextState>,
 1151    selection_history: SelectionHistory,
 1152    defer_selection_effects: bool,
 1153    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1154    autoclose_regions: Vec<AutocloseRegion>,
 1155    snippet_stack: InvalidationStack<SnippetState>,
 1156    select_syntax_node_history: SelectSyntaxNodeHistory,
 1157    ime_transaction: Option<TransactionId>,
 1158    pub diagnostics_max_severity: DiagnosticSeverity,
 1159    active_diagnostics: ActiveDiagnostic,
 1160    show_inline_diagnostics: bool,
 1161    inline_diagnostics_update: Task<()>,
 1162    inline_diagnostics_enabled: bool,
 1163    diagnostics_enabled: bool,
 1164    word_completions_enabled: bool,
 1165    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1166    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1167    hard_wrap: Option<usize>,
 1168    project: Option<Entity<Project>>,
 1169    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1170    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1171    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1172    blink_manager: Entity<BlinkManager>,
 1173    show_cursor_names: bool,
 1174    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1175    pub show_local_selections: bool,
 1176    mode: EditorMode,
 1177    show_breadcrumbs: bool,
 1178    show_gutter: bool,
 1179    show_scrollbars: ScrollbarAxes,
 1180    minimap_visibility: MinimapVisibility,
 1181    offset_content: bool,
 1182    disable_expand_excerpt_buttons: bool,
 1183    delegate_expand_excerpts: bool,
 1184    delegate_stage_and_restore: bool,
 1185    delegate_open_excerpts: bool,
 1186    enable_lsp_data: bool,
 1187    enable_runnables: bool,
 1188    show_line_numbers: Option<bool>,
 1189    use_relative_line_numbers: Option<bool>,
 1190    show_git_diff_gutter: Option<bool>,
 1191    show_code_actions: Option<bool>,
 1192    show_runnables: Option<bool>,
 1193    show_breakpoints: Option<bool>,
 1194    show_diff_review_button: bool,
 1195    show_wrap_guides: Option<bool>,
 1196    show_indent_guides: Option<bool>,
 1197    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1198    highlight_order: usize,
 1199    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1200    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1201    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1202    scrollbar_marker_state: ScrollbarMarkerState,
 1203    active_indent_guides_state: ActiveIndentGuidesState,
 1204    nav_history: Option<ItemNavHistory>,
 1205    context_menu: RefCell<Option<CodeContextMenu>>,
 1206    context_menu_options: Option<ContextMenuOptions>,
 1207    mouse_context_menu: Option<MouseContextMenu>,
 1208    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1209    inline_blame_popover: Option<InlineBlamePopover>,
 1210    inline_blame_popover_show_task: Option<Task<()>>,
 1211    signature_help_state: SignatureHelpState,
 1212    auto_signature_help: Option<bool>,
 1213    find_all_references_task_sources: Vec<Anchor>,
 1214    next_completion_id: CompletionId,
 1215    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1216    code_actions_task: Option<Task<Result<()>>>,
 1217    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1218    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1219    debounced_selection_highlight_complete: bool,
 1220    document_highlights_task: Option<Task<()>>,
 1221    linked_editing_range_task: Option<Task<Option<()>>>,
 1222    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1223    pending_rename: Option<RenameState>,
 1224    searchable: bool,
 1225    cursor_shape: CursorShape,
 1226    /// Whether the cursor is offset one character to the left when something is
 1227    /// selected (needed for vim visual mode)
 1228    cursor_offset_on_selection: bool,
 1229    current_line_highlight: Option<CurrentLineHighlight>,
 1230    /// Whether to collapse search match ranges to just their start position.
 1231    /// When true, navigating to a match positions the cursor at the match
 1232    /// without selecting the matched text.
 1233    collapse_matches: bool,
 1234    autoindent_mode: Option<AutoindentMode>,
 1235    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1236    input_enabled: bool,
 1237    expects_character_input: bool,
 1238    use_modal_editing: bool,
 1239    read_only: bool,
 1240    leader_id: Option<CollaboratorId>,
 1241    remote_id: Option<ViewId>,
 1242    pub hover_state: HoverState,
 1243    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1244    prev_pressure_stage: Option<PressureStage>,
 1245    gutter_hovered: bool,
 1246    hovered_link_state: Option<HoveredLinkState>,
 1247    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1248    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1249    active_edit_prediction: Option<EditPredictionState>,
 1250    /// Used to prevent flickering as the user types while the menu is open
 1251    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1252    edit_prediction_settings: EditPredictionSettings,
 1253    edit_predictions_hidden_for_vim_mode: bool,
 1254    show_edit_predictions_override: Option<bool>,
 1255    show_completions_on_input_override: Option<bool>,
 1256    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1257    edit_prediction_preview: EditPredictionPreview,
 1258    edit_prediction_indent_conflict: bool,
 1259    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1260    next_inlay_id: usize,
 1261    next_color_inlay_id: usize,
 1262    _subscriptions: Vec<Subscription>,
 1263    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1264    gutter_dimensions: GutterDimensions,
 1265    style: Option<EditorStyle>,
 1266    text_style_refinement: Option<TextStyleRefinement>,
 1267    next_editor_action_id: EditorActionId,
 1268    editor_actions: Rc<
 1269        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1270    >,
 1271    use_autoclose: bool,
 1272    use_auto_surround: bool,
 1273    auto_replace_emoji_shortcode: bool,
 1274    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1275    show_git_blame_gutter: bool,
 1276    show_git_blame_inline: bool,
 1277    show_git_blame_inline_delay_task: Option<Task<()>>,
 1278    git_blame_inline_enabled: bool,
 1279    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1280    buffer_serialization: Option<BufferSerialization>,
 1281    show_selection_menu: Option<bool>,
 1282    blame: Option<Entity<GitBlame>>,
 1283    blame_subscription: Option<Subscription>,
 1284    custom_context_menu: Option<
 1285        Box<
 1286            dyn 'static
 1287                + Fn(
 1288                    &mut Self,
 1289                    DisplayPoint,
 1290                    &mut Window,
 1291                    &mut Context<Self>,
 1292                ) -> Option<Entity<ui::ContextMenu>>,
 1293        >,
 1294    >,
 1295    last_bounds: Option<Bounds<Pixels>>,
 1296    last_position_map: Option<Rc<PositionMap>>,
 1297    expect_bounds_change: Option<Bounds<Pixels>>,
 1298    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1299    tasks_update_task: Option<Task<()>>,
 1300    breakpoint_store: Option<Entity<BreakpointStore>>,
 1301    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1302    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1303    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1304    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1305    /// when hunks have comments stored.
 1306    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1307    /// Stored review comments grouped by hunk.
 1308    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1309    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1310    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1311    /// Counter for generating unique comment IDs.
 1312    next_review_comment_id: usize,
 1313    hovered_diff_hunk_row: Option<DisplayRow>,
 1314    pull_diagnostics_task: Task<()>,
 1315    in_project_search: bool,
 1316    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1317    breadcrumb_header: Option<String>,
 1318    focused_block: Option<FocusedBlock>,
 1319    next_scroll_position: NextScrollCursorCenterTopBottom,
 1320    addons: HashMap<TypeId, Box<dyn Addon>>,
 1321    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1322    load_diff_task: Option<Shared<Task<()>>>,
 1323    /// Whether we are temporarily displaying a diff other than git's
 1324    temporary_diff_override: bool,
 1325    selection_mark_mode: bool,
 1326    toggle_fold_multiple_buffers: Task<()>,
 1327    _scroll_cursor_center_top_bottom_task: Task<()>,
 1328    serialize_selections: Task<()>,
 1329    serialize_folds: Task<()>,
 1330    mouse_cursor_hidden: bool,
 1331    minimap: Option<Entity<Self>>,
 1332    hide_mouse_mode: HideMouseMode,
 1333    pub change_list: ChangeList,
 1334    inline_value_cache: InlineValueCache,
 1335    number_deleted_lines: bool,
 1336
 1337    selection_drag_state: SelectionDragState,
 1338    colors: Option<LspColorData>,
 1339    post_scroll_update: Task<()>,
 1340    refresh_colors_task: Task<()>,
 1341    use_document_folding_ranges: bool,
 1342    refresh_folding_ranges_task: Task<()>,
 1343    inlay_hints: Option<LspInlayHintData>,
 1344    folding_newlines: Task<()>,
 1345    select_next_is_case_sensitive: Option<bool>,
 1346    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1347    on_local_selections_changed:
 1348        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1349    suppress_selection_callback: bool,
 1350    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1351    accent_data: Option<AccentData>,
 1352    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1353    semantic_token_state: SemanticTokenState,
 1354    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1355    refresh_document_symbols_task: Shared<Task<()>>,
 1356    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1357    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1358    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1359    sticky_headers_task: Task<()>,
 1360    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1361    pub(crate) colorize_brackets_task: Task<()>,
 1362}
 1363
 1364#[derive(Debug, PartialEq)]
 1365struct AccentData {
 1366    colors: AccentColors,
 1367    overrides: Vec<SharedString>,
 1368}
 1369
 1370fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1371    if debounce_ms > 0 {
 1372        Some(Duration::from_millis(debounce_ms))
 1373    } else {
 1374        None
 1375    }
 1376}
 1377
 1378#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1379enum NextScrollCursorCenterTopBottom {
 1380    #[default]
 1381    Center,
 1382    Top,
 1383    Bottom,
 1384}
 1385
 1386impl NextScrollCursorCenterTopBottom {
 1387    fn next(&self) -> Self {
 1388        match self {
 1389            Self::Center => Self::Top,
 1390            Self::Top => Self::Bottom,
 1391            Self::Bottom => Self::Center,
 1392        }
 1393    }
 1394}
 1395
 1396#[derive(Clone)]
 1397pub struct EditorSnapshot {
 1398    pub mode: EditorMode,
 1399    show_gutter: bool,
 1400    offset_content: bool,
 1401    show_line_numbers: Option<bool>,
 1402    number_deleted_lines: bool,
 1403    show_git_diff_gutter: Option<bool>,
 1404    show_code_actions: Option<bool>,
 1405    show_runnables: Option<bool>,
 1406    show_breakpoints: Option<bool>,
 1407    git_blame_gutter_max_author_length: Option<usize>,
 1408    pub display_snapshot: DisplaySnapshot,
 1409    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1410    is_focused: bool,
 1411    scroll_anchor: SharedScrollAnchor,
 1412    ongoing_scroll: OngoingScroll,
 1413    current_line_highlight: CurrentLineHighlight,
 1414    gutter_hovered: bool,
 1415    semantic_tokens_enabled: bool,
 1416}
 1417
 1418#[derive(Default, Debug, Clone, Copy)]
 1419pub struct GutterDimensions {
 1420    pub left_padding: Pixels,
 1421    pub right_padding: Pixels,
 1422    pub width: Pixels,
 1423    pub margin: Pixels,
 1424    pub git_blame_entries_width: Option<Pixels>,
 1425}
 1426
 1427impl GutterDimensions {
 1428    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1429        Self {
 1430            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1431            ..Default::default()
 1432        }
 1433    }
 1434
 1435    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1436        -cx.text_system().descent(font_id, font_size)
 1437    }
 1438    /// The full width of the space taken up by the gutter.
 1439    pub fn full_width(&self) -> Pixels {
 1440        self.margin + self.width
 1441    }
 1442
 1443    /// The width of the space reserved for the fold indicators,
 1444    /// use alongside 'justify_end' and `gutter_width` to
 1445    /// right align content with the line numbers
 1446    pub fn fold_area_width(&self) -> Pixels {
 1447        self.margin + self.right_padding
 1448    }
 1449}
 1450
 1451struct CharacterDimensions {
 1452    em_width: Pixels,
 1453    em_advance: Pixels,
 1454    line_height: Pixels,
 1455}
 1456
 1457#[derive(Debug)]
 1458pub struct RemoteSelection {
 1459    pub replica_id: ReplicaId,
 1460    pub selection: Selection<Anchor>,
 1461    pub cursor_shape: CursorShape,
 1462    pub collaborator_id: CollaboratorId,
 1463    pub line_mode: bool,
 1464    pub user_name: Option<SharedString>,
 1465    pub color: PlayerColor,
 1466}
 1467
 1468#[derive(Clone, Debug)]
 1469struct SelectionHistoryEntry {
 1470    selections: Arc<[Selection<Anchor>]>,
 1471    select_next_state: Option<SelectNextState>,
 1472    select_prev_state: Option<SelectNextState>,
 1473    add_selections_state: Option<AddSelectionsState>,
 1474}
 1475
 1476#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1477enum SelectionHistoryMode {
 1478    #[default]
 1479    Normal,
 1480    Undoing,
 1481    Redoing,
 1482    Skipping,
 1483}
 1484
 1485#[derive(Clone, PartialEq, Eq, Hash)]
 1486struct HoveredCursor {
 1487    replica_id: ReplicaId,
 1488    selection_id: usize,
 1489}
 1490
 1491#[derive(Debug)]
 1492/// SelectionEffects controls the side-effects of updating the selection.
 1493///
 1494/// The default behaviour does "what you mostly want":
 1495/// - it pushes to the nav history if the cursor moved by >10 lines
 1496/// - it re-triggers completion requests
 1497/// - it scrolls to fit
 1498///
 1499/// You might want to modify these behaviours. For example when doing a "jump"
 1500/// like go to definition, we always want to add to nav history; but when scrolling
 1501/// in vim mode we never do.
 1502///
 1503/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1504/// move.
 1505#[derive(Clone)]
 1506pub struct SelectionEffects {
 1507    nav_history: Option<bool>,
 1508    completions: bool,
 1509    scroll: Option<Autoscroll>,
 1510}
 1511
 1512impl Default for SelectionEffects {
 1513    fn default() -> Self {
 1514        Self {
 1515            nav_history: None,
 1516            completions: true,
 1517            scroll: Some(Autoscroll::fit()),
 1518        }
 1519    }
 1520}
 1521impl SelectionEffects {
 1522    pub fn scroll(scroll: Autoscroll) -> Self {
 1523        Self {
 1524            scroll: Some(scroll),
 1525            ..Default::default()
 1526        }
 1527    }
 1528
 1529    pub fn no_scroll() -> Self {
 1530        Self {
 1531            scroll: None,
 1532            ..Default::default()
 1533        }
 1534    }
 1535
 1536    pub fn completions(self, completions: bool) -> Self {
 1537        Self {
 1538            completions,
 1539            ..self
 1540        }
 1541    }
 1542
 1543    pub fn nav_history(self, nav_history: bool) -> Self {
 1544        Self {
 1545            nav_history: Some(nav_history),
 1546            ..self
 1547        }
 1548    }
 1549}
 1550
 1551struct DeferredSelectionEffectsState {
 1552    changed: bool,
 1553    effects: SelectionEffects,
 1554    old_cursor_position: Anchor,
 1555    history_entry: SelectionHistoryEntry,
 1556}
 1557
 1558#[derive(Default)]
 1559struct SelectionHistory {
 1560    #[allow(clippy::type_complexity)]
 1561    selections_by_transaction:
 1562        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1563    mode: SelectionHistoryMode,
 1564    undo_stack: VecDeque<SelectionHistoryEntry>,
 1565    redo_stack: VecDeque<SelectionHistoryEntry>,
 1566}
 1567
 1568impl SelectionHistory {
 1569    #[track_caller]
 1570    fn insert_transaction(
 1571        &mut self,
 1572        transaction_id: TransactionId,
 1573        selections: Arc<[Selection<Anchor>]>,
 1574    ) {
 1575        if selections.is_empty() {
 1576            log::error!(
 1577                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1578                std::panic::Location::caller()
 1579            );
 1580            return;
 1581        }
 1582        self.selections_by_transaction
 1583            .insert(transaction_id, (selections, None));
 1584    }
 1585
 1586    #[allow(clippy::type_complexity)]
 1587    fn transaction(
 1588        &self,
 1589        transaction_id: TransactionId,
 1590    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1591        self.selections_by_transaction.get(&transaction_id)
 1592    }
 1593
 1594    #[allow(clippy::type_complexity)]
 1595    fn transaction_mut(
 1596        &mut self,
 1597        transaction_id: TransactionId,
 1598    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1599        self.selections_by_transaction.get_mut(&transaction_id)
 1600    }
 1601
 1602    fn push(&mut self, entry: SelectionHistoryEntry) {
 1603        if !entry.selections.is_empty() {
 1604            match self.mode {
 1605                SelectionHistoryMode::Normal => {
 1606                    self.push_undo(entry);
 1607                    self.redo_stack.clear();
 1608                }
 1609                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1610                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1611                SelectionHistoryMode::Skipping => {}
 1612            }
 1613        }
 1614    }
 1615
 1616    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1617        if self
 1618            .undo_stack
 1619            .back()
 1620            .is_none_or(|e| e.selections != entry.selections)
 1621        {
 1622            self.undo_stack.push_back(entry);
 1623            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1624                self.undo_stack.pop_front();
 1625            }
 1626        }
 1627    }
 1628
 1629    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1630        if self
 1631            .redo_stack
 1632            .back()
 1633            .is_none_or(|e| e.selections != entry.selections)
 1634        {
 1635            self.redo_stack.push_back(entry);
 1636            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1637                self.redo_stack.pop_front();
 1638            }
 1639        }
 1640    }
 1641}
 1642
 1643#[derive(Clone, Copy)]
 1644pub struct RowHighlightOptions {
 1645    pub autoscroll: bool,
 1646    pub include_gutter: bool,
 1647}
 1648
 1649impl Default for RowHighlightOptions {
 1650    fn default() -> Self {
 1651        Self {
 1652            autoscroll: Default::default(),
 1653            include_gutter: true,
 1654        }
 1655    }
 1656}
 1657
 1658struct RowHighlight {
 1659    index: usize,
 1660    range: Range<Anchor>,
 1661    color: Hsla,
 1662    options: RowHighlightOptions,
 1663    type_id: TypeId,
 1664}
 1665
 1666#[derive(Clone, Debug)]
 1667struct AddSelectionsState {
 1668    groups: Vec<AddSelectionsGroup>,
 1669}
 1670
 1671#[derive(Clone, Debug)]
 1672struct AddSelectionsGroup {
 1673    above: bool,
 1674    stack: Vec<usize>,
 1675}
 1676
 1677#[derive(Clone)]
 1678struct SelectNextState {
 1679    query: AhoCorasick,
 1680    wordwise: bool,
 1681    done: bool,
 1682}
 1683
 1684impl std::fmt::Debug for SelectNextState {
 1685    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1686        f.debug_struct(std::any::type_name::<Self>())
 1687            .field("wordwise", &self.wordwise)
 1688            .field("done", &self.done)
 1689            .finish()
 1690    }
 1691}
 1692
 1693#[derive(Debug)]
 1694struct AutocloseRegion {
 1695    selection_id: usize,
 1696    range: Range<Anchor>,
 1697    pair: BracketPair,
 1698}
 1699
 1700#[derive(Debug)]
 1701struct SnippetState {
 1702    ranges: Vec<Vec<Range<Anchor>>>,
 1703    active_index: usize,
 1704    choices: Vec<Option<Vec<String>>>,
 1705}
 1706
 1707#[doc(hidden)]
 1708pub struct RenameState {
 1709    pub range: Range<Anchor>,
 1710    pub old_name: Arc<str>,
 1711    pub editor: Entity<Editor>,
 1712    block_id: CustomBlockId,
 1713}
 1714
 1715struct InvalidationStack<T>(Vec<T>);
 1716
 1717struct RegisteredEditPredictionDelegate {
 1718    provider: Arc<dyn EditPredictionDelegateHandle>,
 1719    _subscription: Subscription,
 1720}
 1721
 1722#[derive(Debug, PartialEq, Eq)]
 1723pub struct ActiveDiagnosticGroup {
 1724    pub active_range: Range<Anchor>,
 1725    pub active_message: String,
 1726    pub group_id: usize,
 1727    pub blocks: HashSet<CustomBlockId>,
 1728}
 1729
 1730#[derive(Debug, PartialEq, Eq)]
 1731
 1732pub(crate) enum ActiveDiagnostic {
 1733    None,
 1734    All,
 1735    Group(ActiveDiagnosticGroup),
 1736}
 1737
 1738#[derive(Serialize, Deserialize, Clone, Debug)]
 1739pub struct ClipboardSelection {
 1740    /// The number of bytes in this selection.
 1741    pub len: usize,
 1742    /// Whether this was a full-line selection.
 1743    pub is_entire_line: bool,
 1744    /// The indentation of the first line when this content was originally copied.
 1745    pub first_line_indent: u32,
 1746    #[serde(default)]
 1747    pub file_path: Option<PathBuf>,
 1748    #[serde(default)]
 1749    pub line_range: Option<RangeInclusive<u32>>,
 1750}
 1751
 1752impl ClipboardSelection {
 1753    pub fn for_buffer(
 1754        len: usize,
 1755        is_entire_line: bool,
 1756        range: Range<Point>,
 1757        buffer: &MultiBufferSnapshot,
 1758        project: Option<&Entity<Project>>,
 1759        cx: &App,
 1760    ) -> Self {
 1761        let first_line_indent = buffer
 1762            .indent_size_for_line(MultiBufferRow(range.start.row))
 1763            .len;
 1764
 1765        let file_path = util::maybe!({
 1766            let project = project?.read(cx);
 1767            let file = buffer.file_at(range.start)?;
 1768            let project_path = ProjectPath {
 1769                worktree_id: file.worktree_id(cx),
 1770                path: file.path().clone(),
 1771            };
 1772            project.absolute_path(&project_path, cx)
 1773        });
 1774
 1775        let line_range = file_path.as_ref().and_then(|_| {
 1776            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1777            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1778            if start_excerpt_id == end_excerpt_id {
 1779                Some(start_point.row..=end_point.row)
 1780            } else {
 1781                None
 1782            }
 1783        });
 1784
 1785        Self {
 1786            len,
 1787            is_entire_line,
 1788            first_line_indent,
 1789            file_path,
 1790            line_range,
 1791        }
 1792    }
 1793}
 1794
 1795// selections, scroll behavior, was newest selection reversed
 1796type SelectSyntaxNodeHistoryState = (
 1797    Box<[Selection<Anchor>]>,
 1798    SelectSyntaxNodeScrollBehavior,
 1799    bool,
 1800);
 1801
 1802#[derive(Default)]
 1803struct SelectSyntaxNodeHistory {
 1804    stack: Vec<SelectSyntaxNodeHistoryState>,
 1805    // disable temporarily to allow changing selections without losing the stack
 1806    pub disable_clearing: bool,
 1807}
 1808
 1809impl SelectSyntaxNodeHistory {
 1810    pub fn try_clear(&mut self) {
 1811        if !self.disable_clearing {
 1812            self.stack.clear();
 1813        }
 1814    }
 1815
 1816    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1817        self.stack.push(selection);
 1818    }
 1819
 1820    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1821        self.stack.pop()
 1822    }
 1823}
 1824
 1825enum SelectSyntaxNodeScrollBehavior {
 1826    CursorTop,
 1827    FitSelection,
 1828    CursorBottom,
 1829}
 1830
 1831#[derive(Debug, Clone, Copy)]
 1832pub(crate) struct NavigationData {
 1833    cursor_anchor: Anchor,
 1834    cursor_position: Point,
 1835    scroll_anchor: ScrollAnchor,
 1836    scroll_top_row: u32,
 1837}
 1838
 1839#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1840pub enum GotoDefinitionKind {
 1841    Symbol,
 1842    Declaration,
 1843    Type,
 1844    Implementation,
 1845}
 1846
 1847pub enum FormatTarget {
 1848    Buffers(HashSet<Entity<Buffer>>),
 1849    Ranges(Vec<Range<MultiBufferPoint>>),
 1850}
 1851
 1852pub(crate) struct FocusedBlock {
 1853    id: BlockId,
 1854    focus_handle: WeakFocusHandle,
 1855}
 1856
 1857#[derive(Clone, Debug)]
 1858pub enum JumpData {
 1859    MultiBufferRow {
 1860        row: MultiBufferRow,
 1861        line_offset_from_top: u32,
 1862    },
 1863    MultiBufferPoint {
 1864        excerpt_id: ExcerptId,
 1865        position: Point,
 1866        anchor: text::Anchor,
 1867        line_offset_from_top: u32,
 1868    },
 1869}
 1870
 1871pub enum MultibufferSelectionMode {
 1872    First,
 1873    All,
 1874}
 1875
 1876#[derive(Clone, Copy, Debug, Default)]
 1877pub struct RewrapOptions {
 1878    pub override_language_settings: bool,
 1879    pub preserve_existing_whitespace: bool,
 1880}
 1881
 1882impl Editor {
 1883    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1884        let buffer = cx.new(|cx| Buffer::local("", cx));
 1885        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1886        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1887    }
 1888
 1889    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1890        let buffer = cx.new(|cx| Buffer::local("", cx));
 1891        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1892        Self::new(EditorMode::full(), buffer, None, window, cx)
 1893    }
 1894
 1895    pub fn auto_height(
 1896        min_lines: usize,
 1897        max_lines: usize,
 1898        window: &mut Window,
 1899        cx: &mut Context<Self>,
 1900    ) -> Self {
 1901        let buffer = cx.new(|cx| Buffer::local("", cx));
 1902        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1903        Self::new(
 1904            EditorMode::AutoHeight {
 1905                min_lines,
 1906                max_lines: Some(max_lines),
 1907            },
 1908            buffer,
 1909            None,
 1910            window,
 1911            cx,
 1912        )
 1913    }
 1914
 1915    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1916    /// The editor grows as tall as needed to fit its content.
 1917    pub fn auto_height_unbounded(
 1918        min_lines: usize,
 1919        window: &mut Window,
 1920        cx: &mut Context<Self>,
 1921    ) -> Self {
 1922        let buffer = cx.new(|cx| Buffer::local("", cx));
 1923        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1924        Self::new(
 1925            EditorMode::AutoHeight {
 1926                min_lines,
 1927                max_lines: None,
 1928            },
 1929            buffer,
 1930            None,
 1931            window,
 1932            cx,
 1933        )
 1934    }
 1935
 1936    pub fn for_buffer(
 1937        buffer: Entity<Buffer>,
 1938        project: Option<Entity<Project>>,
 1939        window: &mut Window,
 1940        cx: &mut Context<Self>,
 1941    ) -> Self {
 1942        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1943        Self::new(EditorMode::full(), buffer, project, window, cx)
 1944    }
 1945
 1946    pub fn for_multibuffer(
 1947        buffer: Entity<MultiBuffer>,
 1948        project: Option<Entity<Project>>,
 1949        window: &mut Window,
 1950        cx: &mut Context<Self>,
 1951    ) -> Self {
 1952        Self::new(EditorMode::full(), buffer, project, window, cx)
 1953    }
 1954
 1955    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1956        let mut clone = Self::new(
 1957            self.mode.clone(),
 1958            self.buffer.clone(),
 1959            self.project.clone(),
 1960            window,
 1961            cx,
 1962        );
 1963        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1964            let snapshot = display_map.snapshot(cx);
 1965            clone.display_map.update(cx, |display_map, cx| {
 1966                display_map.set_state(&snapshot, cx);
 1967            });
 1968            snapshot
 1969        });
 1970        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1971        clone.folds_did_change(cx);
 1972        clone.selections.clone_state(&self.selections);
 1973        clone
 1974            .scroll_manager
 1975            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1976        clone.searchable = self.searchable;
 1977        clone.read_only = self.read_only;
 1978        clone.buffers_with_disabled_indent_guides =
 1979            self.buffers_with_disabled_indent_guides.clone();
 1980        clone
 1981    }
 1982
 1983    pub fn new(
 1984        mode: EditorMode,
 1985        buffer: Entity<MultiBuffer>,
 1986        project: Option<Entity<Project>>,
 1987        window: &mut Window,
 1988        cx: &mut Context<Self>,
 1989    ) -> Self {
 1990        Editor::new_internal(mode, buffer, project, None, window, cx)
 1991    }
 1992
 1993    pub fn refresh_sticky_headers(
 1994        &mut self,
 1995        display_snapshot: &DisplaySnapshot,
 1996        cx: &mut Context<Editor>,
 1997    ) {
 1998        if !self.mode.is_full() {
 1999            return;
 2000        }
 2001        let multi_buffer = display_snapshot.buffer_snapshot();
 2002        let scroll_anchor = self
 2003            .scroll_manager
 2004            .native_anchor(display_snapshot, cx)
 2005            .anchor;
 2006        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2007            return;
 2008        };
 2009        let buffer = buffer.clone();
 2010
 2011        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2012        let max_row = buffer.max_point().row;
 2013        let start_row = buffer_visible_start.row.min(max_row);
 2014        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2015
 2016        let syntax = self.style(cx).syntax.clone();
 2017        let background_task = cx.background_spawn(async move {
 2018            buffer
 2019                .outline_items_containing(
 2020                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2021                    true,
 2022                    Some(syntax.as_ref()),
 2023                )
 2024                .into_iter()
 2025                .map(|outline_item| OutlineItem {
 2026                    depth: outline_item.depth,
 2027                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2028                    source_range_for_text: Anchor::range_in_buffer(
 2029                        excerpt_id,
 2030                        outline_item.source_range_for_text,
 2031                    ),
 2032                    text: outline_item.text,
 2033                    highlight_ranges: outline_item.highlight_ranges,
 2034                    name_ranges: outline_item.name_ranges,
 2035                    body_range: outline_item
 2036                        .body_range
 2037                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2038                    annotation_range: outline_item
 2039                        .annotation_range
 2040                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2041                })
 2042                .collect()
 2043        });
 2044        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2045            let sticky_headers = background_task.await;
 2046            this.update(cx, |this, cx| {
 2047                this.sticky_headers = Some(sticky_headers);
 2048                cx.notify();
 2049            })
 2050            .ok();
 2051        });
 2052    }
 2053
 2054    fn new_internal(
 2055        mode: EditorMode,
 2056        multi_buffer: Entity<MultiBuffer>,
 2057        project: Option<Entity<Project>>,
 2058        display_map: Option<Entity<DisplayMap>>,
 2059        window: &mut Window,
 2060        cx: &mut Context<Self>,
 2061    ) -> Self {
 2062        debug_assert!(
 2063            display_map.is_none() || mode.is_minimap(),
 2064            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2065        );
 2066
 2067        let full_mode = mode.is_full();
 2068        let is_minimap = mode.is_minimap();
 2069        let diagnostics_max_severity = if full_mode {
 2070            EditorSettings::get_global(cx)
 2071                .diagnostics_max_severity
 2072                .unwrap_or(DiagnosticSeverity::Hint)
 2073        } else {
 2074            DiagnosticSeverity::Off
 2075        };
 2076        let style = window.text_style();
 2077        let font_size = style.font_size.to_pixels(window.rem_size());
 2078        let editor = cx.entity().downgrade();
 2079        let fold_placeholder = FoldPlaceholder {
 2080            constrain_width: false,
 2081            render: Arc::new(move |fold_id, fold_range, cx| {
 2082                let editor = editor.clone();
 2083                FoldPlaceholder::fold_element(fold_id, cx)
 2084                    .cursor_pointer()
 2085                    .child("")
 2086                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2087                    .on_click(move |_, _window, cx| {
 2088                        editor
 2089                            .update(cx, |editor, cx| {
 2090                                editor.unfold_ranges(
 2091                                    &[fold_range.start..fold_range.end],
 2092                                    true,
 2093                                    false,
 2094                                    cx,
 2095                                );
 2096                                cx.stop_propagation();
 2097                            })
 2098                            .ok();
 2099                    })
 2100                    .into_any()
 2101            }),
 2102            merge_adjacent: true,
 2103            ..FoldPlaceholder::default()
 2104        };
 2105        let display_map = display_map.unwrap_or_else(|| {
 2106            cx.new(|cx| {
 2107                DisplayMap::new(
 2108                    multi_buffer.clone(),
 2109                    style.font(),
 2110                    font_size,
 2111                    None,
 2112                    FILE_HEADER_HEIGHT,
 2113                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2114                    fold_placeholder,
 2115                    diagnostics_max_severity,
 2116                    cx,
 2117                )
 2118            })
 2119        });
 2120
 2121        let selections = SelectionsCollection::new();
 2122
 2123        let blink_manager = cx.new(|cx| {
 2124            let mut blink_manager = BlinkManager::new(
 2125                CURSOR_BLINK_INTERVAL,
 2126                |cx| EditorSettings::get_global(cx).cursor_blink,
 2127                cx,
 2128            );
 2129            if is_minimap {
 2130                blink_manager.disable(cx);
 2131            }
 2132            blink_manager
 2133        });
 2134
 2135        let soft_wrap_mode_override =
 2136            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2137
 2138        let mut project_subscriptions = Vec::new();
 2139        if full_mode && let Some(project) = project.as_ref() {
 2140            project_subscriptions.push(cx.subscribe_in(
 2141                project,
 2142                window,
 2143                |editor, _, event, window, cx| match event {
 2144                    project::Event::RefreshCodeLens => {
 2145                        // we always query lens with actions, without storing them, always refreshing them
 2146                    }
 2147                    project::Event::RefreshInlayHints {
 2148                        server_id,
 2149                        request_id,
 2150                    } => {
 2151                        editor.refresh_inlay_hints(
 2152                            InlayHintRefreshReason::RefreshRequested {
 2153                                server_id: *server_id,
 2154                                request_id: *request_id,
 2155                            },
 2156                            cx,
 2157                        );
 2158                    }
 2159                    project::Event::RefreshSemanticTokens {
 2160                        server_id,
 2161                        request_id,
 2162                    } => {
 2163                        editor.refresh_semantic_tokens(
 2164                            None,
 2165                            Some(RefreshForServer {
 2166                                server_id: *server_id,
 2167                                request_id: *request_id,
 2168                            }),
 2169                            cx,
 2170                        );
 2171                    }
 2172                    project::Event::LanguageServerRemoved(_) => {
 2173                        editor.registered_buffers.clear();
 2174                        editor.register_visible_buffers(cx);
 2175                        editor.invalidate_semantic_tokens(None);
 2176                        editor.update_lsp_data(None, window, cx);
 2177                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2178                        if editor.tasks_update_task.is_none() {
 2179                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2180                        }
 2181                    }
 2182                    project::Event::LanguageServerAdded(..) => {
 2183                        if editor.tasks_update_task.is_none() {
 2184                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2185                        }
 2186                    }
 2187                    project::Event::SnippetEdit(id, snippet_edits) => {
 2188                        // todo(lw): Non singletons
 2189                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2190                            let snapshot = buffer.read(cx).snapshot();
 2191                            let focus_handle = editor.focus_handle(cx);
 2192                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2193                                for (range, snippet) in snippet_edits {
 2194                                    let buffer_range =
 2195                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2196                                    editor
 2197                                        .insert_snippet(
 2198                                            &[MultiBufferOffset(buffer_range.start)
 2199                                                ..MultiBufferOffset(buffer_range.end)],
 2200                                            snippet.clone(),
 2201                                            window,
 2202                                            cx,
 2203                                        )
 2204                                        .ok();
 2205                                }
 2206                            }
 2207                        }
 2208                    }
 2209                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2210                        let buffer_id = *buffer_id;
 2211                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2212                            editor.register_buffer(buffer_id, cx);
 2213                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2214                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2215                            refresh_linked_ranges(editor, window, cx);
 2216                            editor.refresh_code_actions(window, cx);
 2217                            editor.refresh_document_highlights(cx);
 2218                        }
 2219                    }
 2220
 2221                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2222                        let Some(workspace) = editor.workspace() else {
 2223                            return;
 2224                        };
 2225                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2226                        else {
 2227                            return;
 2228                        };
 2229
 2230                        if active_editor.entity_id() == cx.entity_id() {
 2231                            let entity_id = cx.entity_id();
 2232                            workspace.update(cx, |this, cx| {
 2233                                this.panes_mut()
 2234                                    .iter_mut()
 2235                                    .filter(|pane| pane.entity_id() != entity_id)
 2236                                    .for_each(|p| {
 2237                                        p.update(cx, |pane, _| {
 2238                                            pane.nav_history_mut().rename_item(
 2239                                                entity_id,
 2240                                                project_path.clone(),
 2241                                                abs_path.clone().into(),
 2242                                            );
 2243                                        })
 2244                                    });
 2245                            });
 2246
 2247                            Self::open_transaction_for_hidden_buffers(
 2248                                workspace,
 2249                                transaction.clone(),
 2250                                "Rename".to_string(),
 2251                                window,
 2252                                cx,
 2253                            );
 2254                        }
 2255                    }
 2256
 2257                    project::Event::WorkspaceEditApplied(transaction) => {
 2258                        let Some(workspace) = editor.workspace() else {
 2259                            return;
 2260                        };
 2261                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2262                        else {
 2263                            return;
 2264                        };
 2265
 2266                        if active_editor.entity_id() == cx.entity_id() {
 2267                            Self::open_transaction_for_hidden_buffers(
 2268                                workspace,
 2269                                transaction.clone(),
 2270                                "LSP Edit".to_string(),
 2271                                window,
 2272                                cx,
 2273                            );
 2274                        }
 2275                    }
 2276
 2277                    _ => {}
 2278                },
 2279            ));
 2280            if let Some(task_inventory) = project
 2281                .read(cx)
 2282                .task_store()
 2283                .read(cx)
 2284                .task_inventory()
 2285                .cloned()
 2286            {
 2287                project_subscriptions.push(cx.observe_in(
 2288                    &task_inventory,
 2289                    window,
 2290                    |editor, _, window, cx| {
 2291                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2292                    },
 2293                ));
 2294            };
 2295
 2296            project_subscriptions.push(cx.subscribe_in(
 2297                &project.read(cx).breakpoint_store(),
 2298                window,
 2299                |editor, _, event, window, cx| match event {
 2300                    BreakpointStoreEvent::ClearDebugLines => {
 2301                        editor.clear_row_highlights::<ActiveDebugLine>();
 2302                        editor.refresh_inline_values(cx);
 2303                    }
 2304                    BreakpointStoreEvent::SetDebugLine => {
 2305                        if editor.go_to_active_debug_line(window, cx) {
 2306                            cx.stop_propagation();
 2307                        }
 2308
 2309                        editor.refresh_inline_values(cx);
 2310                    }
 2311                    _ => {}
 2312                },
 2313            ));
 2314            let git_store = project.read(cx).git_store().clone();
 2315            let project = project.clone();
 2316            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2317                if let GitStoreEvent::RepositoryAdded = event {
 2318                    this.load_diff_task = Some(
 2319                        update_uncommitted_diff_for_buffer(
 2320                            cx.entity(),
 2321                            &project,
 2322                            this.buffer.read(cx).all_buffers(),
 2323                            this.buffer.clone(),
 2324                            cx,
 2325                        )
 2326                        .shared(),
 2327                    );
 2328                }
 2329            }));
 2330        }
 2331
 2332        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2333
 2334        let inlay_hint_settings =
 2335            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2336        let focus_handle = cx.focus_handle();
 2337        if !is_minimap {
 2338            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2339                .detach();
 2340            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2341                .detach();
 2342            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2343                .detach();
 2344            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2345                .detach();
 2346            cx.observe_pending_input(window, Self::observe_pending_input)
 2347                .detach();
 2348        }
 2349
 2350        let show_indent_guides =
 2351            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2352                Some(false)
 2353            } else {
 2354                None
 2355            };
 2356
 2357        let breakpoint_store = match (&mode, project.as_ref()) {
 2358            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2359            _ => None,
 2360        };
 2361
 2362        let mut code_action_providers = Vec::new();
 2363        let mut load_uncommitted_diff = None;
 2364        if let Some(project) = project.clone() {
 2365            load_uncommitted_diff = Some(
 2366                update_uncommitted_diff_for_buffer(
 2367                    cx.entity(),
 2368                    &project,
 2369                    multi_buffer.read(cx).all_buffers(),
 2370                    multi_buffer.clone(),
 2371                    cx,
 2372                )
 2373                .shared(),
 2374            );
 2375            code_action_providers.push(Rc::new(project) as Rc<_>);
 2376        }
 2377
 2378        let mut editor = Self {
 2379            focus_handle,
 2380            show_cursor_when_unfocused: false,
 2381            last_focused_descendant: None,
 2382            buffer: multi_buffer.clone(),
 2383            display_map: display_map.clone(),
 2384            placeholder_display_map: None,
 2385            selections,
 2386            scroll_manager: ScrollManager::new(cx),
 2387            columnar_selection_state: None,
 2388            add_selections_state: None,
 2389            select_next_state: None,
 2390            select_prev_state: None,
 2391            selection_history: SelectionHistory::default(),
 2392            defer_selection_effects: false,
 2393            deferred_selection_effects_state: None,
 2394            autoclose_regions: Vec::new(),
 2395            snippet_stack: InvalidationStack::default(),
 2396            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2397            ime_transaction: None,
 2398            active_diagnostics: ActiveDiagnostic::None,
 2399            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2400            inline_diagnostics_update: Task::ready(()),
 2401            inline_diagnostics: Vec::new(),
 2402            soft_wrap_mode_override,
 2403            diagnostics_max_severity,
 2404            hard_wrap: None,
 2405            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2406            semantics_provider: project
 2407                .as_ref()
 2408                .map(|project| Rc::new(project.downgrade()) as _),
 2409            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2410            project,
 2411            blink_manager: blink_manager.clone(),
 2412            show_local_selections: true,
 2413            show_scrollbars: ScrollbarAxes {
 2414                horizontal: full_mode,
 2415                vertical: full_mode,
 2416            },
 2417            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2418            offset_content: !matches!(mode, EditorMode::SingleLine),
 2419            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2420            show_gutter: full_mode,
 2421            show_line_numbers: (!full_mode).then_some(false),
 2422            use_relative_line_numbers: None,
 2423            disable_expand_excerpt_buttons: !full_mode,
 2424            delegate_expand_excerpts: false,
 2425            delegate_stage_and_restore: false,
 2426            delegate_open_excerpts: false,
 2427            enable_lsp_data: true,
 2428            enable_runnables: true,
 2429            show_git_diff_gutter: None,
 2430            show_code_actions: None,
 2431            show_runnables: None,
 2432            show_breakpoints: None,
 2433            show_diff_review_button: false,
 2434            show_wrap_guides: None,
 2435            show_indent_guides,
 2436            buffers_with_disabled_indent_guides: HashSet::default(),
 2437            highlight_order: 0,
 2438            highlighted_rows: HashMap::default(),
 2439            background_highlights: HashMap::default(),
 2440            gutter_highlights: HashMap::default(),
 2441            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2442            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2443            nav_history: None,
 2444            context_menu: RefCell::new(None),
 2445            context_menu_options: None,
 2446            mouse_context_menu: None,
 2447            completion_tasks: Vec::new(),
 2448            inline_blame_popover: None,
 2449            inline_blame_popover_show_task: None,
 2450            signature_help_state: SignatureHelpState::default(),
 2451            auto_signature_help: None,
 2452            find_all_references_task_sources: Vec::new(),
 2453            next_completion_id: 0,
 2454            next_inlay_id: 0,
 2455            code_action_providers,
 2456            available_code_actions: None,
 2457            code_actions_task: None,
 2458            quick_selection_highlight_task: None,
 2459            debounced_selection_highlight_task: None,
 2460            debounced_selection_highlight_complete: false,
 2461            document_highlights_task: None,
 2462            linked_editing_range_task: None,
 2463            pending_rename: None,
 2464            searchable: !is_minimap,
 2465            cursor_shape: EditorSettings::get_global(cx)
 2466                .cursor_shape
 2467                .unwrap_or_default(),
 2468            cursor_offset_on_selection: false,
 2469            current_line_highlight: None,
 2470            autoindent_mode: Some(AutoindentMode::EachLine),
 2471            collapse_matches: false,
 2472            workspace: None,
 2473            input_enabled: !is_minimap,
 2474            expects_character_input: !is_minimap,
 2475            use_modal_editing: full_mode,
 2476            read_only: is_minimap,
 2477            use_autoclose: true,
 2478            use_auto_surround: true,
 2479            auto_replace_emoji_shortcode: false,
 2480            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2481            leader_id: None,
 2482            remote_id: None,
 2483            hover_state: HoverState::default(),
 2484            pending_mouse_down: None,
 2485            prev_pressure_stage: None,
 2486            hovered_link_state: None,
 2487            edit_prediction_provider: None,
 2488            active_edit_prediction: None,
 2489            stale_edit_prediction_in_menu: None,
 2490            edit_prediction_preview: EditPredictionPreview::Inactive {
 2491                released_too_fast: false,
 2492            },
 2493            inline_diagnostics_enabled: full_mode,
 2494            diagnostics_enabled: full_mode,
 2495            word_completions_enabled: full_mode,
 2496            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2497            gutter_hovered: false,
 2498            pixel_position_of_newest_cursor: None,
 2499            last_bounds: None,
 2500            last_position_map: None,
 2501            expect_bounds_change: None,
 2502            gutter_dimensions: GutterDimensions::default(),
 2503            style: None,
 2504            show_cursor_names: false,
 2505            hovered_cursors: HashMap::default(),
 2506            next_editor_action_id: EditorActionId::default(),
 2507            editor_actions: Rc::default(),
 2508            edit_predictions_hidden_for_vim_mode: false,
 2509            show_edit_predictions_override: None,
 2510            show_completions_on_input_override: None,
 2511            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2512            edit_prediction_settings: EditPredictionSettings::Disabled,
 2513            edit_prediction_indent_conflict: false,
 2514            edit_prediction_requires_modifier_in_indent_conflict: true,
 2515            custom_context_menu: None,
 2516            show_git_blame_gutter: false,
 2517            show_git_blame_inline: false,
 2518            show_selection_menu: None,
 2519            show_git_blame_inline_delay_task: None,
 2520            git_blame_inline_enabled: full_mode
 2521                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2522            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2523            buffer_serialization: is_minimap.not().then(|| {
 2524                BufferSerialization::new(
 2525                    ProjectSettings::get_global(cx)
 2526                        .session
 2527                        .restore_unsaved_buffers,
 2528                )
 2529            }),
 2530            blame: None,
 2531            blame_subscription: None,
 2532            tasks: BTreeMap::default(),
 2533
 2534            breakpoint_store,
 2535            gutter_breakpoint_indicator: (None, None),
 2536            gutter_diff_review_indicator: (None, None),
 2537            diff_review_drag_state: None,
 2538            diff_review_overlays: Vec::new(),
 2539            stored_review_comments: Vec::new(),
 2540            next_review_comment_id: 0,
 2541            hovered_diff_hunk_row: None,
 2542            _subscriptions: (!is_minimap)
 2543                .then(|| {
 2544                    vec![
 2545                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2546                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2547                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2548                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2549                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2550                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2551                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2552                        cx.observe_window_activation(window, |editor, window, cx| {
 2553                            let active = window.is_window_active();
 2554                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2555                                if active {
 2556                                    blink_manager.enable(cx);
 2557                                } else {
 2558                                    blink_manager.disable(cx);
 2559                                }
 2560                            });
 2561                            if active {
 2562                                editor.show_mouse_cursor(cx);
 2563                            }
 2564                        }),
 2565                    ]
 2566                })
 2567                .unwrap_or_default(),
 2568            tasks_update_task: None,
 2569            pull_diagnostics_task: Task::ready(()),
 2570            colors: None,
 2571            refresh_colors_task: Task::ready(()),
 2572            use_document_folding_ranges: false,
 2573            refresh_folding_ranges_task: Task::ready(()),
 2574            inlay_hints: None,
 2575            next_color_inlay_id: 0,
 2576            post_scroll_update: Task::ready(()),
 2577            linked_edit_ranges: Default::default(),
 2578            in_project_search: false,
 2579            previous_search_ranges: None,
 2580            breadcrumb_header: None,
 2581            focused_block: None,
 2582            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2583            addons: HashMap::default(),
 2584            registered_buffers: HashMap::default(),
 2585            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2586            selection_mark_mode: false,
 2587            toggle_fold_multiple_buffers: Task::ready(()),
 2588            serialize_selections: Task::ready(()),
 2589            serialize_folds: Task::ready(()),
 2590            text_style_refinement: None,
 2591            load_diff_task: load_uncommitted_diff,
 2592            temporary_diff_override: false,
 2593            mouse_cursor_hidden: false,
 2594            minimap: None,
 2595            hide_mouse_mode: EditorSettings::get_global(cx)
 2596                .hide_mouse
 2597                .unwrap_or_default(),
 2598            change_list: ChangeList::new(),
 2599            mode,
 2600            selection_drag_state: SelectionDragState::None,
 2601            folding_newlines: Task::ready(()),
 2602            lookup_key: None,
 2603            select_next_is_case_sensitive: None,
 2604            on_local_selections_changed: None,
 2605            suppress_selection_callback: false,
 2606            applicable_language_settings: HashMap::default(),
 2607            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2608            accent_data: None,
 2609            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2610            number_deleted_lines: false,
 2611            refresh_matching_bracket_highlights_task: Task::ready(()),
 2612            refresh_document_symbols_task: Task::ready(()).shared(),
 2613            lsp_document_symbols: HashMap::default(),
 2614            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2615            outline_symbols_at_cursor: None,
 2616            sticky_headers_task: Task::ready(()),
 2617            sticky_headers: None,
 2618            colorize_brackets_task: Task::ready(()),
 2619        };
 2620
 2621        if is_minimap {
 2622            return editor;
 2623        }
 2624
 2625        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2626        editor.accent_data = editor.fetch_accent_data(cx);
 2627
 2628        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2629            editor
 2630                ._subscriptions
 2631                .push(cx.observe(breakpoints, |_, _, cx| {
 2632                    cx.notify();
 2633                }));
 2634        }
 2635        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2636        editor._subscriptions.extend(project_subscriptions);
 2637
 2638        editor._subscriptions.push(cx.subscribe_in(
 2639            &cx.entity(),
 2640            window,
 2641            |editor, _, e: &EditorEvent, window, cx| match e {
 2642                EditorEvent::ScrollPositionChanged { local, .. } => {
 2643                    if *local {
 2644                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2645                        editor.inline_blame_popover.take();
 2646                        let snapshot = editor.snapshot(window, cx);
 2647                        let new_anchor = editor
 2648                            .scroll_manager
 2649                            .native_anchor(&snapshot.display_snapshot, cx);
 2650                        editor.update_restoration_data(cx, move |data| {
 2651                            data.scroll_position = (
 2652                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2653                                new_anchor.offset,
 2654                            );
 2655                        });
 2656
 2657                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2658                            cx.background_executor()
 2659                                .timer(Duration::from_millis(50))
 2660                                .await;
 2661                            editor
 2662                                .update_in(cx, |editor, window, cx| {
 2663                                    editor.register_visible_buffers(cx);
 2664                                    editor.colorize_brackets(false, cx);
 2665                                    editor.refresh_inlay_hints(
 2666                                        InlayHintRefreshReason::NewLinesShown,
 2667                                        cx,
 2668                                    );
 2669                                    if !editor.buffer().read(cx).is_singleton() {
 2670                                        editor.update_lsp_data(None, window, cx);
 2671                                    }
 2672                                })
 2673                                .ok();
 2674                        });
 2675                    }
 2676                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2677                }
 2678                EditorEvent::Edited { .. } => {
 2679                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2680                        .map(|vim_mode| vim_mode.0)
 2681                        .unwrap_or(false);
 2682                    if !vim_mode {
 2683                        let display_map = editor.display_snapshot(cx);
 2684                        let selections = editor.selections.all_adjusted_display(&display_map);
 2685                        let pop_state = editor
 2686                            .change_list
 2687                            .last()
 2688                            .map(|previous| {
 2689                                previous.len() == selections.len()
 2690                                    && previous.iter().enumerate().all(|(ix, p)| {
 2691                                        p.to_display_point(&display_map).row()
 2692                                            == selections[ix].head().row()
 2693                                    })
 2694                            })
 2695                            .unwrap_or(false);
 2696                        let new_positions = selections
 2697                            .into_iter()
 2698                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2699                            .collect();
 2700                        editor
 2701                            .change_list
 2702                            .push_to_change_list(pop_state, new_positions);
 2703                    }
 2704                }
 2705                _ => (),
 2706            },
 2707        ));
 2708
 2709        if let Some(dap_store) = editor
 2710            .project
 2711            .as_ref()
 2712            .map(|project| project.read(cx).dap_store())
 2713        {
 2714            let weak_editor = cx.weak_entity();
 2715
 2716            editor
 2717                ._subscriptions
 2718                .push(
 2719                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2720                        let session_entity = cx.entity();
 2721                        weak_editor
 2722                            .update(cx, |editor, cx| {
 2723                                editor._subscriptions.push(
 2724                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2725                                );
 2726                            })
 2727                            .ok();
 2728                    }),
 2729                );
 2730
 2731            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2732                editor
 2733                    ._subscriptions
 2734                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2735            }
 2736        }
 2737
 2738        // skip adding the initial selection to selection history
 2739        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2740        editor.end_selection(window, cx);
 2741        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2742
 2743        editor.scroll_manager.show_scrollbars(window, cx);
 2744        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2745
 2746        if full_mode {
 2747            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2748            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2749
 2750            if editor.git_blame_inline_enabled {
 2751                editor.start_git_blame_inline(false, window, cx);
 2752            }
 2753
 2754            editor.go_to_active_debug_line(window, cx);
 2755
 2756            editor.minimap =
 2757                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2758            editor.colors = Some(LspColorData::new(cx));
 2759            editor.use_document_folding_ranges = true;
 2760            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2761
 2762            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2763                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2764            }
 2765            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2766        }
 2767
 2768        editor
 2769    }
 2770
 2771    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2772        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2773    }
 2774
 2775    pub fn deploy_mouse_context_menu(
 2776        &mut self,
 2777        position: gpui::Point<Pixels>,
 2778        context_menu: Entity<ContextMenu>,
 2779        window: &mut Window,
 2780        cx: &mut Context<Self>,
 2781    ) {
 2782        self.mouse_context_menu = Some(MouseContextMenu::new(
 2783            self,
 2784            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2785            context_menu,
 2786            window,
 2787            cx,
 2788        ));
 2789    }
 2790
 2791    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2792        self.mouse_context_menu
 2793            .as_ref()
 2794            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2795    }
 2796
 2797    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2798        if self
 2799            .selections
 2800            .pending_anchor()
 2801            .is_some_and(|pending_selection| {
 2802                let snapshot = self.buffer().read(cx).snapshot(cx);
 2803                pending_selection.range().includes(range, &snapshot)
 2804            })
 2805        {
 2806            return true;
 2807        }
 2808
 2809        self.selections
 2810            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2811            .into_iter()
 2812            .any(|selection| {
 2813                // This is needed to cover a corner case, if we just check for an existing
 2814                // selection in the fold range, having a cursor at the start of the fold
 2815                // marks it as selected. Non-empty selections don't cause this.
 2816                let length = selection.end - selection.start;
 2817                length > 0
 2818            })
 2819    }
 2820
 2821    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2822        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2823    }
 2824
 2825    fn key_context_internal(
 2826        &self,
 2827        has_active_edit_prediction: bool,
 2828        window: &mut Window,
 2829        cx: &mut App,
 2830    ) -> KeyContext {
 2831        let mut key_context = KeyContext::new_with_defaults();
 2832        key_context.add("Editor");
 2833        let mode = match self.mode {
 2834            EditorMode::SingleLine => "single_line",
 2835            EditorMode::AutoHeight { .. } => "auto_height",
 2836            EditorMode::Minimap { .. } => "minimap",
 2837            EditorMode::Full { .. } => "full",
 2838        };
 2839
 2840        if EditorSettings::jupyter_enabled(cx) {
 2841            key_context.add("jupyter");
 2842        }
 2843
 2844        key_context.set("mode", mode);
 2845        if self.pending_rename.is_some() {
 2846            key_context.add("renaming");
 2847        }
 2848
 2849        if let Some(snippet_stack) = self.snippet_stack.last() {
 2850            key_context.add("in_snippet");
 2851
 2852            if snippet_stack.active_index > 0 {
 2853                key_context.add("has_previous_tabstop");
 2854            }
 2855
 2856            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2857                key_context.add("has_next_tabstop");
 2858            }
 2859        }
 2860
 2861        match self.context_menu.borrow().as_ref() {
 2862            Some(CodeContextMenu::Completions(menu)) => {
 2863                if menu.visible() {
 2864                    key_context.add("menu");
 2865                    key_context.add("showing_completions");
 2866                }
 2867            }
 2868            Some(CodeContextMenu::CodeActions(menu)) => {
 2869                if menu.visible() {
 2870                    key_context.add("menu");
 2871                    key_context.add("showing_code_actions")
 2872                }
 2873            }
 2874            None => {}
 2875        }
 2876
 2877        if self.signature_help_state.has_multiple_signatures() {
 2878            key_context.add("showing_signature_help");
 2879        }
 2880
 2881        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2882        if !self.focus_handle(cx).contains_focused(window, cx)
 2883            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2884        {
 2885            for addon in self.addons.values() {
 2886                addon.extend_key_context(&mut key_context, cx)
 2887            }
 2888        }
 2889
 2890        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2891            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2892                Some(
 2893                    file.full_path(cx)
 2894                        .extension()?
 2895                        .to_string_lossy()
 2896                        .to_lowercase(),
 2897                )
 2898            }) {
 2899                key_context.set("extension", extension);
 2900            }
 2901        } else {
 2902            key_context.add("multibuffer");
 2903        }
 2904
 2905        if has_active_edit_prediction {
 2906            if self.edit_prediction_in_conflict() {
 2907                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2908            } else {
 2909                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2910                key_context.add("copilot_suggestion");
 2911            }
 2912        }
 2913
 2914        if self.selection_mark_mode {
 2915            key_context.add("selection_mode");
 2916        }
 2917
 2918        let disjoint = self.selections.disjoint_anchors();
 2919        let snapshot = self.snapshot(window, cx);
 2920        let snapshot = snapshot.buffer_snapshot();
 2921        if self.mode == EditorMode::SingleLine
 2922            && let [selection] = disjoint
 2923            && selection.start == selection.end
 2924            && selection.end.to_offset(snapshot) == snapshot.len()
 2925        {
 2926            key_context.add("end_of_input");
 2927        }
 2928
 2929        if self.has_any_expanded_diff_hunks(cx) {
 2930            key_context.add("diffs_expanded");
 2931        }
 2932
 2933        key_context
 2934    }
 2935
 2936    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2937        self.last_bounds.as_ref()
 2938    }
 2939
 2940    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2941        if self.mouse_cursor_hidden {
 2942            self.mouse_cursor_hidden = false;
 2943            cx.notify();
 2944        }
 2945    }
 2946
 2947    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2948        let hide_mouse_cursor = match origin {
 2949            HideMouseCursorOrigin::TypingAction => {
 2950                matches!(
 2951                    self.hide_mouse_mode,
 2952                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2953                )
 2954            }
 2955            HideMouseCursorOrigin::MovementAction => {
 2956                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2957            }
 2958        };
 2959        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2960            self.mouse_cursor_hidden = hide_mouse_cursor;
 2961            cx.notify();
 2962        }
 2963    }
 2964
 2965    pub fn edit_prediction_in_conflict(&self) -> bool {
 2966        if !self.show_edit_predictions_in_menu() {
 2967            return false;
 2968        }
 2969
 2970        let showing_completions = self
 2971            .context_menu
 2972            .borrow()
 2973            .as_ref()
 2974            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2975
 2976        showing_completions
 2977            || self.edit_prediction_requires_modifier()
 2978            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2979            // bindings to insert tab characters.
 2980            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2981    }
 2982
 2983    pub fn accept_edit_prediction_keybind(
 2984        &self,
 2985        granularity: EditPredictionGranularity,
 2986        window: &mut Window,
 2987        cx: &mut App,
 2988    ) -> AcceptEditPredictionBinding {
 2989        let key_context = self.key_context_internal(true, window, cx);
 2990        let in_conflict = self.edit_prediction_in_conflict();
 2991
 2992        let bindings =
 2993            match granularity {
 2994                EditPredictionGranularity::Word => window
 2995                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2996                EditPredictionGranularity::Line => window
 2997                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2998                EditPredictionGranularity::Full => {
 2999                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 3000                }
 3001            };
 3002
 3003        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 3004            !in_conflict
 3005                || binding
 3006                    .keystrokes()
 3007                    .first()
 3008                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 3009        }))
 3010    }
 3011
 3012    pub fn new_file(
 3013        workspace: &mut Workspace,
 3014        _: &workspace::NewFile,
 3015        window: &mut Window,
 3016        cx: &mut Context<Workspace>,
 3017    ) {
 3018        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3019            "Failed to create buffer",
 3020            window,
 3021            cx,
 3022            |e, _, _| match e.error_code() {
 3023                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3024                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3025                e.error_tag("required").unwrap_or("the latest version")
 3026            )),
 3027                _ => None,
 3028            },
 3029        );
 3030    }
 3031
 3032    pub fn new_in_workspace(
 3033        workspace: &mut Workspace,
 3034        window: &mut Window,
 3035        cx: &mut Context<Workspace>,
 3036    ) -> Task<Result<Entity<Editor>>> {
 3037        let project = workspace.project().clone();
 3038        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3039
 3040        cx.spawn_in(window, async move |workspace, cx| {
 3041            let buffer = create.await?;
 3042            workspace.update_in(cx, |workspace, window, cx| {
 3043                let editor =
 3044                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3045                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3046                editor
 3047            })
 3048        })
 3049    }
 3050
 3051    fn new_file_vertical(
 3052        workspace: &mut Workspace,
 3053        _: &workspace::NewFileSplitVertical,
 3054        window: &mut Window,
 3055        cx: &mut Context<Workspace>,
 3056    ) {
 3057        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3058    }
 3059
 3060    fn new_file_horizontal(
 3061        workspace: &mut Workspace,
 3062        _: &workspace::NewFileSplitHorizontal,
 3063        window: &mut Window,
 3064        cx: &mut Context<Workspace>,
 3065    ) {
 3066        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3067    }
 3068
 3069    fn new_file_split(
 3070        workspace: &mut Workspace,
 3071        action: &workspace::NewFileSplit,
 3072        window: &mut Window,
 3073        cx: &mut Context<Workspace>,
 3074    ) {
 3075        Self::new_file_in_direction(workspace, action.0, window, cx)
 3076    }
 3077
 3078    fn new_file_in_direction(
 3079        workspace: &mut Workspace,
 3080        direction: SplitDirection,
 3081        window: &mut Window,
 3082        cx: &mut Context<Workspace>,
 3083    ) {
 3084        let project = workspace.project().clone();
 3085        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3086
 3087        cx.spawn_in(window, async move |workspace, cx| {
 3088            let buffer = create.await?;
 3089            workspace.update_in(cx, move |workspace, window, cx| {
 3090                workspace.split_item(
 3091                    direction,
 3092                    Box::new(
 3093                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3094                    ),
 3095                    window,
 3096                    cx,
 3097                )
 3098            })?;
 3099            anyhow::Ok(())
 3100        })
 3101        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3102            match e.error_code() {
 3103                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3104                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3105                e.error_tag("required").unwrap_or("the latest version")
 3106            )),
 3107                _ => None,
 3108            }
 3109        });
 3110    }
 3111
 3112    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3113        self.leader_id
 3114    }
 3115
 3116    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3117        &self.buffer
 3118    }
 3119
 3120    pub fn project(&self) -> Option<&Entity<Project>> {
 3121        self.project.as_ref()
 3122    }
 3123
 3124    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3125        self.workspace.as_ref()?.0.upgrade()
 3126    }
 3127
 3128    /// Detaches a task and shows an error notification in the workspace if available,
 3129    /// otherwise just logs the error.
 3130    pub fn detach_and_notify_err<R, E>(
 3131        &self,
 3132        task: Task<Result<R, E>>,
 3133        window: &mut Window,
 3134        cx: &mut App,
 3135    ) where
 3136        E: std::fmt::Debug + std::fmt::Display + 'static,
 3137        R: 'static,
 3138    {
 3139        if let Some(workspace) = self.workspace() {
 3140            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3141        } else {
 3142            task.detach_and_log_err(cx);
 3143        }
 3144    }
 3145
 3146    /// Returns the workspace serialization ID if this editor should be serialized.
 3147    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3148        self.workspace
 3149            .as_ref()
 3150            .filter(|_| self.should_serialize_buffer())
 3151            .and_then(|workspace| workspace.1)
 3152    }
 3153
 3154    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3155        self.buffer().read(cx).title(cx)
 3156    }
 3157
 3158    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3159        let git_blame_gutter_max_author_length = self
 3160            .render_git_blame_gutter(cx)
 3161            .then(|| {
 3162                if let Some(blame) = self.blame.as_ref() {
 3163                    let max_author_length =
 3164                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3165                    Some(max_author_length)
 3166                } else {
 3167                    None
 3168                }
 3169            })
 3170            .flatten();
 3171
 3172        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3173
 3174        EditorSnapshot {
 3175            mode: self.mode.clone(),
 3176            show_gutter: self.show_gutter,
 3177            offset_content: self.offset_content,
 3178            show_line_numbers: self.show_line_numbers,
 3179            number_deleted_lines: self.number_deleted_lines,
 3180            show_git_diff_gutter: self.show_git_diff_gutter,
 3181            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3182            show_code_actions: self.show_code_actions,
 3183            show_runnables: self.show_runnables,
 3184            show_breakpoints: self.show_breakpoints,
 3185            git_blame_gutter_max_author_length,
 3186            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3187            display_snapshot,
 3188            placeholder_display_snapshot: self
 3189                .placeholder_display_map
 3190                .as_ref()
 3191                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3192            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3193            is_focused: self.focus_handle.is_focused(window),
 3194            current_line_highlight: self
 3195                .current_line_highlight
 3196                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3197            gutter_hovered: self.gutter_hovered,
 3198        }
 3199    }
 3200
 3201    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3202        self.buffer.read(cx).language_at(point, cx)
 3203    }
 3204
 3205    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3206        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3207    }
 3208
 3209    pub fn active_excerpt(
 3210        &self,
 3211        cx: &App,
 3212    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3213        self.buffer
 3214            .read(cx)
 3215            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3216    }
 3217
 3218    pub fn mode(&self) -> &EditorMode {
 3219        &self.mode
 3220    }
 3221
 3222    pub fn set_mode(&mut self, mode: EditorMode) {
 3223        self.mode = mode;
 3224    }
 3225
 3226    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3227        self.collaboration_hub.as_deref()
 3228    }
 3229
 3230    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3231        self.collaboration_hub = Some(hub);
 3232    }
 3233
 3234    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3235        self.in_project_search = in_project_search;
 3236    }
 3237
 3238    pub fn set_custom_context_menu(
 3239        &mut self,
 3240        f: impl 'static
 3241        + Fn(
 3242            &mut Self,
 3243            DisplayPoint,
 3244            &mut Window,
 3245            &mut Context<Self>,
 3246        ) -> Option<Entity<ui::ContextMenu>>,
 3247    ) {
 3248        self.custom_context_menu = Some(Box::new(f))
 3249    }
 3250
 3251    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3252        self.completion_provider = provider;
 3253    }
 3254
 3255    #[cfg(any(test, feature = "test-support"))]
 3256    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3257        self.completion_provider.clone()
 3258    }
 3259
 3260    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3261        self.semantics_provider.clone()
 3262    }
 3263
 3264    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3265        self.semantics_provider = provider;
 3266    }
 3267
 3268    pub fn set_edit_prediction_provider<T>(
 3269        &mut self,
 3270        provider: Option<Entity<T>>,
 3271        window: &mut Window,
 3272        cx: &mut Context<Self>,
 3273    ) where
 3274        T: EditPredictionDelegate,
 3275    {
 3276        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3277            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3278                if this.focus_handle.is_focused(window) {
 3279                    this.update_visible_edit_prediction(window, cx);
 3280                }
 3281            }),
 3282            provider: Arc::new(provider),
 3283        });
 3284        self.update_edit_prediction_settings(cx);
 3285        self.refresh_edit_prediction(false, false, window, cx);
 3286    }
 3287
 3288    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3289        self.placeholder_display_map
 3290            .as_ref()
 3291            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3292    }
 3293
 3294    pub fn set_placeholder_text(
 3295        &mut self,
 3296        placeholder_text: &str,
 3297        window: &mut Window,
 3298        cx: &mut Context<Self>,
 3299    ) {
 3300        let multibuffer = cx
 3301            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3302
 3303        let style = window.text_style();
 3304
 3305        self.placeholder_display_map = Some(cx.new(|cx| {
 3306            DisplayMap::new(
 3307                multibuffer,
 3308                style.font(),
 3309                style.font_size.to_pixels(window.rem_size()),
 3310                None,
 3311                FILE_HEADER_HEIGHT,
 3312                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3313                Default::default(),
 3314                DiagnosticSeverity::Off,
 3315                cx,
 3316            )
 3317        }));
 3318        cx.notify();
 3319    }
 3320
 3321    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3322        self.cursor_shape = cursor_shape;
 3323
 3324        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3325        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3326
 3327        cx.notify();
 3328    }
 3329
 3330    pub fn cursor_shape(&self) -> CursorShape {
 3331        self.cursor_shape
 3332    }
 3333
 3334    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3335        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3336    }
 3337
 3338    pub fn set_current_line_highlight(
 3339        &mut self,
 3340        current_line_highlight: Option<CurrentLineHighlight>,
 3341    ) {
 3342        self.current_line_highlight = current_line_highlight;
 3343    }
 3344
 3345    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3346        self.collapse_matches = collapse_matches;
 3347    }
 3348
 3349    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3350        if self.collapse_matches {
 3351            return range.start..range.start;
 3352        }
 3353        range.clone()
 3354    }
 3355
 3356    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3357        self.display_map.read(cx).clip_at_line_ends
 3358    }
 3359
 3360    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3361        if self.display_map.read(cx).clip_at_line_ends != clip {
 3362            self.display_map
 3363                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3364        }
 3365    }
 3366
 3367    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3368        self.input_enabled = input_enabled;
 3369    }
 3370
 3371    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3372        self.expects_character_input = expects_character_input;
 3373    }
 3374
 3375    pub fn set_edit_predictions_hidden_for_vim_mode(
 3376        &mut self,
 3377        hidden: bool,
 3378        window: &mut Window,
 3379        cx: &mut Context<Self>,
 3380    ) {
 3381        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3382            self.edit_predictions_hidden_for_vim_mode = hidden;
 3383            if hidden {
 3384                self.update_visible_edit_prediction(window, cx);
 3385            } else {
 3386                self.refresh_edit_prediction(true, false, window, cx);
 3387            }
 3388        }
 3389    }
 3390
 3391    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3392        self.menu_edit_predictions_policy = value;
 3393    }
 3394
 3395    pub fn set_autoindent(&mut self, autoindent: bool) {
 3396        if autoindent {
 3397            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3398        } else {
 3399            self.autoindent_mode = None;
 3400        }
 3401    }
 3402
 3403    pub fn capability(&self, cx: &App) -> Capability {
 3404        if self.read_only {
 3405            Capability::ReadOnly
 3406        } else {
 3407            self.buffer.read(cx).capability()
 3408        }
 3409    }
 3410
 3411    pub fn read_only(&self, cx: &App) -> bool {
 3412        self.read_only || self.buffer.read(cx).read_only()
 3413    }
 3414
 3415    pub fn set_read_only(&mut self, read_only: bool) {
 3416        self.read_only = read_only;
 3417    }
 3418
 3419    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3420        self.use_autoclose = autoclose;
 3421    }
 3422
 3423    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3424        self.use_auto_surround = auto_surround;
 3425    }
 3426
 3427    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3428        self.auto_replace_emoji_shortcode = auto_replace;
 3429    }
 3430
 3431    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3432        self.buffer_serialization = should_serialize.then(|| {
 3433            BufferSerialization::new(
 3434                ProjectSettings::get_global(cx)
 3435                    .session
 3436                    .restore_unsaved_buffers,
 3437            )
 3438        })
 3439    }
 3440
 3441    fn should_serialize_buffer(&self) -> bool {
 3442        self.buffer_serialization.is_some()
 3443    }
 3444
 3445    pub fn toggle_edit_predictions(
 3446        &mut self,
 3447        _: &ToggleEditPrediction,
 3448        window: &mut Window,
 3449        cx: &mut Context<Self>,
 3450    ) {
 3451        if self.show_edit_predictions_override.is_some() {
 3452            self.set_show_edit_predictions(None, window, cx);
 3453        } else {
 3454            let show_edit_predictions = !self.edit_predictions_enabled();
 3455            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3456        }
 3457    }
 3458
 3459    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3460        self.show_completions_on_input_override = show_completions_on_input;
 3461    }
 3462
 3463    pub fn set_show_edit_predictions(
 3464        &mut self,
 3465        show_edit_predictions: Option<bool>,
 3466        window: &mut Window,
 3467        cx: &mut Context<Self>,
 3468    ) {
 3469        self.show_edit_predictions_override = show_edit_predictions;
 3470        self.update_edit_prediction_settings(cx);
 3471
 3472        if let Some(false) = show_edit_predictions {
 3473            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3474        } else {
 3475            self.refresh_edit_prediction(false, true, window, cx);
 3476        }
 3477    }
 3478
 3479    fn edit_predictions_disabled_in_scope(
 3480        &self,
 3481        buffer: &Entity<Buffer>,
 3482        buffer_position: language::Anchor,
 3483        cx: &App,
 3484    ) -> bool {
 3485        let snapshot = buffer.read(cx).snapshot();
 3486        let settings = snapshot.settings_at(buffer_position, cx);
 3487
 3488        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3489            return false;
 3490        };
 3491
 3492        scope.override_name().is_some_and(|scope_name| {
 3493            settings
 3494                .edit_predictions_disabled_in
 3495                .iter()
 3496                .any(|s| s == scope_name)
 3497        })
 3498    }
 3499
 3500    pub fn set_use_modal_editing(&mut self, to: bool) {
 3501        self.use_modal_editing = to;
 3502    }
 3503
 3504    pub fn use_modal_editing(&self) -> bool {
 3505        self.use_modal_editing
 3506    }
 3507
 3508    fn selections_did_change(
 3509        &mut self,
 3510        local: bool,
 3511        old_cursor_position: &Anchor,
 3512        effects: SelectionEffects,
 3513        window: &mut Window,
 3514        cx: &mut Context<Self>,
 3515    ) {
 3516        window.invalidate_character_coordinates();
 3517
 3518        // Copy selections to primary selection buffer
 3519        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3520        if local {
 3521            let selections = self
 3522                .selections
 3523                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3524            let buffer_handle = self.buffer.read(cx).read(cx);
 3525
 3526            let mut text = String::new();
 3527            for (index, selection) in selections.iter().enumerate() {
 3528                let text_for_selection = buffer_handle
 3529                    .text_for_range(selection.start..selection.end)
 3530                    .collect::<String>();
 3531
 3532                text.push_str(&text_for_selection);
 3533                if index != selections.len() - 1 {
 3534                    text.push('\n');
 3535                }
 3536            }
 3537
 3538            if !text.is_empty() {
 3539                cx.write_to_primary(ClipboardItem::new_string(text));
 3540            }
 3541        }
 3542
 3543        let selection_anchors = self.selections.disjoint_anchors_arc();
 3544
 3545        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3546            self.buffer.update(cx, |buffer, cx| {
 3547                buffer.set_active_selections(
 3548                    &selection_anchors,
 3549                    self.selections.line_mode(),
 3550                    self.cursor_shape,
 3551                    cx,
 3552                )
 3553            });
 3554        }
 3555        let display_map = self
 3556            .display_map
 3557            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3558        let buffer = display_map.buffer_snapshot();
 3559        if self.selections.count() == 1 {
 3560            self.add_selections_state = None;
 3561        }
 3562        self.select_next_state = None;
 3563        self.select_prev_state = None;
 3564        self.select_syntax_node_history.try_clear();
 3565        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3566        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3567        self.take_rename(false, window, cx);
 3568
 3569        let newest_selection = self.selections.newest_anchor();
 3570        let new_cursor_position = newest_selection.head();
 3571        let selection_start = newest_selection.start;
 3572
 3573        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3574            self.push_to_nav_history(
 3575                *old_cursor_position,
 3576                Some(new_cursor_position.to_point(buffer)),
 3577                false,
 3578                effects.nav_history == Some(true),
 3579                cx,
 3580            );
 3581        }
 3582
 3583        if local {
 3584            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3585                self.register_buffer(buffer_id, cx);
 3586            }
 3587
 3588            let mut context_menu = self.context_menu.borrow_mut();
 3589            let completion_menu = match context_menu.as_ref() {
 3590                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3591                Some(CodeContextMenu::CodeActions(_)) => {
 3592                    *context_menu = None;
 3593                    None
 3594                }
 3595                None => None,
 3596            };
 3597            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3598            drop(context_menu);
 3599
 3600            if effects.completions
 3601                && let Some(completion_position) = completion_position
 3602            {
 3603                let start_offset = selection_start.to_offset(buffer);
 3604                let position_matches = start_offset == completion_position.to_offset(buffer);
 3605                let continue_showing = if let Some((snap, ..)) =
 3606                    buffer.point_to_buffer_offset(completion_position)
 3607                    && !snap.capability.editable()
 3608                {
 3609                    false
 3610                } else if position_matches {
 3611                    if self.snippet_stack.is_empty() {
 3612                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3613                            == Some(CharKind::Word)
 3614                    } else {
 3615                        // Snippet choices can be shown even when the cursor is in whitespace.
 3616                        // Dismissing the menu with actions like backspace is handled by
 3617                        // invalidation regions.
 3618                        true
 3619                    }
 3620                } else {
 3621                    false
 3622                };
 3623
 3624                if continue_showing {
 3625                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3626                } else {
 3627                    self.hide_context_menu(window, cx);
 3628                }
 3629            }
 3630
 3631            hide_hover(self, cx);
 3632
 3633            if old_cursor_position.to_display_point(&display_map).row()
 3634                != new_cursor_position.to_display_point(&display_map).row()
 3635            {
 3636                self.available_code_actions.take();
 3637            }
 3638            self.refresh_code_actions(window, cx);
 3639            self.refresh_document_highlights(cx);
 3640            refresh_linked_ranges(self, window, cx);
 3641
 3642            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3643            self.refresh_matching_bracket_highlights(&display_map, cx);
 3644            self.refresh_outline_symbols_at_cursor(cx);
 3645            self.update_visible_edit_prediction(window, cx);
 3646            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3647            self.inline_blame_popover.take();
 3648            if self.git_blame_inline_enabled {
 3649                self.start_inline_blame_timer(window, cx);
 3650            }
 3651        }
 3652
 3653        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3654
 3655        if local && !self.suppress_selection_callback {
 3656            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3657                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3658                callback(cursor_position, window, cx);
 3659            }
 3660        }
 3661
 3662        cx.emit(EditorEvent::SelectionsChanged { local });
 3663
 3664        let selections = &self.selections.disjoint_anchors_arc();
 3665        if selections.len() == 1 {
 3666            cx.emit(SearchEvent::ActiveMatchChanged)
 3667        }
 3668        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3669            let inmemory_selections = selections
 3670                .iter()
 3671                .map(|s| {
 3672                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3673                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3674                })
 3675                .collect();
 3676            self.update_restoration_data(cx, |data| {
 3677                data.selections = inmemory_selections;
 3678            });
 3679
 3680            if WorkspaceSettings::get(None, cx).restore_on_startup
 3681                != RestoreOnStartupBehavior::EmptyTab
 3682                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3683            {
 3684                let snapshot = self.buffer().read(cx).snapshot(cx);
 3685                let selections = selections.clone();
 3686                let background_executor = cx.background_executor().clone();
 3687                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3688                self.serialize_selections = cx.background_spawn(async move {
 3689                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3690                    let db_selections = selections
 3691                        .iter()
 3692                        .map(|selection| {
 3693                            (
 3694                                selection.start.to_offset(&snapshot).0,
 3695                                selection.end.to_offset(&snapshot).0,
 3696                            )
 3697                        })
 3698                        .collect();
 3699
 3700                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3701                        .await
 3702                        .with_context(|| {
 3703                            format!(
 3704                                "persisting editor selections for editor {editor_id}, \
 3705                                workspace {workspace_id:?}"
 3706                            )
 3707                        })
 3708                        .log_err();
 3709                });
 3710            }
 3711        }
 3712
 3713        cx.notify();
 3714    }
 3715
 3716    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3717        use text::ToOffset as _;
 3718        use text::ToPoint as _;
 3719
 3720        if self.mode.is_minimap()
 3721            || WorkspaceSettings::get(None, cx).restore_on_startup
 3722                == RestoreOnStartupBehavior::EmptyTab
 3723        {
 3724            return;
 3725        }
 3726
 3727        if !self.buffer().read(cx).is_singleton() {
 3728            return;
 3729        }
 3730
 3731        let display_snapshot = self
 3732            .display_map
 3733            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3734        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3735            return;
 3736        };
 3737        let inmemory_folds = display_snapshot
 3738            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3739            .map(|fold| {
 3740                fold.range.start.text_anchor.to_point(&snapshot)
 3741                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3742            })
 3743            .collect();
 3744        self.update_restoration_data(cx, |data| {
 3745            data.folds = inmemory_folds;
 3746        });
 3747
 3748        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3749            return;
 3750        };
 3751
 3752        // Get file path for path-based fold storage (survives tab close)
 3753        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3754            project::File::from_dyn(buffer.read(cx).file())
 3755                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3756        }) else {
 3757            return;
 3758        };
 3759
 3760        let background_executor = cx.background_executor().clone();
 3761        const FINGERPRINT_LEN: usize = 32;
 3762        let db_folds = display_snapshot
 3763            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3764            .map(|fold| {
 3765                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3766                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3767
 3768                // Extract fingerprints - content at fold boundaries for validation on restore
 3769                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3770                // content that might change independently.
 3771                // start_fp: first min(32, fold_len) bytes of fold content
 3772                // end_fp: last min(32, fold_len) bytes of fold content
 3773                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3774                let fold_len = end - start;
 3775                let start_fp_end = snapshot
 3776                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3777                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3778                let end_fp_start = snapshot
 3779                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3780                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3781
 3782                (start, end, start_fp, end_fp)
 3783            })
 3784            .collect::<Vec<_>>();
 3785        self.serialize_folds = cx.background_spawn(async move {
 3786            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3787            if db_folds.is_empty() {
 3788                // No folds - delete any persisted folds for this file
 3789                DB.delete_file_folds(workspace_id, file_path)
 3790                    .await
 3791                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3792                    .log_err();
 3793            } else {
 3794                DB.save_file_folds(workspace_id, file_path, db_folds)
 3795                    .await
 3796                    .with_context(|| {
 3797                        format!("persisting file folds for workspace {workspace_id:?}")
 3798                    })
 3799                    .log_err();
 3800            }
 3801        });
 3802    }
 3803
 3804    pub fn sync_selections(
 3805        &mut self,
 3806        other: Entity<Editor>,
 3807        cx: &mut Context<Self>,
 3808    ) -> gpui::Subscription {
 3809        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3810        if !other_selections.is_empty() {
 3811            self.selections
 3812                .change_with(&self.display_snapshot(cx), |selections| {
 3813                    selections.select_anchors(other_selections);
 3814                });
 3815        }
 3816
 3817        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3818            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3819                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3820                if other_selections.is_empty() {
 3821                    return;
 3822                }
 3823                let snapshot = this.display_snapshot(cx);
 3824                this.selections.change_with(&snapshot, |selections| {
 3825                    selections.select_anchors(other_selections);
 3826                });
 3827            }
 3828        });
 3829
 3830        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3831            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3832                let these_selections = this.selections.disjoint_anchors().to_vec();
 3833                if these_selections.is_empty() {
 3834                    return;
 3835                }
 3836                other.update(cx, |other_editor, cx| {
 3837                    let snapshot = other_editor.display_snapshot(cx);
 3838                    other_editor
 3839                        .selections
 3840                        .change_with(&snapshot, |selections| {
 3841                            selections.select_anchors(these_selections);
 3842                        })
 3843                });
 3844            }
 3845        });
 3846
 3847        Subscription::join(other_subscription, this_subscription)
 3848    }
 3849
 3850    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3851        if self.buffer().read(cx).is_singleton() {
 3852            return;
 3853        }
 3854        let snapshot = self.buffer.read(cx).snapshot(cx);
 3855        let buffer_ids: HashSet<BufferId> = self
 3856            .selections
 3857            .disjoint_anchor_ranges()
 3858            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3859            .collect();
 3860        for buffer_id in buffer_ids {
 3861            self.unfold_buffer(buffer_id, cx);
 3862        }
 3863    }
 3864
 3865    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3866    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3867    /// effects of selection change occur at the end of the transaction.
 3868    pub fn change_selections<R>(
 3869        &mut self,
 3870        effects: SelectionEffects,
 3871        window: &mut Window,
 3872        cx: &mut Context<Self>,
 3873        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3874    ) -> R {
 3875        let snapshot = self.display_snapshot(cx);
 3876        if let Some(state) = &mut self.deferred_selection_effects_state {
 3877            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3878            state.effects.completions = effects.completions;
 3879            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3880            let (changed, result) = self.selections.change_with(&snapshot, change);
 3881            state.changed |= changed;
 3882            return result;
 3883        }
 3884        let mut state = DeferredSelectionEffectsState {
 3885            changed: false,
 3886            effects,
 3887            old_cursor_position: self.selections.newest_anchor().head(),
 3888            history_entry: SelectionHistoryEntry {
 3889                selections: self.selections.disjoint_anchors_arc(),
 3890                select_next_state: self.select_next_state.clone(),
 3891                select_prev_state: self.select_prev_state.clone(),
 3892                add_selections_state: self.add_selections_state.clone(),
 3893            },
 3894        };
 3895        let (changed, result) = self.selections.change_with(&snapshot, change);
 3896        state.changed = state.changed || changed;
 3897        if self.defer_selection_effects {
 3898            self.deferred_selection_effects_state = Some(state);
 3899        } else {
 3900            self.apply_selection_effects(state, window, cx);
 3901        }
 3902        result
 3903    }
 3904
 3905    /// Defers the effects of selection change, so that the effects of multiple calls to
 3906    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3907    /// to selection history and the state of popovers based on selection position aren't
 3908    /// erroneously updated.
 3909    pub fn with_selection_effects_deferred<R>(
 3910        &mut self,
 3911        window: &mut Window,
 3912        cx: &mut Context<Self>,
 3913        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3914    ) -> R {
 3915        let already_deferred = self.defer_selection_effects;
 3916        self.defer_selection_effects = true;
 3917        let result = update(self, window, cx);
 3918        if !already_deferred {
 3919            self.defer_selection_effects = false;
 3920            if let Some(state) = self.deferred_selection_effects_state.take() {
 3921                self.apply_selection_effects(state, window, cx);
 3922            }
 3923        }
 3924        result
 3925    }
 3926
 3927    fn apply_selection_effects(
 3928        &mut self,
 3929        state: DeferredSelectionEffectsState,
 3930        window: &mut Window,
 3931        cx: &mut Context<Self>,
 3932    ) {
 3933        if state.changed {
 3934            self.selection_history.push(state.history_entry);
 3935
 3936            if let Some(autoscroll) = state.effects.scroll {
 3937                self.request_autoscroll(autoscroll, cx);
 3938            }
 3939
 3940            let old_cursor_position = &state.old_cursor_position;
 3941
 3942            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3943
 3944            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3945                self.show_signature_help_auto(window, cx);
 3946            }
 3947        }
 3948    }
 3949
 3950    pub fn edit<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
 3961            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3962    }
 3963
 3964    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3965    where
 3966        I: IntoIterator<Item = (Range<S>, T)>,
 3967        S: ToOffset,
 3968        T: Into<Arc<str>>,
 3969    {
 3970        if self.read_only(cx) {
 3971            return;
 3972        }
 3973
 3974        self.buffer.update(cx, |buffer, cx| {
 3975            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3976        });
 3977    }
 3978
 3979    pub fn edit_with_block_indent<I, S, T>(
 3980        &mut self,
 3981        edits: I,
 3982        original_indent_columns: Vec<Option<u32>>,
 3983        cx: &mut Context<Self>,
 3984    ) where
 3985        I: IntoIterator<Item = (Range<S>, T)>,
 3986        S: ToOffset,
 3987        T: Into<Arc<str>>,
 3988    {
 3989        if self.read_only(cx) {
 3990            return;
 3991        }
 3992
 3993        self.buffer.update(cx, |buffer, cx| {
 3994            buffer.edit(
 3995                edits,
 3996                Some(AutoindentMode::Block {
 3997                    original_indent_columns,
 3998                }),
 3999                cx,
 4000            )
 4001        });
 4002    }
 4003
 4004    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 4005        self.hide_context_menu(window, cx);
 4006
 4007        match phase {
 4008            SelectPhase::Begin {
 4009                position,
 4010                add,
 4011                click_count,
 4012            } => self.begin_selection(position, add, click_count, window, cx),
 4013            SelectPhase::BeginColumnar {
 4014                position,
 4015                goal_column,
 4016                reset,
 4017                mode,
 4018            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4019            SelectPhase::Extend {
 4020                position,
 4021                click_count,
 4022            } => self.extend_selection(position, click_count, window, cx),
 4023            SelectPhase::Update {
 4024                position,
 4025                goal_column,
 4026                scroll_delta,
 4027            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4028            SelectPhase::End => self.end_selection(window, cx),
 4029        }
 4030    }
 4031
 4032    fn extend_selection(
 4033        &mut self,
 4034        position: DisplayPoint,
 4035        click_count: usize,
 4036        window: &mut Window,
 4037        cx: &mut Context<Self>,
 4038    ) {
 4039        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4040        let tail = self
 4041            .selections
 4042            .newest::<MultiBufferOffset>(&display_map)
 4043            .tail();
 4044        let click_count = click_count.max(match self.selections.select_mode() {
 4045            SelectMode::Character => 1,
 4046            SelectMode::Word(_) => 2,
 4047            SelectMode::Line(_) => 3,
 4048            SelectMode::All => 4,
 4049        });
 4050        self.begin_selection(position, false, click_count, window, cx);
 4051
 4052        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4053
 4054        let current_selection = match self.selections.select_mode() {
 4055            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4056            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4057        };
 4058
 4059        let mut pending_selection = self
 4060            .selections
 4061            .pending_anchor()
 4062            .cloned()
 4063            .expect("extend_selection not called with pending selection");
 4064
 4065        if pending_selection
 4066            .start
 4067            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4068            == Ordering::Greater
 4069        {
 4070            pending_selection.start = current_selection.start;
 4071        }
 4072        if pending_selection
 4073            .end
 4074            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4075            == Ordering::Less
 4076        {
 4077            pending_selection.end = current_selection.end;
 4078            pending_selection.reversed = true;
 4079        }
 4080
 4081        let mut pending_mode = self.selections.pending_mode().unwrap();
 4082        match &mut pending_mode {
 4083            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4084            _ => {}
 4085        }
 4086
 4087        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4088            SelectionEffects::scroll(Autoscroll::fit())
 4089        } else {
 4090            SelectionEffects::no_scroll()
 4091        };
 4092
 4093        self.change_selections(effects, window, cx, |s| {
 4094            s.set_pending(pending_selection.clone(), pending_mode);
 4095            s.set_is_extending(true);
 4096        });
 4097    }
 4098
 4099    fn begin_selection(
 4100        &mut self,
 4101        position: DisplayPoint,
 4102        add: bool,
 4103        click_count: usize,
 4104        window: &mut Window,
 4105        cx: &mut Context<Self>,
 4106    ) {
 4107        if !self.focus_handle.is_focused(window) {
 4108            self.last_focused_descendant = None;
 4109            window.focus(&self.focus_handle, cx);
 4110        }
 4111
 4112        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4113        let buffer = display_map.buffer_snapshot();
 4114        let position = display_map.clip_point(position, Bias::Left);
 4115
 4116        let start;
 4117        let end;
 4118        let mode;
 4119        let mut auto_scroll;
 4120        match click_count {
 4121            1 => {
 4122                start = buffer.anchor_before(position.to_point(&display_map));
 4123                end = start;
 4124                mode = SelectMode::Character;
 4125                auto_scroll = true;
 4126            }
 4127            2 => {
 4128                let position = display_map
 4129                    .clip_point(position, Bias::Left)
 4130                    .to_offset(&display_map, Bias::Left);
 4131                let (range, _) = buffer.surrounding_word(position, None);
 4132                start = buffer.anchor_before(range.start);
 4133                end = buffer.anchor_before(range.end);
 4134                mode = SelectMode::Word(start..end);
 4135                auto_scroll = true;
 4136            }
 4137            3 => {
 4138                let position = display_map
 4139                    .clip_point(position, Bias::Left)
 4140                    .to_point(&display_map);
 4141                let line_start = display_map.prev_line_boundary(position).0;
 4142                let next_line_start = buffer.clip_point(
 4143                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4144                    Bias::Left,
 4145                );
 4146                start = buffer.anchor_before(line_start);
 4147                end = buffer.anchor_before(next_line_start);
 4148                mode = SelectMode::Line(start..end);
 4149                auto_scroll = true;
 4150            }
 4151            _ => {
 4152                start = buffer.anchor_before(MultiBufferOffset(0));
 4153                end = buffer.anchor_before(buffer.len());
 4154                mode = SelectMode::All;
 4155                auto_scroll = false;
 4156            }
 4157        }
 4158        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4159
 4160        let point_to_delete: Option<usize> = {
 4161            let selected_points: Vec<Selection<Point>> =
 4162                self.selections.disjoint_in_range(start..end, &display_map);
 4163
 4164            if !add || click_count > 1 {
 4165                None
 4166            } else if !selected_points.is_empty() {
 4167                Some(selected_points[0].id)
 4168            } else {
 4169                let clicked_point_already_selected =
 4170                    self.selections.disjoint_anchors().iter().find(|selection| {
 4171                        selection.start.to_point(buffer) == start.to_point(buffer)
 4172                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4173                    });
 4174
 4175                clicked_point_already_selected.map(|selection| selection.id)
 4176            }
 4177        };
 4178
 4179        let selections_count = self.selections.count();
 4180        let effects = if auto_scroll {
 4181            SelectionEffects::default()
 4182        } else {
 4183            SelectionEffects::no_scroll()
 4184        };
 4185
 4186        self.change_selections(effects, window, cx, |s| {
 4187            if let Some(point_to_delete) = point_to_delete {
 4188                s.delete(point_to_delete);
 4189
 4190                if selections_count == 1 {
 4191                    s.set_pending_anchor_range(start..end, mode);
 4192                }
 4193            } else {
 4194                if !add {
 4195                    s.clear_disjoint();
 4196                }
 4197
 4198                s.set_pending_anchor_range(start..end, mode);
 4199            }
 4200        });
 4201    }
 4202
 4203    fn begin_columnar_selection(
 4204        &mut self,
 4205        position: DisplayPoint,
 4206        goal_column: u32,
 4207        reset: bool,
 4208        mode: ColumnarMode,
 4209        window: &mut Window,
 4210        cx: &mut Context<Self>,
 4211    ) {
 4212        if !self.focus_handle.is_focused(window) {
 4213            self.last_focused_descendant = None;
 4214            window.focus(&self.focus_handle, cx);
 4215        }
 4216
 4217        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4218
 4219        if reset {
 4220            let pointer_position = display_map
 4221                .buffer_snapshot()
 4222                .anchor_before(position.to_point(&display_map));
 4223
 4224            self.change_selections(
 4225                SelectionEffects::scroll(Autoscroll::newest()),
 4226                window,
 4227                cx,
 4228                |s| {
 4229                    s.clear_disjoint();
 4230                    s.set_pending_anchor_range(
 4231                        pointer_position..pointer_position,
 4232                        SelectMode::Character,
 4233                    );
 4234                },
 4235            );
 4236        };
 4237
 4238        let tail = self.selections.newest::<Point>(&display_map).tail();
 4239        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4240        self.columnar_selection_state = match mode {
 4241            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4242                selection_tail: selection_anchor,
 4243                display_point: if reset {
 4244                    if position.column() != goal_column {
 4245                        Some(DisplayPoint::new(position.row(), goal_column))
 4246                    } else {
 4247                        None
 4248                    }
 4249                } else {
 4250                    None
 4251                },
 4252            }),
 4253            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4254                selection_tail: selection_anchor,
 4255            }),
 4256        };
 4257
 4258        if !reset {
 4259            self.select_columns(position, goal_column, &display_map, window, cx);
 4260        }
 4261    }
 4262
 4263    fn update_selection(
 4264        &mut self,
 4265        position: DisplayPoint,
 4266        goal_column: u32,
 4267        scroll_delta: gpui::Point<f32>,
 4268        window: &mut Window,
 4269        cx: &mut Context<Self>,
 4270    ) {
 4271        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4272
 4273        if self.columnar_selection_state.is_some() {
 4274            self.select_columns(position, goal_column, &display_map, window, cx);
 4275        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4276            let buffer = display_map.buffer_snapshot();
 4277            let head;
 4278            let tail;
 4279            let mode = self.selections.pending_mode().unwrap();
 4280            match &mode {
 4281                SelectMode::Character => {
 4282                    head = position.to_point(&display_map);
 4283                    tail = pending.tail().to_point(buffer);
 4284                }
 4285                SelectMode::Word(original_range) => {
 4286                    let offset = display_map
 4287                        .clip_point(position, Bias::Left)
 4288                        .to_offset(&display_map, Bias::Left);
 4289                    let original_range = original_range.to_offset(buffer);
 4290
 4291                    let head_offset = if buffer.is_inside_word(offset, None)
 4292                        || original_range.contains(&offset)
 4293                    {
 4294                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4295                        if word_range.start < original_range.start {
 4296                            word_range.start
 4297                        } else {
 4298                            word_range.end
 4299                        }
 4300                    } else {
 4301                        offset
 4302                    };
 4303
 4304                    head = head_offset.to_point(buffer);
 4305                    if head_offset <= original_range.start {
 4306                        tail = original_range.end.to_point(buffer);
 4307                    } else {
 4308                        tail = original_range.start.to_point(buffer);
 4309                    }
 4310                }
 4311                SelectMode::Line(original_range) => {
 4312                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4313
 4314                    let position = display_map
 4315                        .clip_point(position, Bias::Left)
 4316                        .to_point(&display_map);
 4317                    let line_start = display_map.prev_line_boundary(position).0;
 4318                    let next_line_start = buffer.clip_point(
 4319                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4320                        Bias::Left,
 4321                    );
 4322
 4323                    if line_start < original_range.start {
 4324                        head = line_start
 4325                    } else {
 4326                        head = next_line_start
 4327                    }
 4328
 4329                    if head <= original_range.start {
 4330                        tail = original_range.end;
 4331                    } else {
 4332                        tail = original_range.start;
 4333                    }
 4334                }
 4335                SelectMode::All => {
 4336                    return;
 4337                }
 4338            };
 4339
 4340            if head < tail {
 4341                pending.start = buffer.anchor_before(head);
 4342                pending.end = buffer.anchor_before(tail);
 4343                pending.reversed = true;
 4344            } else {
 4345                pending.start = buffer.anchor_before(tail);
 4346                pending.end = buffer.anchor_before(head);
 4347                pending.reversed = false;
 4348            }
 4349
 4350            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4351                s.set_pending(pending.clone(), mode);
 4352            });
 4353        } else {
 4354            log::error!("update_selection dispatched with no pending selection");
 4355            return;
 4356        }
 4357
 4358        self.apply_scroll_delta(scroll_delta, window, cx);
 4359        cx.notify();
 4360    }
 4361
 4362    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4363        self.columnar_selection_state.take();
 4364        if let Some(pending_mode) = self.selections.pending_mode() {
 4365            let selections = self
 4366                .selections
 4367                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4368            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4369                s.select(selections);
 4370                s.clear_pending();
 4371                if s.is_extending() {
 4372                    s.set_is_extending(false);
 4373                } else {
 4374                    s.set_select_mode(pending_mode);
 4375                }
 4376            });
 4377        }
 4378    }
 4379
 4380    fn select_columns(
 4381        &mut self,
 4382        head: DisplayPoint,
 4383        goal_column: u32,
 4384        display_map: &DisplaySnapshot,
 4385        window: &mut Window,
 4386        cx: &mut Context<Self>,
 4387    ) {
 4388        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4389            return;
 4390        };
 4391
 4392        let tail = match columnar_state {
 4393            ColumnarSelectionState::FromMouse {
 4394                selection_tail,
 4395                display_point,
 4396            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4397            ColumnarSelectionState::FromSelection { selection_tail } => {
 4398                selection_tail.to_display_point(display_map)
 4399            }
 4400        };
 4401
 4402        let start_row = cmp::min(tail.row(), head.row());
 4403        let end_row = cmp::max(tail.row(), head.row());
 4404        let start_column = cmp::min(tail.column(), goal_column);
 4405        let end_column = cmp::max(tail.column(), goal_column);
 4406        let reversed = start_column < tail.column();
 4407
 4408        let selection_ranges = (start_row.0..=end_row.0)
 4409            .map(DisplayRow)
 4410            .filter_map(|row| {
 4411                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4412                    || start_column <= display_map.line_len(row))
 4413                    && !display_map.is_block_line(row)
 4414                {
 4415                    let start = display_map
 4416                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4417                        .to_point(display_map);
 4418                    let end = display_map
 4419                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4420                        .to_point(display_map);
 4421                    if reversed {
 4422                        Some(end..start)
 4423                    } else {
 4424                        Some(start..end)
 4425                    }
 4426                } else {
 4427                    None
 4428                }
 4429            })
 4430            .collect::<Vec<_>>();
 4431        if selection_ranges.is_empty() {
 4432            return;
 4433        }
 4434
 4435        let ranges = match columnar_state {
 4436            ColumnarSelectionState::FromMouse { .. } => {
 4437                let mut non_empty_ranges = selection_ranges
 4438                    .iter()
 4439                    .filter(|selection_range| selection_range.start != selection_range.end)
 4440                    .peekable();
 4441                if non_empty_ranges.peek().is_some() {
 4442                    non_empty_ranges.cloned().collect()
 4443                } else {
 4444                    selection_ranges
 4445                }
 4446            }
 4447            _ => selection_ranges,
 4448        };
 4449
 4450        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4451            s.select_ranges(ranges);
 4452        });
 4453        cx.notify();
 4454    }
 4455
 4456    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4457        self.selections
 4458            .all_adjusted(snapshot)
 4459            .iter()
 4460            .any(|selection| !selection.is_empty())
 4461    }
 4462
 4463    pub fn has_pending_nonempty_selection(&self) -> bool {
 4464        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4465            Some(Selection { start, end, .. }) => start != end,
 4466            None => false,
 4467        };
 4468
 4469        pending_nonempty_selection
 4470            || (self.columnar_selection_state.is_some()
 4471                && self.selections.disjoint_anchors().len() > 1)
 4472    }
 4473
 4474    pub fn has_pending_selection(&self) -> bool {
 4475        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4476    }
 4477
 4478    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4479        self.selection_mark_mode = false;
 4480        self.selection_drag_state = SelectionDragState::None;
 4481
 4482        if self.dismiss_menus_and_popups(true, window, cx) {
 4483            cx.notify();
 4484            return;
 4485        }
 4486        if self.clear_expanded_diff_hunks(cx) {
 4487            cx.notify();
 4488            return;
 4489        }
 4490        if self.show_git_blame_gutter {
 4491            self.show_git_blame_gutter = false;
 4492            cx.notify();
 4493            return;
 4494        }
 4495
 4496        if self.mode.is_full()
 4497            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4498        {
 4499            cx.notify();
 4500            return;
 4501        }
 4502
 4503        cx.propagate();
 4504    }
 4505
 4506    pub fn dismiss_menus_and_popups(
 4507        &mut self,
 4508        is_user_requested: bool,
 4509        window: &mut Window,
 4510        cx: &mut Context<Self>,
 4511    ) -> bool {
 4512        let mut dismissed = false;
 4513
 4514        dismissed |= self.take_rename(false, window, cx).is_some();
 4515        dismissed |= self.hide_blame_popover(true, cx);
 4516        dismissed |= hide_hover(self, cx);
 4517        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4518        dismissed |= self.hide_context_menu(window, cx).is_some();
 4519        dismissed |= self.mouse_context_menu.take().is_some();
 4520        dismissed |= is_user_requested
 4521            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4522        dismissed |= self.snippet_stack.pop().is_some();
 4523        if self.diff_review_drag_state.is_some() {
 4524            self.cancel_diff_review_drag(cx);
 4525            dismissed = true;
 4526        }
 4527        if !self.diff_review_overlays.is_empty() {
 4528            self.dismiss_all_diff_review_overlays(cx);
 4529            dismissed = true;
 4530        }
 4531
 4532        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4533            self.dismiss_diagnostics(cx);
 4534            dismissed = true;
 4535        }
 4536
 4537        dismissed
 4538    }
 4539
 4540    fn linked_editing_ranges_for(
 4541        &self,
 4542        selection: Range<text::Anchor>,
 4543        cx: &App,
 4544    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4545        if self.linked_edit_ranges.is_empty() {
 4546            return None;
 4547        }
 4548        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4549            selection.end.buffer_id.and_then(|end_buffer_id| {
 4550                if selection.start.buffer_id != Some(end_buffer_id) {
 4551                    return None;
 4552                }
 4553                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4554                let snapshot = buffer.read(cx).snapshot();
 4555                self.linked_edit_ranges
 4556                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4557                    .map(|ranges| (ranges, snapshot, buffer))
 4558            })?;
 4559        use text::ToOffset as TO;
 4560        // find offset from the start of current range to current cursor position
 4561        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4562
 4563        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4564        let start_difference = start_offset - start_byte_offset;
 4565        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4566        let end_difference = end_offset - start_byte_offset;
 4567
 4568        // Current range has associated linked ranges.
 4569        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4570        for range in linked_ranges.iter() {
 4571            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4572            let end_offset = start_offset + end_difference;
 4573            let start_offset = start_offset + start_difference;
 4574            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4575                continue;
 4576            }
 4577            if self.selections.disjoint_anchor_ranges().any(|s| {
 4578                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4579                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4580                {
 4581                    return false;
 4582                }
 4583                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4584                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4585            }) {
 4586                continue;
 4587            }
 4588            let start = buffer_snapshot.anchor_after(start_offset);
 4589            let end = buffer_snapshot.anchor_after(end_offset);
 4590            linked_edits
 4591                .entry(buffer.clone())
 4592                .or_default()
 4593                .push(start..end);
 4594        }
 4595        Some(linked_edits)
 4596    }
 4597
 4598    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4599        let text: Arc<str> = text.into();
 4600
 4601        if self.read_only(cx) {
 4602            return;
 4603        }
 4604
 4605        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4606
 4607        self.unfold_buffers_with_selections(cx);
 4608
 4609        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4610        let mut bracket_inserted = false;
 4611        let mut edits = Vec::new();
 4612        let mut linked_edits = LinkedEdits::new();
 4613        let mut new_selections = Vec::with_capacity(selections.len());
 4614        let mut new_autoclose_regions = Vec::new();
 4615        let snapshot = self.buffer.read(cx).read(cx);
 4616        let mut clear_linked_edit_ranges = false;
 4617        let mut all_selections_read_only = true;
 4618        let mut has_adjacent_edits = false;
 4619        let mut in_adjacent_group = false;
 4620
 4621        let mut regions = self
 4622            .selections_with_autoclose_regions(selections, &snapshot)
 4623            .peekable();
 4624
 4625        while let Some((selection, autoclose_region)) = regions.next() {
 4626            if snapshot
 4627                .point_to_buffer_point(selection.head())
 4628                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4629            {
 4630                continue;
 4631            }
 4632            if snapshot
 4633                .point_to_buffer_point(selection.tail())
 4634                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4635            {
 4636                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4637                continue;
 4638            }
 4639            all_selections_read_only = false;
 4640
 4641            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4642                // Determine if the inserted text matches the opening or closing
 4643                // bracket of any of this language's bracket pairs.
 4644                let mut bracket_pair = None;
 4645                let mut is_bracket_pair_start = false;
 4646                let mut is_bracket_pair_end = false;
 4647                if !text.is_empty() {
 4648                    let mut bracket_pair_matching_end = None;
 4649                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4650                    //  and they are removing the character that triggered IME popup.
 4651                    for (pair, enabled) in scope.brackets() {
 4652                        if !pair.close && !pair.surround {
 4653                            continue;
 4654                        }
 4655
 4656                        if enabled && pair.start.ends_with(text.as_ref()) {
 4657                            let prefix_len = pair.start.len() - text.len();
 4658                            let preceding_text_matches_prefix = prefix_len == 0
 4659                                || (selection.start.column >= (prefix_len as u32)
 4660                                    && snapshot.contains_str_at(
 4661                                        Point::new(
 4662                                            selection.start.row,
 4663                                            selection.start.column - (prefix_len as u32),
 4664                                        ),
 4665                                        &pair.start[..prefix_len],
 4666                                    ));
 4667                            if preceding_text_matches_prefix {
 4668                                bracket_pair = Some(pair.clone());
 4669                                is_bracket_pair_start = true;
 4670                                break;
 4671                            }
 4672                        }
 4673                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4674                        {
 4675                            // take first bracket pair matching end, but don't break in case a later bracket
 4676                            // pair matches start
 4677                            bracket_pair_matching_end = Some(pair.clone());
 4678                        }
 4679                    }
 4680                    if let Some(end) = bracket_pair_matching_end
 4681                        && bracket_pair.is_none()
 4682                    {
 4683                        bracket_pair = Some(end);
 4684                        is_bracket_pair_end = true;
 4685                    }
 4686                }
 4687
 4688                if let Some(bracket_pair) = bracket_pair {
 4689                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4690                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4691                    let auto_surround =
 4692                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4693                    if selection.is_empty() {
 4694                        if is_bracket_pair_start {
 4695                            // If the inserted text is a suffix of an opening bracket and the
 4696                            // selection is preceded by the rest of the opening bracket, then
 4697                            // insert the closing bracket.
 4698                            let following_text_allows_autoclose = snapshot
 4699                                .chars_at(selection.start)
 4700                                .next()
 4701                                .is_none_or(|c| scope.should_autoclose_before(c));
 4702
 4703                            let preceding_text_allows_autoclose = selection.start.column == 0
 4704                                || snapshot
 4705                                    .reversed_chars_at(selection.start)
 4706                                    .next()
 4707                                    .is_none_or(|c| {
 4708                                        bracket_pair.start != bracket_pair.end
 4709                                            || !snapshot
 4710                                                .char_classifier_at(selection.start)
 4711                                                .is_word(c)
 4712                                    });
 4713
 4714                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4715                                && bracket_pair.start.len() == 1
 4716                            {
 4717                                let target = bracket_pair.start.chars().next().unwrap();
 4718                                let mut byte_offset = 0u32;
 4719                                let current_line_count = snapshot
 4720                                    .reversed_chars_at(selection.start)
 4721                                    .take_while(|&c| c != '\n')
 4722                                    .filter(|c| {
 4723                                        byte_offset += c.len_utf8() as u32;
 4724                                        if *c != target {
 4725                                            return false;
 4726                                        }
 4727
 4728                                        let point = Point::new(
 4729                                            selection.start.row,
 4730                                            selection.start.column.saturating_sub(byte_offset),
 4731                                        );
 4732
 4733                                        let is_enabled = snapshot
 4734                                            .language_scope_at(point)
 4735                                            .and_then(|scope| {
 4736                                                scope
 4737                                                    .brackets()
 4738                                                    .find(|(pair, _)| {
 4739                                                        pair.start == bracket_pair.start
 4740                                                    })
 4741                                                    .map(|(_, enabled)| enabled)
 4742                                            })
 4743                                            .unwrap_or(true);
 4744
 4745                                        let is_delimiter = snapshot
 4746                                            .language_scope_at(Point::new(
 4747                                                point.row,
 4748                                                point.column + 1,
 4749                                            ))
 4750                                            .and_then(|scope| {
 4751                                                scope
 4752                                                    .brackets()
 4753                                                    .find(|(pair, _)| {
 4754                                                        pair.start == bracket_pair.start
 4755                                                    })
 4756                                                    .map(|(_, enabled)| !enabled)
 4757                                            })
 4758                                            .unwrap_or(false);
 4759
 4760                                        is_enabled && !is_delimiter
 4761                                    })
 4762                                    .count();
 4763                                current_line_count % 2 == 1
 4764                            } else {
 4765                                false
 4766                            };
 4767
 4768                            if autoclose
 4769                                && bracket_pair.close
 4770                                && following_text_allows_autoclose
 4771                                && preceding_text_allows_autoclose
 4772                                && !is_closing_quote
 4773                            {
 4774                                let anchor = snapshot.anchor_before(selection.end);
 4775                                new_selections.push((selection.map(|_| anchor), text.len()));
 4776                                new_autoclose_regions.push((
 4777                                    anchor,
 4778                                    text.len(),
 4779                                    selection.id,
 4780                                    bracket_pair.clone(),
 4781                                ));
 4782                                edits.push((
 4783                                    selection.range(),
 4784                                    format!("{}{}", text, bracket_pair.end).into(),
 4785                                ));
 4786                                bracket_inserted = true;
 4787                                continue;
 4788                            }
 4789                        }
 4790
 4791                        if let Some(region) = autoclose_region {
 4792                            // If the selection is followed by an auto-inserted closing bracket,
 4793                            // then don't insert that closing bracket again; just move the selection
 4794                            // past the closing bracket.
 4795                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4796                                && text.as_ref() == region.pair.end.as_str()
 4797                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4798                            if should_skip {
 4799                                let anchor = snapshot.anchor_after(selection.end);
 4800                                new_selections
 4801                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4802                                continue;
 4803                            }
 4804                        }
 4805
 4806                        let always_treat_brackets_as_autoclosed = snapshot
 4807                            .language_settings_at(selection.start, cx)
 4808                            .always_treat_brackets_as_autoclosed;
 4809                        if always_treat_brackets_as_autoclosed
 4810                            && is_bracket_pair_end
 4811                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4812                        {
 4813                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4814                            // and the inserted text is a closing bracket and the selection is followed
 4815                            // by the closing bracket then move the selection past the closing bracket.
 4816                            let anchor = snapshot.anchor_after(selection.end);
 4817                            new_selections.push((selection.map(|_| anchor), text.len()));
 4818                            continue;
 4819                        }
 4820                    }
 4821                    // If an opening bracket is 1 character long and is typed while
 4822                    // text is selected, then surround that text with the bracket pair.
 4823                    else if auto_surround
 4824                        && bracket_pair.surround
 4825                        && is_bracket_pair_start
 4826                        && bracket_pair.start.chars().count() == 1
 4827                    {
 4828                        edits.push((selection.start..selection.start, text.clone()));
 4829                        edits.push((
 4830                            selection.end..selection.end,
 4831                            bracket_pair.end.as_str().into(),
 4832                        ));
 4833                        bracket_inserted = true;
 4834                        new_selections.push((
 4835                            Selection {
 4836                                id: selection.id,
 4837                                start: snapshot.anchor_after(selection.start),
 4838                                end: snapshot.anchor_before(selection.end),
 4839                                reversed: selection.reversed,
 4840                                goal: selection.goal,
 4841                            },
 4842                            0,
 4843                        ));
 4844                        continue;
 4845                    }
 4846                }
 4847            }
 4848
 4849            if self.auto_replace_emoji_shortcode
 4850                && selection.is_empty()
 4851                && text.as_ref().ends_with(':')
 4852                && let Some(possible_emoji_short_code) =
 4853                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4854                && !possible_emoji_short_code.is_empty()
 4855                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4856            {
 4857                let emoji_shortcode_start = Point::new(
 4858                    selection.start.row,
 4859                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4860                );
 4861
 4862                // Remove shortcode from buffer
 4863                edits.push((
 4864                    emoji_shortcode_start..selection.start,
 4865                    "".to_string().into(),
 4866                ));
 4867                new_selections.push((
 4868                    Selection {
 4869                        id: selection.id,
 4870                        start: snapshot.anchor_after(emoji_shortcode_start),
 4871                        end: snapshot.anchor_before(selection.start),
 4872                        reversed: selection.reversed,
 4873                        goal: selection.goal,
 4874                    },
 4875                    0,
 4876                ));
 4877
 4878                // Insert emoji
 4879                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4880                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4881                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4882
 4883                continue;
 4884            }
 4885
 4886            let next_is_adjacent = regions
 4887                .peek()
 4888                .is_some_and(|(next, _)| selection.end == next.start);
 4889
 4890            // If not handling any auto-close operation, then just replace the selected
 4891            // text with the given input and move the selection to the end of the
 4892            // newly inserted text.
 4893            let anchor = if in_adjacent_group || next_is_adjacent {
 4894                // After edits the right bias would shift those anchor to the next visible fragment
 4895                // but we want to resolve to the previous one
 4896                snapshot.anchor_before(selection.end)
 4897            } else {
 4898                snapshot.anchor_after(selection.end)
 4899            };
 4900
 4901            if !self.linked_edit_ranges.is_empty() {
 4902                let start_anchor = snapshot.anchor_before(selection.start);
 4903
 4904                let is_word_char = text.chars().next().is_none_or(|char| {
 4905                    let classifier = snapshot
 4906                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4907                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4908                    classifier.is_word(char)
 4909                });
 4910                let is_dot = text.as_ref() == ".";
 4911                let should_apply_linked_edit = is_word_char || is_dot;
 4912
 4913                if should_apply_linked_edit {
 4914                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4915                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4916                } else {
 4917                    clear_linked_edit_ranges = true;
 4918                }
 4919            }
 4920
 4921            new_selections.push((selection.map(|_| anchor), 0));
 4922            edits.push((selection.start..selection.end, text.clone()));
 4923
 4924            has_adjacent_edits |= next_is_adjacent;
 4925            in_adjacent_group = next_is_adjacent;
 4926        }
 4927
 4928        if all_selections_read_only {
 4929            return;
 4930        }
 4931
 4932        drop(regions);
 4933        drop(snapshot);
 4934
 4935        self.transact(window, cx, |this, window, cx| {
 4936            if clear_linked_edit_ranges {
 4937                this.linked_edit_ranges.clear();
 4938            }
 4939            let initial_buffer_versions =
 4940                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4941
 4942            this.buffer.update(cx, |buffer, cx| {
 4943                if has_adjacent_edits {
 4944                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4945                } else {
 4946                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4947                }
 4948            });
 4949            linked_edits.apply(cx);
 4950            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4951            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4952            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4953            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4954                new_anchor_selections,
 4955                &map,
 4956            )
 4957            .zip(new_selection_deltas)
 4958            .map(|(selection, delta)| Selection {
 4959                id: selection.id,
 4960                start: selection.start + delta,
 4961                end: selection.end + delta,
 4962                reversed: selection.reversed,
 4963                goal: SelectionGoal::None,
 4964            })
 4965            .collect::<Vec<_>>();
 4966
 4967            let mut i = 0;
 4968            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4969                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4970                let start = map.buffer_snapshot().anchor_before(position);
 4971                let end = map.buffer_snapshot().anchor_after(position);
 4972                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4973                    match existing_state
 4974                        .range
 4975                        .start
 4976                        .cmp(&start, map.buffer_snapshot())
 4977                    {
 4978                        Ordering::Less => i += 1,
 4979                        Ordering::Greater => break,
 4980                        Ordering::Equal => {
 4981                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4982                                Ordering::Less => i += 1,
 4983                                Ordering::Equal => break,
 4984                                Ordering::Greater => break,
 4985                            }
 4986                        }
 4987                    }
 4988                }
 4989                this.autoclose_regions.insert(
 4990                    i,
 4991                    AutocloseRegion {
 4992                        selection_id,
 4993                        range: start..end,
 4994                        pair,
 4995                    },
 4996                );
 4997            }
 4998
 4999            let had_active_edit_prediction = this.has_active_edit_prediction();
 5000            this.change_selections(
 5001                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 5002                window,
 5003                cx,
 5004                |s| s.select(new_selections),
 5005            );
 5006
 5007            if !bracket_inserted
 5008                && let Some(on_type_format_task) =
 5009                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5010            {
 5011                on_type_format_task.detach_and_log_err(cx);
 5012            }
 5013
 5014            let editor_settings = EditorSettings::get_global(cx);
 5015            if bracket_inserted
 5016                && (editor_settings.auto_signature_help
 5017                    || editor_settings.show_signature_help_after_edits)
 5018            {
 5019                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5020            }
 5021
 5022            let trigger_in_words =
 5023                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5024            if this.hard_wrap.is_some() {
 5025                let latest: Range<Point> = this.selections.newest(&map).range();
 5026                if latest.is_empty()
 5027                    && this
 5028                        .buffer()
 5029                        .read(cx)
 5030                        .snapshot(cx)
 5031                        .line_len(MultiBufferRow(latest.start.row))
 5032                        == latest.start.column
 5033                {
 5034                    this.rewrap_impl(
 5035                        RewrapOptions {
 5036                            override_language_settings: true,
 5037                            preserve_existing_whitespace: true,
 5038                        },
 5039                        cx,
 5040                    )
 5041                }
 5042            }
 5043            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5044            refresh_linked_ranges(this, window, cx);
 5045            this.refresh_edit_prediction(true, false, window, cx);
 5046            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5047        });
 5048    }
 5049
 5050    fn find_possible_emoji_shortcode_at_position(
 5051        snapshot: &MultiBufferSnapshot,
 5052        position: Point,
 5053    ) -> Option<String> {
 5054        let mut chars = Vec::new();
 5055        let mut found_colon = false;
 5056        for char in snapshot.reversed_chars_at(position).take(100) {
 5057            // Found a possible emoji shortcode in the middle of the buffer
 5058            if found_colon {
 5059                if char.is_whitespace() {
 5060                    chars.reverse();
 5061                    return Some(chars.iter().collect());
 5062                }
 5063                // If the previous character is not a whitespace, we are in the middle of a word
 5064                // and we only want to complete the shortcode if the word is made up of other emojis
 5065                let mut containing_word = String::new();
 5066                for ch in snapshot
 5067                    .reversed_chars_at(position)
 5068                    .skip(chars.len() + 1)
 5069                    .take(100)
 5070                {
 5071                    if ch.is_whitespace() {
 5072                        break;
 5073                    }
 5074                    containing_word.push(ch);
 5075                }
 5076                let containing_word = containing_word.chars().rev().collect::<String>();
 5077                if util::word_consists_of_emojis(containing_word.as_str()) {
 5078                    chars.reverse();
 5079                    return Some(chars.iter().collect());
 5080                }
 5081            }
 5082
 5083            if char.is_whitespace() || !char.is_ascii() {
 5084                return None;
 5085            }
 5086            if char == ':' {
 5087                found_colon = true;
 5088            } else {
 5089                chars.push(char);
 5090            }
 5091        }
 5092        // Found a possible emoji shortcode at the beginning of the buffer
 5093        chars.reverse();
 5094        Some(chars.iter().collect())
 5095    }
 5096
 5097    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5098        if self.read_only(cx) {
 5099            return;
 5100        }
 5101
 5102        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5103        self.transact(window, cx, |this, window, cx| {
 5104            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5105                let selections = this
 5106                    .selections
 5107                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5108                let multi_buffer = this.buffer.read(cx);
 5109                let buffer = multi_buffer.snapshot(cx);
 5110                selections
 5111                    .iter()
 5112                    .map(|selection| {
 5113                        let start_point = selection.start.to_point(&buffer);
 5114                        let mut existing_indent =
 5115                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5116                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5117                        let start = selection.start;
 5118                        let end = selection.end;
 5119                        let selection_is_empty = start == end;
 5120                        let language_scope = buffer.language_scope_at(start);
 5121                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5122                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5123                                &buffer,
 5124                                start..end,
 5125                                language,
 5126                            )
 5127                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5128                                    &buffer,
 5129                                    start..end,
 5130                                );
 5131
 5132                            let mut newline_config = NewlineConfig::Newline {
 5133                                additional_indent: IndentSize::spaces(0),
 5134                                extra_line_additional_indent: if needs_extra_newline {
 5135                                    Some(IndentSize::spaces(0))
 5136                                } else {
 5137                                    None
 5138                                },
 5139                                prevent_auto_indent: false,
 5140                            };
 5141
 5142                            let comment_delimiter = maybe!({
 5143                                if !selection_is_empty {
 5144                                    return None;
 5145                                }
 5146
 5147                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5148                                    return None;
 5149                                }
 5150
 5151                                return comment_delimiter_for_newline(
 5152                                    &start_point,
 5153                                    &buffer,
 5154                                    language,
 5155                                );
 5156                            });
 5157
 5158                            let doc_delimiter = maybe!({
 5159                                if !selection_is_empty {
 5160                                    return None;
 5161                                }
 5162
 5163                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5164                                    return None;
 5165                                }
 5166
 5167                                return documentation_delimiter_for_newline(
 5168                                    &start_point,
 5169                                    &buffer,
 5170                                    language,
 5171                                    &mut newline_config,
 5172                                );
 5173                            });
 5174
 5175                            let list_delimiter = maybe!({
 5176                                if !selection_is_empty {
 5177                                    return None;
 5178                                }
 5179
 5180                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5181                                    return None;
 5182                                }
 5183
 5184                                return list_delimiter_for_newline(
 5185                                    &start_point,
 5186                                    &buffer,
 5187                                    language,
 5188                                    &mut newline_config,
 5189                                );
 5190                            });
 5191
 5192                            (
 5193                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5194                                newline_config,
 5195                            )
 5196                        } else {
 5197                            (
 5198                                None,
 5199                                NewlineConfig::Newline {
 5200                                    additional_indent: IndentSize::spaces(0),
 5201                                    extra_line_additional_indent: None,
 5202                                    prevent_auto_indent: false,
 5203                                },
 5204                            )
 5205                        };
 5206
 5207                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5208                            NewlineConfig::ClearCurrentLine => {
 5209                                let row_start =
 5210                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5211                                (row_start, String::new(), false)
 5212                            }
 5213                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5214                                let row_start =
 5215                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5216                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5217                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5218                                let reduced_indent =
 5219                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5220                                let mut new_text = String::new();
 5221                                new_text.extend(reduced_indent.chars());
 5222                                new_text.push_str(continuation);
 5223                                (row_start, new_text, true)
 5224                            }
 5225                            NewlineConfig::Newline {
 5226                                additional_indent,
 5227                                extra_line_additional_indent,
 5228                                prevent_auto_indent,
 5229                            } => {
 5230                                let auto_indent_mode =
 5231                                    buffer.language_settings_at(start, cx).auto_indent;
 5232                                let preserve_indent =
 5233                                    auto_indent_mode != language::AutoIndentMode::None;
 5234                                let apply_syntax_indent =
 5235                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5236                                let capacity_for_delimiter =
 5237                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5238                                let existing_indent_len = if preserve_indent {
 5239                                    existing_indent.len as usize
 5240                                } else {
 5241                                    0
 5242                                };
 5243                                let extra_line_len = extra_line_additional_indent
 5244                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5245                                    .unwrap_or(0);
 5246                                let mut new_text = String::with_capacity(
 5247                                    1 + capacity_for_delimiter
 5248                                        + existing_indent_len
 5249                                        + additional_indent.len as usize
 5250                                        + extra_line_len,
 5251                                );
 5252                                new_text.push('\n');
 5253                                if preserve_indent {
 5254                                    new_text.extend(existing_indent.chars());
 5255                                }
 5256                                new_text.extend(additional_indent.chars());
 5257                                if let Some(delimiter) = &delimiter {
 5258                                    new_text.push_str(delimiter);
 5259                                }
 5260                                if let Some(extra_indent) = extra_line_additional_indent {
 5261                                    new_text.push('\n');
 5262                                    if preserve_indent {
 5263                                        new_text.extend(existing_indent.chars());
 5264                                    }
 5265                                    new_text.extend(extra_indent.chars());
 5266                                }
 5267                                (
 5268                                    start,
 5269                                    new_text,
 5270                                    *prevent_auto_indent || !apply_syntax_indent,
 5271                                )
 5272                            }
 5273                        };
 5274
 5275                        let anchor = buffer.anchor_after(end);
 5276                        let new_selection = selection.map(|_| anchor);
 5277                        (
 5278                            ((edit_start..end, new_text), prevent_auto_indent),
 5279                            (newline_config.has_extra_line(), new_selection),
 5280                        )
 5281                    })
 5282                    .unzip()
 5283            };
 5284
 5285            let mut auto_indent_edits = Vec::new();
 5286            let mut edits = Vec::new();
 5287            for (edit, prevent_auto_indent) in edits_with_flags {
 5288                if prevent_auto_indent {
 5289                    edits.push(edit);
 5290                } else {
 5291                    auto_indent_edits.push(edit);
 5292                }
 5293            }
 5294            if !edits.is_empty() {
 5295                this.edit(edits, cx);
 5296            }
 5297            if !auto_indent_edits.is_empty() {
 5298                this.edit_with_autoindent(auto_indent_edits, cx);
 5299            }
 5300
 5301            let buffer = this.buffer.read(cx).snapshot(cx);
 5302            let new_selections = selection_info
 5303                .into_iter()
 5304                .map(|(extra_newline_inserted, new_selection)| {
 5305                    let mut cursor = new_selection.end.to_point(&buffer);
 5306                    if extra_newline_inserted {
 5307                        cursor.row -= 1;
 5308                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5309                    }
 5310                    new_selection.map(|_| cursor)
 5311                })
 5312                .collect();
 5313
 5314            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5315            this.refresh_edit_prediction(true, false, window, cx);
 5316            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5317                task.detach_and_log_err(cx);
 5318            }
 5319        });
 5320    }
 5321
 5322    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5323        if self.read_only(cx) {
 5324            return;
 5325        }
 5326
 5327        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5328
 5329        let buffer = self.buffer.read(cx);
 5330        let snapshot = buffer.snapshot(cx);
 5331
 5332        let mut edits = Vec::new();
 5333        let mut rows = Vec::new();
 5334
 5335        for (rows_inserted, selection) in self
 5336            .selections
 5337            .all_adjusted(&self.display_snapshot(cx))
 5338            .into_iter()
 5339            .enumerate()
 5340        {
 5341            let cursor = selection.head();
 5342            let row = cursor.row;
 5343
 5344            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5345
 5346            let newline = "\n".to_string();
 5347            edits.push((start_of_line..start_of_line, newline));
 5348
 5349            rows.push(row + rows_inserted as u32);
 5350        }
 5351
 5352        self.transact(window, cx, |editor, window, cx| {
 5353            editor.edit(edits, cx);
 5354
 5355            editor.change_selections(Default::default(), window, cx, |s| {
 5356                let mut index = 0;
 5357                s.move_cursors_with(&mut |map, _, _| {
 5358                    let row = rows[index];
 5359                    index += 1;
 5360
 5361                    let point = Point::new(row, 0);
 5362                    let boundary = map.next_line_boundary(point).1;
 5363                    let clipped = map.clip_point(boundary, Bias::Left);
 5364
 5365                    (clipped, SelectionGoal::None)
 5366                });
 5367            });
 5368
 5369            let mut indent_edits = Vec::new();
 5370            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5371            for row in rows {
 5372                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5373                for (row, indent) in indents {
 5374                    if indent.len == 0 {
 5375                        continue;
 5376                    }
 5377
 5378                    let text = match indent.kind {
 5379                        IndentKind::Space => " ".repeat(indent.len as usize),
 5380                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5381                    };
 5382                    let point = Point::new(row.0, 0);
 5383                    indent_edits.push((point..point, text));
 5384                }
 5385            }
 5386            editor.edit(indent_edits, cx);
 5387            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5388                format.detach_and_log_err(cx);
 5389            }
 5390        });
 5391    }
 5392
 5393    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5394        if self.read_only(cx) {
 5395            return;
 5396        }
 5397
 5398        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5399
 5400        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5401        let mut rows = Vec::new();
 5402        let mut rows_inserted = 0;
 5403
 5404        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5405            let cursor = selection.head();
 5406            let row = cursor.row;
 5407
 5408            let point = Point::new(row, 0);
 5409            let Some((buffer_handle, buffer_point, _)) =
 5410                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5411            else {
 5412                continue;
 5413            };
 5414
 5415            buffer_edits
 5416                .entry(buffer_handle.entity_id())
 5417                .or_insert_with(|| (buffer_handle, Vec::new()))
 5418                .1
 5419                .push(buffer_point);
 5420
 5421            rows_inserted += 1;
 5422            rows.push(row + rows_inserted);
 5423        }
 5424
 5425        self.transact(window, cx, |editor, window, cx| {
 5426            for (_, (buffer_handle, points)) in &buffer_edits {
 5427                buffer_handle.update(cx, |buffer, cx| {
 5428                    let edits: Vec<_> = points
 5429                        .iter()
 5430                        .map(|point| {
 5431                            let target = Point::new(point.row + 1, 0);
 5432                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5433                            (start_of_line..start_of_line, "\n")
 5434                        })
 5435                        .collect();
 5436                    buffer.edit(edits, None, cx);
 5437                });
 5438            }
 5439
 5440            editor.change_selections(Default::default(), window, cx, |s| {
 5441                let mut index = 0;
 5442                s.move_cursors_with(&mut |map, _, _| {
 5443                    let row = rows[index];
 5444                    index += 1;
 5445
 5446                    let point = Point::new(row, 0);
 5447                    let boundary = map.next_line_boundary(point).1;
 5448                    let clipped = map.clip_point(boundary, Bias::Left);
 5449
 5450                    (clipped, SelectionGoal::None)
 5451                });
 5452            });
 5453
 5454            let mut indent_edits = Vec::new();
 5455            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5456            for row in rows {
 5457                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5458                for (row, indent) in indents {
 5459                    if indent.len == 0 {
 5460                        continue;
 5461                    }
 5462
 5463                    let text = match indent.kind {
 5464                        IndentKind::Space => " ".repeat(indent.len as usize),
 5465                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5466                    };
 5467                    let point = Point::new(row.0, 0);
 5468                    indent_edits.push((point..point, text));
 5469                }
 5470            }
 5471            editor.edit(indent_edits, cx);
 5472            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5473                format.detach_and_log_err(cx);
 5474            }
 5475        });
 5476    }
 5477
 5478    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5479        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5480            original_indent_columns: Vec::new(),
 5481        });
 5482        self.replace_selections(text, autoindent, window, cx, false);
 5483    }
 5484
 5485    /// Replaces the editor's selections with the provided `text`, applying the
 5486    /// given `autoindent_mode` (`None` will skip autoindentation).
 5487    ///
 5488    /// Early returns if the editor is in read-only mode, without applying any
 5489    /// edits.
 5490    fn replace_selections(
 5491        &mut self,
 5492        text: &str,
 5493        autoindent_mode: Option<AutoindentMode>,
 5494        window: &mut Window,
 5495        cx: &mut Context<Self>,
 5496        apply_linked_edits: bool,
 5497    ) {
 5498        if self.read_only(cx) {
 5499            return;
 5500        }
 5501
 5502        let text: Arc<str> = text.into();
 5503        self.transact(window, cx, |this, window, cx| {
 5504            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5505            let linked_edits = if apply_linked_edits {
 5506                this.linked_edits_for_selections(text.clone(), cx)
 5507            } else {
 5508                LinkedEdits::new()
 5509            };
 5510
 5511            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5512                let anchors = {
 5513                    let snapshot = buffer.read(cx);
 5514                    old_selections
 5515                        .iter()
 5516                        .map(|s| {
 5517                            let anchor = snapshot.anchor_after(s.head());
 5518                            s.map(|_| anchor)
 5519                        })
 5520                        .collect::<Vec<_>>()
 5521                };
 5522                buffer.edit(
 5523                    old_selections
 5524                        .iter()
 5525                        .map(|s| (s.start..s.end, text.clone())),
 5526                    autoindent_mode,
 5527                    cx,
 5528                );
 5529                anchors
 5530            });
 5531
 5532            linked_edits.apply(cx);
 5533
 5534            this.change_selections(Default::default(), window, cx, |s| {
 5535                s.select_anchors(selection_anchors);
 5536            });
 5537
 5538            if apply_linked_edits {
 5539                refresh_linked_ranges(this, window, cx);
 5540            }
 5541
 5542            cx.notify();
 5543        });
 5544    }
 5545
 5546    /// Collects linked edits for the current selections, pairing each linked
 5547    /// range with `text`.
 5548    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5549        let mut linked_edits = LinkedEdits::new();
 5550        if !self.linked_edit_ranges.is_empty() {
 5551            for selection in self.selections.disjoint_anchors() {
 5552                let start = selection.start.text_anchor;
 5553                let end = selection.end.text_anchor;
 5554                linked_edits.push(self, start..end, text.clone(), cx);
 5555            }
 5556        }
 5557        linked_edits
 5558    }
 5559
 5560    /// Deletes the content covered by the current selections and applies
 5561    /// linked edits.
 5562    pub fn delete_selections_with_linked_edits(
 5563        &mut self,
 5564        window: &mut Window,
 5565        cx: &mut Context<Self>,
 5566    ) {
 5567        self.replace_selections("", None, window, cx, true);
 5568    }
 5569
 5570    #[cfg(any(test, feature = "test-support"))]
 5571    pub fn set_linked_edit_ranges_for_testing(
 5572        &mut self,
 5573        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5574        cx: &mut Context<Self>,
 5575    ) -> Option<()> {
 5576        let Some((buffer, _)) = self
 5577            .buffer
 5578            .read(cx)
 5579            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5580        else {
 5581            return None;
 5582        };
 5583        let buffer = buffer.read(cx);
 5584        let buffer_id = buffer.remote_id();
 5585        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5586        for (base_range, linked_ranges_points) in ranges {
 5587            let base_anchor =
 5588                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5589            let linked_anchors = linked_ranges_points
 5590                .into_iter()
 5591                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5592                .collect();
 5593            linked_ranges.push((base_anchor, linked_anchors));
 5594        }
 5595        let mut map = HashMap::default();
 5596        map.insert(buffer_id, linked_ranges);
 5597        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5598        Some(())
 5599    }
 5600
 5601    fn trigger_completion_on_input(
 5602        &mut self,
 5603        text: &str,
 5604        trigger_in_words: bool,
 5605        window: &mut Window,
 5606        cx: &mut Context<Self>,
 5607    ) {
 5608        let completions_source = self
 5609            .context_menu
 5610            .borrow()
 5611            .as_ref()
 5612            .and_then(|menu| match menu {
 5613                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5614                CodeContextMenu::CodeActions(_) => None,
 5615            });
 5616
 5617        match completions_source {
 5618            Some(CompletionsMenuSource::Words { .. }) => {
 5619                self.open_or_update_completions_menu(
 5620                    Some(CompletionsMenuSource::Words {
 5621                        ignore_threshold: false,
 5622                    }),
 5623                    None,
 5624                    trigger_in_words,
 5625                    window,
 5626                    cx,
 5627                );
 5628            }
 5629            _ => self.open_or_update_completions_menu(
 5630                None,
 5631                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5632                true,
 5633                window,
 5634                cx,
 5635            ),
 5636        }
 5637    }
 5638
 5639    /// If any empty selections is touching the start of its innermost containing autoclose
 5640    /// region, expand it to select the brackets.
 5641    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5642        let selections = self
 5643            .selections
 5644            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5645        let buffer = self.buffer.read(cx).read(cx);
 5646        let new_selections = self
 5647            .selections_with_autoclose_regions(selections, &buffer)
 5648            .map(|(mut selection, region)| {
 5649                if !selection.is_empty() {
 5650                    return selection;
 5651                }
 5652
 5653                if let Some(region) = region {
 5654                    let mut range = region.range.to_offset(&buffer);
 5655                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5656                        range.start -= region.pair.start.len();
 5657                        if buffer.contains_str_at(range.start, &region.pair.start)
 5658                            && buffer.contains_str_at(range.end, &region.pair.end)
 5659                        {
 5660                            range.end += region.pair.end.len();
 5661                            selection.start = range.start;
 5662                            selection.end = range.end;
 5663
 5664                            return selection;
 5665                        }
 5666                    }
 5667                }
 5668
 5669                let always_treat_brackets_as_autoclosed = buffer
 5670                    .language_settings_at(selection.start, cx)
 5671                    .always_treat_brackets_as_autoclosed;
 5672
 5673                if !always_treat_brackets_as_autoclosed {
 5674                    return selection;
 5675                }
 5676
 5677                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5678                    for (pair, enabled) in scope.brackets() {
 5679                        if !enabled || !pair.close {
 5680                            continue;
 5681                        }
 5682
 5683                        if buffer.contains_str_at(selection.start, &pair.end) {
 5684                            let pair_start_len = pair.start.len();
 5685                            if buffer.contains_str_at(
 5686                                selection.start.saturating_sub_usize(pair_start_len),
 5687                                &pair.start,
 5688                            ) {
 5689                                selection.start -= pair_start_len;
 5690                                selection.end += pair.end.len();
 5691
 5692                                return selection;
 5693                            }
 5694                        }
 5695                    }
 5696                }
 5697
 5698                selection
 5699            })
 5700            .collect();
 5701
 5702        drop(buffer);
 5703        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5704            selections.select(new_selections)
 5705        });
 5706    }
 5707
 5708    /// Iterate the given selections, and for each one, find the smallest surrounding
 5709    /// autoclose region. This uses the ordering of the selections and the autoclose
 5710    /// regions to avoid repeated comparisons.
 5711    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5712        &'a self,
 5713        selections: impl IntoIterator<Item = Selection<D>>,
 5714        buffer: &'a MultiBufferSnapshot,
 5715    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5716        let mut i = 0;
 5717        let mut regions = self.autoclose_regions.as_slice();
 5718        selections.into_iter().map(move |selection| {
 5719            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5720
 5721            let mut enclosing = None;
 5722            while let Some(pair_state) = regions.get(i) {
 5723                if pair_state.range.end.to_offset(buffer) < range.start {
 5724                    regions = &regions[i + 1..];
 5725                    i = 0;
 5726                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5727                    break;
 5728                } else {
 5729                    if pair_state.selection_id == selection.id {
 5730                        enclosing = Some(pair_state);
 5731                    }
 5732                    i += 1;
 5733                }
 5734            }
 5735
 5736            (selection, enclosing)
 5737        })
 5738    }
 5739
 5740    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5741    fn invalidate_autoclose_regions(
 5742        &mut self,
 5743        mut selections: &[Selection<Anchor>],
 5744        buffer: &MultiBufferSnapshot,
 5745    ) {
 5746        self.autoclose_regions.retain(|state| {
 5747            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5748                return false;
 5749            }
 5750
 5751            let mut i = 0;
 5752            while let Some(selection) = selections.get(i) {
 5753                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5754                    selections = &selections[1..];
 5755                    continue;
 5756                }
 5757                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5758                    break;
 5759                }
 5760                if selection.id == state.selection_id {
 5761                    return true;
 5762                } else {
 5763                    i += 1;
 5764                }
 5765            }
 5766            false
 5767        });
 5768    }
 5769
 5770    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5771        let offset = position.to_offset(buffer);
 5772        let (word_range, kind) =
 5773            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5774        if offset > word_range.start && kind == Some(CharKind::Word) {
 5775            Some(
 5776                buffer
 5777                    .text_for_range(word_range.start..offset)
 5778                    .collect::<String>(),
 5779            )
 5780        } else {
 5781            None
 5782        }
 5783    }
 5784
 5785    pub fn visible_excerpts(
 5786        &self,
 5787        lsp_related_only: bool,
 5788        cx: &mut Context<Editor>,
 5789    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5790        let project = self.project().cloned();
 5791        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5792        let multi_buffer = self.buffer().read(cx);
 5793        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5794        let multi_buffer_visible_start = self
 5795            .scroll_manager
 5796            .native_anchor(&display_snapshot, cx)
 5797            .anchor
 5798            .to_point(&multi_buffer_snapshot);
 5799        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5800            multi_buffer_visible_start
 5801                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5802            Bias::Left,
 5803        );
 5804        multi_buffer_snapshot
 5805            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5806            .into_iter()
 5807            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5808            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5809                if !lsp_related_only {
 5810                    return Some((
 5811                        excerpt_id,
 5812                        (
 5813                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5814                            buffer.version().clone(),
 5815                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5816                        ),
 5817                    ));
 5818                }
 5819
 5820                let project = project.as_ref()?.read(cx);
 5821                let buffer_file = project::File::from_dyn(buffer.file())?;
 5822                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5823                let worktree_entry = buffer_worktree
 5824                    .read(cx)
 5825                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5826                if worktree_entry.is_ignored {
 5827                    None
 5828                } else {
 5829                    Some((
 5830                        excerpt_id,
 5831                        (
 5832                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5833                            buffer.version().clone(),
 5834                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5835                        ),
 5836                    ))
 5837                }
 5838            })
 5839            .collect()
 5840    }
 5841
 5842    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5843        TextLayoutDetails {
 5844            text_system: window.text_system().clone(),
 5845            editor_style: self.style.clone().unwrap(),
 5846            rem_size: window.rem_size(),
 5847            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5848            visible_rows: self.visible_line_count(),
 5849            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5850        }
 5851    }
 5852
 5853    fn trigger_on_type_formatting(
 5854        &self,
 5855        input: String,
 5856        window: &mut Window,
 5857        cx: &mut Context<Self>,
 5858    ) -> Option<Task<Result<()>>> {
 5859        if input.chars().count() != 1 {
 5860            return None;
 5861        }
 5862
 5863        let project = self.project()?;
 5864        let position = self.selections.newest_anchor().head();
 5865        let (buffer, buffer_position) = self
 5866            .buffer
 5867            .read(cx)
 5868            .text_anchor_for_position(position, cx)?;
 5869
 5870        let settings = language_settings::language_settings(
 5871            buffer
 5872                .read(cx)
 5873                .language_at(buffer_position)
 5874                .map(|l| l.name()),
 5875            buffer.read(cx).file(),
 5876            cx,
 5877        );
 5878        if !settings.use_on_type_format {
 5879            return None;
 5880        }
 5881
 5882        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5883        // hence we do LSP request & edit on host side only — add formats to host's history.
 5884        let push_to_lsp_host_history = true;
 5885        // If this is not the host, append its history with new edits.
 5886        let push_to_client_history = project.read(cx).is_via_collab();
 5887
 5888        let on_type_formatting = project.update(cx, |project, cx| {
 5889            project.on_type_format(
 5890                buffer.clone(),
 5891                buffer_position,
 5892                input,
 5893                push_to_lsp_host_history,
 5894                cx,
 5895            )
 5896        });
 5897        Some(cx.spawn_in(window, async move |editor, cx| {
 5898            if let Some(transaction) = on_type_formatting.await? {
 5899                if push_to_client_history {
 5900                    buffer.update(cx, |buffer, _| {
 5901                        buffer.push_transaction(transaction, Instant::now());
 5902                        buffer.finalize_last_transaction();
 5903                    });
 5904                }
 5905                editor.update(cx, |editor, cx| {
 5906                    editor.refresh_document_highlights(cx);
 5907                })?;
 5908            }
 5909            Ok(())
 5910        }))
 5911    }
 5912
 5913    pub fn show_word_completions(
 5914        &mut self,
 5915        _: &ShowWordCompletions,
 5916        window: &mut Window,
 5917        cx: &mut Context<Self>,
 5918    ) {
 5919        self.open_or_update_completions_menu(
 5920            Some(CompletionsMenuSource::Words {
 5921                ignore_threshold: true,
 5922            }),
 5923            None,
 5924            false,
 5925            window,
 5926            cx,
 5927        );
 5928    }
 5929
 5930    pub fn show_completions(
 5931        &mut self,
 5932        _: &ShowCompletions,
 5933        window: &mut Window,
 5934        cx: &mut Context<Self>,
 5935    ) {
 5936        self.open_or_update_completions_menu(None, None, false, window, cx);
 5937    }
 5938
 5939    fn open_or_update_completions_menu(
 5940        &mut self,
 5941        requested_source: Option<CompletionsMenuSource>,
 5942        trigger: Option<String>,
 5943        trigger_in_words: bool,
 5944        window: &mut Window,
 5945        cx: &mut Context<Self>,
 5946    ) {
 5947        if self.pending_rename.is_some() {
 5948            return;
 5949        }
 5950
 5951        let completions_source = self
 5952            .context_menu
 5953            .borrow()
 5954            .as_ref()
 5955            .and_then(|menu| match menu {
 5956                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5957                CodeContextMenu::CodeActions(_) => None,
 5958            });
 5959
 5960        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5961
 5962        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5963        // inserted and selected. To handle that case, the start of the selection is used so that
 5964        // the menu starts with all choices.
 5965        let position = self
 5966            .selections
 5967            .newest_anchor()
 5968            .start
 5969            .bias_right(&multibuffer_snapshot);
 5970        if position.diff_base_anchor.is_some() {
 5971            return;
 5972        }
 5973        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5974        let Some(buffer) = buffer_position
 5975            .text_anchor
 5976            .buffer_id
 5977            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5978        else {
 5979            return;
 5980        };
 5981        let buffer_snapshot = buffer.read(cx).snapshot();
 5982
 5983        let menu_is_open = matches!(
 5984            self.context_menu.borrow().as_ref(),
 5985            Some(CodeContextMenu::Completions(_))
 5986        );
 5987
 5988        let language = buffer_snapshot
 5989            .language_at(buffer_position.text_anchor)
 5990            .map(|language| language.name());
 5991
 5992        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5993        let completion_settings = language_settings.completions.clone();
 5994
 5995        let show_completions_on_input = self
 5996            .show_completions_on_input_override
 5997            .unwrap_or(language_settings.show_completions_on_input);
 5998        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5999            return;
 6000        }
 6001
 6002        let query: Option<Arc<String>> =
 6003            Self::completion_query(&multibuffer_snapshot, buffer_position)
 6004                .map(|query| query.into());
 6005
 6006        drop(multibuffer_snapshot);
 6007
 6008        // Hide the current completions menu when query is empty. Without this, cached
 6009        // completions from before the trigger char may be reused (#32774).
 6010        if query.is_none() && menu_is_open {
 6011            self.hide_context_menu(window, cx);
 6012        }
 6013
 6014        let mut ignore_word_threshold = false;
 6015        let provider = match requested_source {
 6016            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 6017            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 6018                ignore_word_threshold = ignore_threshold;
 6019                None
 6020            }
 6021            Some(CompletionsMenuSource::SnippetChoices)
 6022            | Some(CompletionsMenuSource::SnippetsOnly) => {
 6023                log::error!("bug: SnippetChoices requested_source is not handled");
 6024                None
 6025            }
 6026        };
 6027
 6028        let sort_completions = provider
 6029            .as_ref()
 6030            .is_some_and(|provider| provider.sort_completions());
 6031
 6032        let filter_completions = provider
 6033            .as_ref()
 6034            .is_none_or(|provider| provider.filter_completions());
 6035
 6036        let was_snippets_only = matches!(
 6037            completions_source,
 6038            Some(CompletionsMenuSource::SnippetsOnly)
 6039        );
 6040
 6041        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6042            if filter_completions {
 6043                menu.filter(
 6044                    query.clone().unwrap_or_default(),
 6045                    buffer_position.text_anchor,
 6046                    &buffer,
 6047                    provider.clone(),
 6048                    window,
 6049                    cx,
 6050                );
 6051            }
 6052            // When `is_incomplete` is false, no need to re-query completions when the current query
 6053            // is a suffix of the initial query.
 6054            let was_complete = !menu.is_incomplete;
 6055            if was_complete && !was_snippets_only {
 6056                // If the new query is a suffix of the old query (typing more characters) and
 6057                // the previous result was complete, the existing completions can be filtered.
 6058                //
 6059                // Note that snippet completions are always complete.
 6060                let query_matches = match (&menu.initial_query, &query) {
 6061                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6062                    (None, _) => true,
 6063                    _ => false,
 6064                };
 6065                if query_matches {
 6066                    let position_matches = if menu.initial_position == position {
 6067                        true
 6068                    } else {
 6069                        let snapshot = self.buffer.read(cx).read(cx);
 6070                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6071                    };
 6072                    if position_matches {
 6073                        return;
 6074                    }
 6075                }
 6076            }
 6077        };
 6078
 6079        let Anchor {
 6080            excerpt_id: buffer_excerpt_id,
 6081            text_anchor: buffer_position,
 6082            ..
 6083        } = buffer_position;
 6084
 6085        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6086            buffer_snapshot.surrounding_word(buffer_position, None)
 6087        {
 6088            let word_to_exclude = buffer_snapshot
 6089                .text_for_range(word_range.clone())
 6090                .collect::<String>();
 6091            (
 6092                buffer_snapshot.anchor_before(word_range.start)
 6093                    ..buffer_snapshot.anchor_after(buffer_position),
 6094                Some(word_to_exclude),
 6095            )
 6096        } else {
 6097            (buffer_position..buffer_position, None)
 6098        };
 6099
 6100        let show_completion_documentation = buffer_snapshot
 6101            .settings_at(buffer_position, cx)
 6102            .show_completion_documentation;
 6103
 6104        // The document can be large, so stay in reasonable bounds when searching for words,
 6105        // otherwise completion pop-up might be slow to appear.
 6106        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6107        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6108        let min_word_search = buffer_snapshot.clip_point(
 6109            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6110            Bias::Left,
 6111        );
 6112        let max_word_search = buffer_snapshot.clip_point(
 6113            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6114            Bias::Right,
 6115        );
 6116        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6117            ..buffer_snapshot.point_to_offset(max_word_search);
 6118
 6119        let skip_digits = query
 6120            .as_ref()
 6121            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6122
 6123        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6124            trigger.as_ref().is_none_or(|trigger| {
 6125                provider.is_completion_trigger(
 6126                    &buffer,
 6127                    position.text_anchor,
 6128                    trigger,
 6129                    trigger_in_words,
 6130                    cx,
 6131                )
 6132            })
 6133        });
 6134
 6135        let provider_responses = if let Some(provider) = &provider
 6136            && load_provider_completions
 6137        {
 6138            let trigger_character =
 6139                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6140            let completion_context = CompletionContext {
 6141                trigger_kind: match &trigger_character {
 6142                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6143                    None => CompletionTriggerKind::INVOKED,
 6144                },
 6145                trigger_character,
 6146            };
 6147
 6148            provider.completions(
 6149                buffer_excerpt_id,
 6150                &buffer,
 6151                buffer_position,
 6152                completion_context,
 6153                window,
 6154                cx,
 6155            )
 6156        } else {
 6157            Task::ready(Ok(Vec::new()))
 6158        };
 6159
 6160        let load_word_completions = if !self.word_completions_enabled {
 6161            false
 6162        } else if requested_source
 6163            == Some(CompletionsMenuSource::Words {
 6164                ignore_threshold: true,
 6165            })
 6166        {
 6167            true
 6168        } else {
 6169            load_provider_completions
 6170                && completion_settings.words != WordsCompletionMode::Disabled
 6171                && (ignore_word_threshold || {
 6172                    let words_min_length = completion_settings.words_min_length;
 6173                    // check whether word has at least `words_min_length` characters
 6174                    let query_chars = query.iter().flat_map(|q| q.chars());
 6175                    query_chars.take(words_min_length).count() == words_min_length
 6176                })
 6177        };
 6178
 6179        let mut words = if load_word_completions {
 6180            cx.background_spawn({
 6181                let buffer_snapshot = buffer_snapshot.clone();
 6182                async move {
 6183                    buffer_snapshot.words_in_range(WordsQuery {
 6184                        fuzzy_contents: None,
 6185                        range: word_search_range,
 6186                        skip_digits,
 6187                    })
 6188                }
 6189            })
 6190        } else {
 6191            Task::ready(BTreeMap::default())
 6192        };
 6193
 6194        let snippets = if let Some(provider) = &provider
 6195            && provider.show_snippets()
 6196            && let Some(project) = self.project()
 6197        {
 6198            let char_classifier = buffer_snapshot
 6199                .char_classifier_at(buffer_position)
 6200                .scope_context(Some(CharScopeContext::Completion));
 6201            project.update(cx, |project, cx| {
 6202                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6203            })
 6204        } else {
 6205            Task::ready(Ok(CompletionResponse {
 6206                completions: Vec::new(),
 6207                display_options: Default::default(),
 6208                is_incomplete: false,
 6209            }))
 6210        };
 6211
 6212        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6213
 6214        let id = post_inc(&mut self.next_completion_id);
 6215        let task = cx.spawn_in(window, async move |editor, cx| {
 6216            let Ok(()) = editor.update(cx, |this, _| {
 6217                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6218            }) else {
 6219                return;
 6220            };
 6221
 6222            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6223            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6224            let mut completions = Vec::new();
 6225            let mut is_incomplete = false;
 6226            let mut display_options: Option<CompletionDisplayOptions> = None;
 6227            if let Some(provider_responses) = provider_responses.await.log_err()
 6228                && !provider_responses.is_empty()
 6229            {
 6230                for response in provider_responses {
 6231                    completions.extend(response.completions);
 6232                    is_incomplete = is_incomplete || response.is_incomplete;
 6233                    match display_options.as_mut() {
 6234                        None => {
 6235                            display_options = Some(response.display_options);
 6236                        }
 6237                        Some(options) => options.merge(&response.display_options),
 6238                    }
 6239                }
 6240                if completion_settings.words == WordsCompletionMode::Fallback {
 6241                    words = Task::ready(BTreeMap::default());
 6242                }
 6243            }
 6244            let display_options = display_options.unwrap_or_default();
 6245
 6246            let mut words = words.await;
 6247            if let Some(word_to_exclude) = &word_to_exclude {
 6248                words.remove(word_to_exclude);
 6249            }
 6250            for lsp_completion in &completions {
 6251                words.remove(&lsp_completion.new_text);
 6252            }
 6253            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6254                replace_range: word_replace_range.clone(),
 6255                new_text: word.clone(),
 6256                label: CodeLabel::plain(word, None),
 6257                match_start: None,
 6258                snippet_deduplication_key: None,
 6259                icon_path: None,
 6260                documentation: None,
 6261                source: CompletionSource::BufferWord {
 6262                    word_range,
 6263                    resolved: false,
 6264                },
 6265                insert_text_mode: Some(InsertTextMode::AS_IS),
 6266                confirm: None,
 6267            }));
 6268
 6269            completions.extend(
 6270                snippets
 6271                    .await
 6272                    .into_iter()
 6273                    .flat_map(|response| response.completions),
 6274            );
 6275
 6276            let menu = if completions.is_empty() {
 6277                None
 6278            } else {
 6279                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6280                    let languages = editor
 6281                        .workspace
 6282                        .as_ref()
 6283                        .and_then(|(workspace, _)| workspace.upgrade())
 6284                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6285                    let menu = CompletionsMenu::new(
 6286                        id,
 6287                        requested_source.unwrap_or(if load_provider_completions {
 6288                            CompletionsMenuSource::Normal
 6289                        } else {
 6290                            CompletionsMenuSource::SnippetsOnly
 6291                        }),
 6292                        sort_completions,
 6293                        show_completion_documentation,
 6294                        position,
 6295                        query.clone(),
 6296                        is_incomplete,
 6297                        buffer.clone(),
 6298                        completions.into(),
 6299                        editor
 6300                            .context_menu()
 6301                            .borrow_mut()
 6302                            .as_ref()
 6303                            .map(|menu| menu.primary_scroll_handle()),
 6304                        display_options,
 6305                        snippet_sort_order,
 6306                        languages,
 6307                        language,
 6308                        cx,
 6309                    );
 6310
 6311                    let query = if filter_completions { query } else { None };
 6312                    let matches_task = menu.do_async_filtering(
 6313                        query.unwrap_or_default(),
 6314                        buffer_position,
 6315                        &buffer,
 6316                        cx,
 6317                    );
 6318                    (menu, matches_task)
 6319                }) else {
 6320                    return;
 6321                };
 6322
 6323                let matches = matches_task.await;
 6324
 6325                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6326                    // Newer menu already set, so exit.
 6327                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6328                        editor.context_menu.borrow().as_ref()
 6329                        && prev_menu.id > id
 6330                    {
 6331                        return;
 6332                    };
 6333
 6334                    // Only valid to take prev_menu because either the new menu is immediately set
 6335                    // below, or the menu is hidden.
 6336                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6337                        editor.context_menu.borrow_mut().take()
 6338                    {
 6339                        let position_matches =
 6340                            if prev_menu.initial_position == menu.initial_position {
 6341                                true
 6342                            } else {
 6343                                let snapshot = editor.buffer.read(cx).read(cx);
 6344                                prev_menu.initial_position.to_offset(&snapshot)
 6345                                    == menu.initial_position.to_offset(&snapshot)
 6346                            };
 6347                        if position_matches {
 6348                            // Preserve markdown cache before `set_filter_results` because it will
 6349                            // try to populate the documentation cache.
 6350                            menu.preserve_markdown_cache(prev_menu);
 6351                        }
 6352                    };
 6353
 6354                    menu.set_filter_results(matches, provider, window, cx);
 6355                }) else {
 6356                    return;
 6357                };
 6358
 6359                menu.visible().then_some(menu)
 6360            };
 6361
 6362            editor
 6363                .update_in(cx, |editor, window, cx| {
 6364                    if editor.focus_handle.is_focused(window)
 6365                        && let Some(menu) = menu
 6366                    {
 6367                        *editor.context_menu.borrow_mut() =
 6368                            Some(CodeContextMenu::Completions(menu));
 6369
 6370                        crate::hover_popover::hide_hover(editor, cx);
 6371                        if editor.show_edit_predictions_in_menu() {
 6372                            editor.update_visible_edit_prediction(window, cx);
 6373                        } else {
 6374                            editor
 6375                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6376                        }
 6377
 6378                        cx.notify();
 6379                        return;
 6380                    }
 6381
 6382                    if editor.completion_tasks.len() <= 1 {
 6383                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6384                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6385                        // If it was already hidden and we don't show edit predictions in the menu,
 6386                        // we should also show the edit prediction when available.
 6387                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6388                            editor.update_visible_edit_prediction(window, cx);
 6389                        }
 6390                    }
 6391                })
 6392                .ok();
 6393        });
 6394
 6395        self.completion_tasks.push((id, task));
 6396    }
 6397
 6398    #[cfg(any(test, feature = "test-support"))]
 6399    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6400        let menu = self.context_menu.borrow();
 6401        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6402            let completions = menu.completions.borrow();
 6403            Some(completions.to_vec())
 6404        } else {
 6405            None
 6406        }
 6407    }
 6408
 6409    pub fn with_completions_menu_matching_id<R>(
 6410        &self,
 6411        id: CompletionId,
 6412        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6413    ) -> R {
 6414        let mut context_menu = self.context_menu.borrow_mut();
 6415        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6416            return f(None);
 6417        };
 6418        if completions_menu.id != id {
 6419            return f(None);
 6420        }
 6421        f(Some(completions_menu))
 6422    }
 6423
 6424    pub fn confirm_completion(
 6425        &mut self,
 6426        action: &ConfirmCompletion,
 6427        window: &mut Window,
 6428        cx: &mut Context<Self>,
 6429    ) -> Option<Task<Result<()>>> {
 6430        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6431        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6432    }
 6433
 6434    pub fn confirm_completion_insert(
 6435        &mut self,
 6436        _: &ConfirmCompletionInsert,
 6437        window: &mut Window,
 6438        cx: &mut Context<Self>,
 6439    ) -> Option<Task<Result<()>>> {
 6440        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6441        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6442    }
 6443
 6444    pub fn confirm_completion_replace(
 6445        &mut self,
 6446        _: &ConfirmCompletionReplace,
 6447        window: &mut Window,
 6448        cx: &mut Context<Self>,
 6449    ) -> Option<Task<Result<()>>> {
 6450        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6451        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6452    }
 6453
 6454    pub fn compose_completion(
 6455        &mut self,
 6456        action: &ComposeCompletion,
 6457        window: &mut Window,
 6458        cx: &mut Context<Self>,
 6459    ) -> Option<Task<Result<()>>> {
 6460        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6461        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6462    }
 6463
 6464    fn do_completion(
 6465        &mut self,
 6466        item_ix: Option<usize>,
 6467        intent: CompletionIntent,
 6468        window: &mut Window,
 6469        cx: &mut Context<Editor>,
 6470    ) -> Option<Task<Result<()>>> {
 6471        use language::ToOffset as _;
 6472
 6473        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6474        else {
 6475            return None;
 6476        };
 6477
 6478        let candidate_id = {
 6479            let entries = completions_menu.entries.borrow();
 6480            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6481            if self.show_edit_predictions_in_menu() {
 6482                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6483            }
 6484            mat.candidate_id
 6485        };
 6486
 6487        let completion = completions_menu
 6488            .completions
 6489            .borrow()
 6490            .get(candidate_id)?
 6491            .clone();
 6492        cx.stop_propagation();
 6493
 6494        let buffer_handle = completions_menu.buffer.clone();
 6495
 6496        let CompletionEdit {
 6497            new_text,
 6498            snippet,
 6499            replace_range,
 6500        } = process_completion_for_edit(
 6501            &completion,
 6502            intent,
 6503            &buffer_handle,
 6504            &completions_menu.initial_position.text_anchor,
 6505            cx,
 6506        );
 6507
 6508        let buffer = buffer_handle.read(cx);
 6509        let snapshot = self.buffer.read(cx).snapshot(cx);
 6510        let newest_anchor = self.selections.newest_anchor();
 6511        let replace_range_multibuffer = {
 6512            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6513            excerpt.map_range_from_buffer(replace_range.clone())
 6514        };
 6515        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6516            return None;
 6517        }
 6518
 6519        let old_text = buffer
 6520            .text_for_range(replace_range.clone())
 6521            .collect::<String>();
 6522        let lookbehind = newest_anchor
 6523            .start
 6524            .text_anchor
 6525            .to_offset(buffer)
 6526            .saturating_sub(replace_range.start.0);
 6527        let lookahead = replace_range
 6528            .end
 6529            .0
 6530            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6531        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6532        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6533
 6534        let selections = self
 6535            .selections
 6536            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6537        let mut ranges = Vec::new();
 6538        let mut linked_edits = LinkedEdits::new();
 6539
 6540        let text: Arc<str> = new_text.clone().into();
 6541        for selection in &selections {
 6542            let range = if selection.id == newest_anchor.id {
 6543                replace_range_multibuffer.clone()
 6544            } else {
 6545                let mut range = selection.range();
 6546
 6547                // if prefix is present, don't duplicate it
 6548                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6549                    range.start = range.start.saturating_sub_usize(lookbehind);
 6550
 6551                    // if suffix is also present, mimic the newest cursor and replace it
 6552                    if selection.id != newest_anchor.id
 6553                        && snapshot.contains_str_at(range.end, suffix)
 6554                    {
 6555                        range.end += lookahead;
 6556                    }
 6557                }
 6558                range
 6559            };
 6560
 6561            ranges.push(range.clone());
 6562
 6563            if !self.linked_edit_ranges.is_empty() {
 6564                let start_anchor = snapshot.anchor_before(range.start);
 6565                let end_anchor = snapshot.anchor_after(range.end);
 6566                let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6567                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6568            }
 6569        }
 6570
 6571        let common_prefix_len = old_text
 6572            .chars()
 6573            .zip(new_text.chars())
 6574            .take_while(|(a, b)| a == b)
 6575            .map(|(a, _)| a.len_utf8())
 6576            .sum::<usize>();
 6577
 6578        cx.emit(EditorEvent::InputHandled {
 6579            utf16_range_to_replace: None,
 6580            text: new_text[common_prefix_len..].into(),
 6581        });
 6582
 6583        self.transact(window, cx, |editor, window, cx| {
 6584            if let Some(mut snippet) = snippet {
 6585                snippet.text = new_text.to_string();
 6586                editor
 6587                    .insert_snippet(&ranges, snippet, window, cx)
 6588                    .log_err();
 6589            } else {
 6590                editor.buffer.update(cx, |multi_buffer, cx| {
 6591                    let auto_indent = match completion.insert_text_mode {
 6592                        Some(InsertTextMode::AS_IS) => None,
 6593                        _ => editor.autoindent_mode.clone(),
 6594                    };
 6595                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6596                    multi_buffer.edit(edits, auto_indent, cx);
 6597                });
 6598            }
 6599            linked_edits.apply(cx);
 6600            editor.refresh_edit_prediction(true, false, window, cx);
 6601        });
 6602        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6603
 6604        let show_new_completions_on_confirm = completion
 6605            .confirm
 6606            .as_ref()
 6607            .is_some_and(|confirm| confirm(intent, window, cx));
 6608        if show_new_completions_on_confirm {
 6609            self.open_or_update_completions_menu(None, None, false, window, cx);
 6610        }
 6611
 6612        let provider = self.completion_provider.as_ref()?;
 6613
 6614        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6615        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6616            let CompletionSource::Lsp {
 6617                lsp_completion,
 6618                server_id,
 6619                ..
 6620            } = &completion.source
 6621            else {
 6622                return None;
 6623            };
 6624            let lsp_command = lsp_completion.command.as_ref()?;
 6625            let available_commands = lsp_store
 6626                .read(cx)
 6627                .lsp_server_capabilities
 6628                .get(server_id)
 6629                .and_then(|server_capabilities| {
 6630                    server_capabilities
 6631                        .execute_command_provider
 6632                        .as_ref()
 6633                        .map(|options| options.commands.as_slice())
 6634                })?;
 6635            if available_commands.contains(&lsp_command.command) {
 6636                Some(CodeAction {
 6637                    server_id: *server_id,
 6638                    range: language::Anchor::MIN..language::Anchor::MIN,
 6639                    lsp_action: LspAction::Command(lsp_command.clone()),
 6640                    resolved: false,
 6641                })
 6642            } else {
 6643                None
 6644            }
 6645        });
 6646
 6647        drop(completion);
 6648        let apply_edits = provider.apply_additional_edits_for_completion(
 6649            buffer_handle.clone(),
 6650            completions_menu.completions.clone(),
 6651            candidate_id,
 6652            true,
 6653            cx,
 6654        );
 6655
 6656        let editor_settings = EditorSettings::get_global(cx);
 6657        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6658            // After the code completion is finished, users often want to know what signatures are needed.
 6659            // so we should automatically call signature_help
 6660            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6661        }
 6662
 6663        Some(cx.spawn_in(window, async move |editor, cx| {
 6664            apply_edits.await?;
 6665
 6666            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6667                let title = command.lsp_action.title().to_owned();
 6668                let project_transaction = lsp_store
 6669                    .update(cx, |lsp_store, cx| {
 6670                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6671                    })
 6672                    .await
 6673                    .context("applying post-completion command")?;
 6674                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6675                    Self::open_project_transaction(
 6676                        &editor,
 6677                        workspace.downgrade(),
 6678                        project_transaction,
 6679                        title,
 6680                        cx,
 6681                    )
 6682                    .await?;
 6683                }
 6684            }
 6685
 6686            Ok(())
 6687        }))
 6688    }
 6689
 6690    pub fn toggle_code_actions(
 6691        &mut self,
 6692        action: &ToggleCodeActions,
 6693        window: &mut Window,
 6694        cx: &mut Context<Self>,
 6695    ) {
 6696        let quick_launch = action.quick_launch;
 6697        let mut context_menu = self.context_menu.borrow_mut();
 6698        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6699            if code_actions.deployed_from == action.deployed_from {
 6700                // Toggle if we're selecting the same one
 6701                *context_menu = None;
 6702                cx.notify();
 6703                return;
 6704            } else {
 6705                // Otherwise, clear it and start a new one
 6706                *context_menu = None;
 6707                cx.notify();
 6708            }
 6709        }
 6710        drop(context_menu);
 6711        let snapshot = self.snapshot(window, cx);
 6712        let deployed_from = action.deployed_from.clone();
 6713        let action = action.clone();
 6714        self.completion_tasks.clear();
 6715        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6716
 6717        let multibuffer_point = match &action.deployed_from {
 6718            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6719                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6720            }
 6721            _ => self
 6722                .selections
 6723                .newest::<Point>(&snapshot.display_snapshot)
 6724                .head(),
 6725        };
 6726        let Some((buffer, buffer_row)) = snapshot
 6727            .buffer_snapshot()
 6728            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6729            .and_then(|(buffer_snapshot, range)| {
 6730                self.buffer()
 6731                    .read(cx)
 6732                    .buffer(buffer_snapshot.remote_id())
 6733                    .map(|buffer| (buffer, range.start.row))
 6734            })
 6735        else {
 6736            return;
 6737        };
 6738        let buffer_id = buffer.read(cx).remote_id();
 6739        let tasks = self
 6740            .tasks
 6741            .get(&(buffer_id, buffer_row))
 6742            .map(|t| Arc::new(t.to_owned()));
 6743
 6744        if !self.focus_handle.is_focused(window) {
 6745            return;
 6746        }
 6747        let project = self.project.clone();
 6748
 6749        let code_actions_task = match deployed_from {
 6750            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6751            _ => self.code_actions(buffer_row, window, cx),
 6752        };
 6753
 6754        let runnable_task = match deployed_from {
 6755            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6756            _ => {
 6757                let mut task_context_task = Task::ready(None);
 6758                if let Some(tasks) = &tasks
 6759                    && let Some(project) = project
 6760                {
 6761                    task_context_task =
 6762                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6763                }
 6764
 6765                cx.spawn_in(window, {
 6766                    let buffer = buffer.clone();
 6767                    async move |editor, cx| {
 6768                        let task_context = task_context_task.await;
 6769
 6770                        let resolved_tasks =
 6771                            tasks
 6772                                .zip(task_context.clone())
 6773                                .map(|(tasks, task_context)| ResolvedTasks {
 6774                                    templates: tasks.resolve(&task_context).collect(),
 6775                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6776                                        multibuffer_point.row,
 6777                                        tasks.column,
 6778                                    )),
 6779                                });
 6780                        let debug_scenarios = editor
 6781                            .update(cx, |editor, cx| {
 6782                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6783                            })?
 6784                            .await;
 6785                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6786                    }
 6787                })
 6788            }
 6789        };
 6790
 6791        cx.spawn_in(window, async move |editor, cx| {
 6792            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6793            let code_actions = code_actions_task.await;
 6794            let spawn_straight_away = quick_launch
 6795                && resolved_tasks
 6796                    .as_ref()
 6797                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6798                && code_actions
 6799                    .as_ref()
 6800                    .is_none_or(|actions| actions.is_empty())
 6801                && debug_scenarios.is_empty();
 6802
 6803            editor.update_in(cx, |editor, window, cx| {
 6804                crate::hover_popover::hide_hover(editor, cx);
 6805                let actions = CodeActionContents::new(
 6806                    resolved_tasks,
 6807                    code_actions,
 6808                    debug_scenarios,
 6809                    task_context.unwrap_or_default(),
 6810                );
 6811
 6812                // Don't show the menu if there are no actions available
 6813                if actions.is_empty() {
 6814                    cx.notify();
 6815                    return Task::ready(Ok(()));
 6816                }
 6817
 6818                *editor.context_menu.borrow_mut() =
 6819                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6820                        buffer,
 6821                        actions,
 6822                        selected_item: Default::default(),
 6823                        scroll_handle: UniformListScrollHandle::default(),
 6824                        deployed_from,
 6825                    }));
 6826                cx.notify();
 6827                if spawn_straight_away
 6828                    && let Some(task) = editor.confirm_code_action(
 6829                        &ConfirmCodeAction { item_ix: Some(0) },
 6830                        window,
 6831                        cx,
 6832                    )
 6833                {
 6834                    return task;
 6835                }
 6836
 6837                Task::ready(Ok(()))
 6838            })
 6839        })
 6840        .detach_and_log_err(cx);
 6841    }
 6842
 6843    fn debug_scenarios(
 6844        &mut self,
 6845        resolved_tasks: &Option<ResolvedTasks>,
 6846        buffer: &Entity<Buffer>,
 6847        cx: &mut App,
 6848    ) -> Task<Vec<task::DebugScenario>> {
 6849        maybe!({
 6850            let project = self.project()?;
 6851            let dap_store = project.read(cx).dap_store();
 6852            let mut scenarios = vec![];
 6853            let resolved_tasks = resolved_tasks.as_ref()?;
 6854            let buffer = buffer.read(cx);
 6855            let language = buffer.language()?;
 6856            let file = buffer.file();
 6857            let debug_adapter = language_settings(language.name().into(), file, cx)
 6858                .debuggers
 6859                .first()
 6860                .map(SharedString::from)
 6861                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6862
 6863            dap_store.update(cx, |dap_store, cx| {
 6864                for (_, task) in &resolved_tasks.templates {
 6865                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6866                        task.original_task().clone(),
 6867                        debug_adapter.clone().into(),
 6868                        task.display_label().to_owned().into(),
 6869                        cx,
 6870                    );
 6871                    scenarios.push(maybe_scenario);
 6872                }
 6873            });
 6874            Some(cx.background_spawn(async move {
 6875                futures::future::join_all(scenarios)
 6876                    .await
 6877                    .into_iter()
 6878                    .flatten()
 6879                    .collect::<Vec<_>>()
 6880            }))
 6881        })
 6882        .unwrap_or_else(|| Task::ready(vec![]))
 6883    }
 6884
 6885    fn code_actions(
 6886        &mut self,
 6887        buffer_row: u32,
 6888        window: &mut Window,
 6889        cx: &mut Context<Self>,
 6890    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6891        let mut task = self.code_actions_task.take();
 6892        cx.spawn_in(window, async move |editor, cx| {
 6893            while let Some(prev_task) = task {
 6894                prev_task.await.log_err();
 6895                task = editor
 6896                    .update(cx, |this, _| this.code_actions_task.take())
 6897                    .ok()?;
 6898            }
 6899
 6900            editor
 6901                .update(cx, |editor, cx| {
 6902                    editor
 6903                        .available_code_actions
 6904                        .clone()
 6905                        .and_then(|(location, code_actions)| {
 6906                            let snapshot = location.buffer.read(cx).snapshot();
 6907                            let point_range = location.range.to_point(&snapshot);
 6908                            let point_range = point_range.start.row..=point_range.end.row;
 6909                            if point_range.contains(&buffer_row) {
 6910                                Some(code_actions)
 6911                            } else {
 6912                                None
 6913                            }
 6914                        })
 6915                })
 6916                .ok()
 6917                .flatten()
 6918        })
 6919    }
 6920
 6921    pub fn confirm_code_action(
 6922        &mut self,
 6923        action: &ConfirmCodeAction,
 6924        window: &mut Window,
 6925        cx: &mut Context<Self>,
 6926    ) -> Option<Task<Result<()>>> {
 6927        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6928
 6929        let actions_menu =
 6930            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6931                menu
 6932            } else {
 6933                return None;
 6934            };
 6935
 6936        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6937        let action = actions_menu.actions.get(action_ix)?;
 6938        let title = action.label();
 6939        let buffer = actions_menu.buffer;
 6940        let workspace = self.workspace()?;
 6941
 6942        match action {
 6943            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6944                workspace.update(cx, |workspace, cx| {
 6945                    workspace.schedule_resolved_task(
 6946                        task_source_kind,
 6947                        resolved_task,
 6948                        false,
 6949                        window,
 6950                        cx,
 6951                    );
 6952
 6953                    Some(Task::ready(Ok(())))
 6954                })
 6955            }
 6956            CodeActionsItem::CodeAction {
 6957                excerpt_id,
 6958                action,
 6959                provider,
 6960            } => {
 6961                let apply_code_action =
 6962                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6963                let workspace = workspace.downgrade();
 6964                Some(cx.spawn_in(window, async move |editor, cx| {
 6965                    let project_transaction = apply_code_action.await?;
 6966                    Self::open_project_transaction(
 6967                        &editor,
 6968                        workspace,
 6969                        project_transaction,
 6970                        title,
 6971                        cx,
 6972                    )
 6973                    .await
 6974                }))
 6975            }
 6976            CodeActionsItem::DebugScenario(scenario) => {
 6977                let context = actions_menu.actions.context.into();
 6978
 6979                workspace.update(cx, |workspace, cx| {
 6980                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6981                    workspace.start_debug_session(
 6982                        scenario,
 6983                        context,
 6984                        Some(buffer),
 6985                        None,
 6986                        window,
 6987                        cx,
 6988                    );
 6989                });
 6990                Some(Task::ready(Ok(())))
 6991            }
 6992        }
 6993    }
 6994
 6995    fn open_transaction_for_hidden_buffers(
 6996        workspace: Entity<Workspace>,
 6997        transaction: ProjectTransaction,
 6998        title: String,
 6999        window: &mut Window,
 7000        cx: &mut Context<Self>,
 7001    ) {
 7002        if transaction.0.is_empty() {
 7003            return;
 7004        }
 7005
 7006        let edited_buffers_already_open = {
 7007            let other_editors: Vec<Entity<Editor>> = workspace
 7008                .read(cx)
 7009                .panes()
 7010                .iter()
 7011                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7012                .filter(|editor| editor.entity_id() != cx.entity_id())
 7013                .collect();
 7014
 7015            transaction.0.keys().all(|buffer| {
 7016                other_editors.iter().any(|editor| {
 7017                    let multi_buffer = editor.read(cx).buffer();
 7018                    multi_buffer.read(cx).is_singleton()
 7019                        && multi_buffer
 7020                            .read(cx)
 7021                            .as_singleton()
 7022                            .map_or(false, |singleton| {
 7023                                singleton.entity_id() == buffer.entity_id()
 7024                            })
 7025                })
 7026            })
 7027        };
 7028        if !edited_buffers_already_open {
 7029            let workspace = workspace.downgrade();
 7030            cx.defer_in(window, move |_, window, cx| {
 7031                cx.spawn_in(window, async move |editor, cx| {
 7032                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7033                        .await
 7034                        .ok()
 7035                })
 7036                .detach();
 7037            });
 7038        }
 7039    }
 7040
 7041    pub async fn open_project_transaction(
 7042        editor: &WeakEntity<Editor>,
 7043        workspace: WeakEntity<Workspace>,
 7044        transaction: ProjectTransaction,
 7045        title: String,
 7046        cx: &mut AsyncWindowContext,
 7047    ) -> Result<()> {
 7048        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7049        cx.update(|_, cx| {
 7050            entries.sort_unstable_by_key(|(buffer, _)| {
 7051                buffer.read(cx).file().map(|f| f.path().clone())
 7052            });
 7053        })?;
 7054        if entries.is_empty() {
 7055            return Ok(());
 7056        }
 7057
 7058        // If the project transaction's edits are all contained within this editor, then
 7059        // avoid opening a new editor to display them.
 7060
 7061        if let [(buffer, transaction)] = &*entries {
 7062            let excerpt = editor.update(cx, |editor, cx| {
 7063                editor
 7064                    .buffer()
 7065                    .read(cx)
 7066                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7067            })?;
 7068            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7069                && excerpted_buffer == *buffer
 7070            {
 7071                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7072                    let excerpt_range = excerpt_range.to_offset(buffer);
 7073                    buffer
 7074                        .edited_ranges_for_transaction::<usize>(transaction)
 7075                        .all(|range| {
 7076                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7077                        })
 7078                });
 7079
 7080                if all_edits_within_excerpt {
 7081                    return Ok(());
 7082                }
 7083            }
 7084        }
 7085
 7086        let mut ranges_to_highlight = Vec::new();
 7087        let excerpt_buffer = cx.new(|cx| {
 7088            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7089            for (buffer_handle, transaction) in &entries {
 7090                let edited_ranges = buffer_handle
 7091                    .read(cx)
 7092                    .edited_ranges_for_transaction::<Point>(transaction)
 7093                    .collect::<Vec<_>>();
 7094                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7095                    PathKey::for_buffer(buffer_handle, cx),
 7096                    buffer_handle.clone(),
 7097                    edited_ranges,
 7098                    multibuffer_context_lines(cx),
 7099                    cx,
 7100                );
 7101
 7102                ranges_to_highlight.extend(ranges);
 7103            }
 7104            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7105            multibuffer
 7106        });
 7107
 7108        workspace.update_in(cx, |workspace, window, cx| {
 7109            let project = workspace.project().clone();
 7110            let editor =
 7111                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7112            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7113            editor.update(cx, |editor, cx| {
 7114                editor.highlight_background(
 7115                    HighlightKey::Editor,
 7116                    &ranges_to_highlight,
 7117                    |_, theme| theme.colors().editor_highlighted_line_background,
 7118                    cx,
 7119                );
 7120            });
 7121        })?;
 7122
 7123        Ok(())
 7124    }
 7125
 7126    pub fn clear_code_action_providers(&mut self) {
 7127        self.code_action_providers.clear();
 7128        self.available_code_actions.take();
 7129    }
 7130
 7131    pub fn add_code_action_provider(
 7132        &mut self,
 7133        provider: Rc<dyn CodeActionProvider>,
 7134        window: &mut Window,
 7135        cx: &mut Context<Self>,
 7136    ) {
 7137        if self
 7138            .code_action_providers
 7139            .iter()
 7140            .any(|existing_provider| existing_provider.id() == provider.id())
 7141        {
 7142            return;
 7143        }
 7144
 7145        self.code_action_providers.push(provider);
 7146        self.refresh_code_actions(window, cx);
 7147    }
 7148
 7149    pub fn remove_code_action_provider(
 7150        &mut self,
 7151        id: Arc<str>,
 7152        window: &mut Window,
 7153        cx: &mut Context<Self>,
 7154    ) {
 7155        self.code_action_providers
 7156            .retain(|provider| provider.id() != id);
 7157        self.refresh_code_actions(window, cx);
 7158    }
 7159
 7160    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7161        !self.code_action_providers.is_empty()
 7162            && EditorSettings::get_global(cx).toolbar.code_actions
 7163    }
 7164
 7165    pub fn has_available_code_actions(&self) -> bool {
 7166        self.available_code_actions
 7167            .as_ref()
 7168            .is_some_and(|(_, actions)| !actions.is_empty())
 7169    }
 7170
 7171    fn render_inline_code_actions(
 7172        &self,
 7173        icon_size: ui::IconSize,
 7174        display_row: DisplayRow,
 7175        is_active: bool,
 7176        cx: &mut Context<Self>,
 7177    ) -> AnyElement {
 7178        let show_tooltip = !self.context_menu_visible();
 7179        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7180            .icon_size(icon_size)
 7181            .shape(ui::IconButtonShape::Square)
 7182            .icon_color(ui::Color::Hidden)
 7183            .toggle_state(is_active)
 7184            .when(show_tooltip, |this| {
 7185                this.tooltip({
 7186                    let focus_handle = self.focus_handle.clone();
 7187                    move |_window, cx| {
 7188                        Tooltip::for_action_in(
 7189                            "Toggle Code Actions",
 7190                            &ToggleCodeActions {
 7191                                deployed_from: None,
 7192                                quick_launch: false,
 7193                            },
 7194                            &focus_handle,
 7195                            cx,
 7196                        )
 7197                    }
 7198                })
 7199            })
 7200            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7201                window.focus(&editor.focus_handle(cx), cx);
 7202                editor.toggle_code_actions(
 7203                    &crate::actions::ToggleCodeActions {
 7204                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7205                            display_row,
 7206                        )),
 7207                        quick_launch: false,
 7208                    },
 7209                    window,
 7210                    cx,
 7211                );
 7212            }))
 7213            .into_any_element()
 7214    }
 7215
 7216    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7217        &self.context_menu
 7218    }
 7219
 7220    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7221        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7222            cx.background_executor()
 7223                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7224                .await;
 7225
 7226            let (start_buffer, start, _, end, newest_selection) = this
 7227                .update(cx, |this, cx| {
 7228                    let newest_selection = this.selections.newest_anchor().clone();
 7229                    if newest_selection.head().diff_base_anchor.is_some() {
 7230                        return None;
 7231                    }
 7232                    let display_snapshot = this.display_snapshot(cx);
 7233                    let newest_selection_adjusted =
 7234                        this.selections.newest_adjusted(&display_snapshot);
 7235                    let buffer = this.buffer.read(cx);
 7236
 7237                    let (start_buffer, start) =
 7238                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7239                    let (end_buffer, end) =
 7240                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7241
 7242                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7243                })?
 7244                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7245                .context(
 7246                    "Expected selection to lie in a single buffer when refreshing code actions",
 7247                )?;
 7248            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7249                let providers = this.code_action_providers.clone();
 7250                let tasks = this
 7251                    .code_action_providers
 7252                    .iter()
 7253                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7254                    .collect::<Vec<_>>();
 7255                (providers, tasks)
 7256            })?;
 7257
 7258            let mut actions = Vec::new();
 7259            for (provider, provider_actions) in
 7260                providers.into_iter().zip(future::join_all(tasks).await)
 7261            {
 7262                if let Some(provider_actions) = provider_actions.log_err() {
 7263                    actions.extend(provider_actions.into_iter().map(|action| {
 7264                        AvailableCodeAction {
 7265                            excerpt_id: newest_selection.start.excerpt_id,
 7266                            action,
 7267                            provider: provider.clone(),
 7268                        }
 7269                    }));
 7270                }
 7271            }
 7272
 7273            this.update(cx, |this, cx| {
 7274                this.available_code_actions = if actions.is_empty() {
 7275                    None
 7276                } else {
 7277                    Some((
 7278                        Location {
 7279                            buffer: start_buffer,
 7280                            range: start..end,
 7281                        },
 7282                        actions.into(),
 7283                    ))
 7284                };
 7285                cx.notify();
 7286            })
 7287        }));
 7288    }
 7289
 7290    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7291        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7292            self.show_git_blame_inline = false;
 7293
 7294            self.show_git_blame_inline_delay_task =
 7295                Some(cx.spawn_in(window, async move |this, cx| {
 7296                    cx.background_executor().timer(delay).await;
 7297
 7298                    this.update(cx, |this, cx| {
 7299                        this.show_git_blame_inline = true;
 7300                        cx.notify();
 7301                    })
 7302                    .log_err();
 7303                }));
 7304        }
 7305    }
 7306
 7307    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7308        let snapshot = self.snapshot(window, cx);
 7309        let cursor = self
 7310            .selections
 7311            .newest::<Point>(&snapshot.display_snapshot)
 7312            .head();
 7313        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7314        else {
 7315            return;
 7316        };
 7317
 7318        if self.blame.is_none() {
 7319            self.start_git_blame(true, window, cx);
 7320        }
 7321        let Some(blame) = self.blame.as_ref() else {
 7322            return;
 7323        };
 7324
 7325        let row_info = RowInfo {
 7326            buffer_id: Some(buffer.remote_id()),
 7327            buffer_row: Some(point.row),
 7328            ..Default::default()
 7329        };
 7330        let Some((buffer, blame_entry)) = blame
 7331            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7332            .flatten()
 7333        else {
 7334            return;
 7335        };
 7336
 7337        let anchor = self.selections.newest_anchor().head();
 7338        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7339        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7340            self.show_blame_popover(
 7341                buffer,
 7342                &blame_entry,
 7343                position + last_bounds.origin,
 7344                true,
 7345                cx,
 7346            );
 7347        };
 7348    }
 7349
 7350    fn show_blame_popover(
 7351        &mut self,
 7352        buffer: BufferId,
 7353        blame_entry: &BlameEntry,
 7354        position: gpui::Point<Pixels>,
 7355        ignore_timeout: bool,
 7356        cx: &mut Context<Self>,
 7357    ) {
 7358        if let Some(state) = &mut self.inline_blame_popover {
 7359            state.hide_task.take();
 7360        } else {
 7361            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7362            let blame_entry = blame_entry.clone();
 7363            let show_task = cx.spawn(async move |editor, cx| {
 7364                if !ignore_timeout {
 7365                    cx.background_executor()
 7366                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7367                        .await;
 7368                }
 7369                editor
 7370                    .update(cx, |editor, cx| {
 7371                        editor.inline_blame_popover_show_task.take();
 7372                        let Some(blame) = editor.blame.as_ref() else {
 7373                            return;
 7374                        };
 7375                        let blame = blame.read(cx);
 7376                        let details = blame.details_for_entry(buffer, &blame_entry);
 7377                        let markdown = cx.new(|cx| {
 7378                            Markdown::new(
 7379                                details
 7380                                    .as_ref()
 7381                                    .map(|message| message.message.clone())
 7382                                    .unwrap_or_default(),
 7383                                None,
 7384                                None,
 7385                                cx,
 7386                            )
 7387                        });
 7388                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7389                            position,
 7390                            hide_task: None,
 7391                            popover_bounds: None,
 7392                            popover_state: InlineBlamePopoverState {
 7393                                scroll_handle: ScrollHandle::new(),
 7394                                commit_message: details,
 7395                                markdown,
 7396                            },
 7397                            keyboard_grace: ignore_timeout,
 7398                        });
 7399                        cx.notify();
 7400                    })
 7401                    .ok();
 7402            });
 7403            self.inline_blame_popover_show_task = Some(show_task);
 7404        }
 7405    }
 7406
 7407    pub fn has_mouse_context_menu(&self) -> bool {
 7408        self.mouse_context_menu.is_some()
 7409    }
 7410
 7411    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7412        self.inline_blame_popover_show_task.take();
 7413        if let Some(state) = &mut self.inline_blame_popover {
 7414            let hide_task = cx.spawn(async move |editor, cx| {
 7415                if !ignore_timeout {
 7416                    cx.background_executor()
 7417                        .timer(std::time::Duration::from_millis(100))
 7418                        .await;
 7419                }
 7420                editor
 7421                    .update(cx, |editor, cx| {
 7422                        editor.inline_blame_popover.take();
 7423                        cx.notify();
 7424                    })
 7425                    .ok();
 7426            });
 7427            state.hide_task = Some(hide_task);
 7428            true
 7429        } else {
 7430            false
 7431        }
 7432    }
 7433
 7434    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7435        if self.pending_rename.is_some() {
 7436            return None;
 7437        }
 7438
 7439        let provider = self.semantics_provider.clone()?;
 7440        let buffer = self.buffer.read(cx);
 7441        let newest_selection = self.selections.newest_anchor().clone();
 7442        let cursor_position = newest_selection.head();
 7443        let (cursor_buffer, cursor_buffer_position) =
 7444            buffer.text_anchor_for_position(cursor_position, cx)?;
 7445        let (tail_buffer, tail_buffer_position) =
 7446            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7447        if cursor_buffer != tail_buffer {
 7448            return None;
 7449        }
 7450
 7451        let snapshot = cursor_buffer.read(cx).snapshot();
 7452        let word_ranges = cx.background_spawn(async move {
 7453            // this might look odd to put on the background thread, but
 7454            // `surrounding_word` can be quite expensive as it calls into
 7455            // tree-sitter language scopes
 7456            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7457            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7458            (start_word_range, end_word_range)
 7459        });
 7460
 7461        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7462        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7463            let (start_word_range, end_word_range) = word_ranges.await;
 7464            if start_word_range != end_word_range {
 7465                this.update(cx, |this, cx| {
 7466                    this.document_highlights_task.take();
 7467                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7468                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7469                })
 7470                .ok();
 7471                return;
 7472            }
 7473            cx.background_executor()
 7474                .timer(Duration::from_millis(debounce))
 7475                .await;
 7476
 7477            let highlights = if let Some(highlights) = cx.update(|cx| {
 7478                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7479            }) {
 7480                highlights.await.log_err()
 7481            } else {
 7482                None
 7483            };
 7484
 7485            if let Some(highlights) = highlights {
 7486                this.update(cx, |this, cx| {
 7487                    if this.pending_rename.is_some() {
 7488                        return;
 7489                    }
 7490
 7491                    let buffer = this.buffer.read(cx);
 7492                    if buffer
 7493                        .text_anchor_for_position(cursor_position, cx)
 7494                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7495                    {
 7496                        return;
 7497                    }
 7498
 7499                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7500                    let mut write_ranges = Vec::new();
 7501                    let mut read_ranges = Vec::new();
 7502                    for highlight in highlights {
 7503                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7504                        for (excerpt_id, _, excerpt_range) in
 7505                            buffer.excerpts_for_buffer(buffer_id, cx)
 7506                        {
 7507                            let start = highlight
 7508                                .range
 7509                                .start
 7510                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7511                            let end = highlight
 7512                                .range
 7513                                .end
 7514                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7515                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7516                                continue;
 7517                            }
 7518
 7519                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7520                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7521                                write_ranges.push(range);
 7522                            } else {
 7523                                read_ranges.push(range);
 7524                            }
 7525                        }
 7526                    }
 7527
 7528                    this.highlight_background(
 7529                        HighlightKey::DocumentHighlightRead,
 7530                        &read_ranges,
 7531                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7532                        cx,
 7533                    );
 7534                    this.highlight_background(
 7535                        HighlightKey::DocumentHighlightWrite,
 7536                        &write_ranges,
 7537                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7538                        cx,
 7539                    );
 7540                    cx.notify();
 7541                })
 7542                .log_err();
 7543            }
 7544        }));
 7545        None
 7546    }
 7547
 7548    fn prepare_highlight_query_from_selection(
 7549        &mut self,
 7550        snapshot: &DisplaySnapshot,
 7551        cx: &mut Context<Editor>,
 7552    ) -> Option<(String, Range<Anchor>)> {
 7553        if matches!(self.mode, EditorMode::SingleLine) {
 7554            return None;
 7555        }
 7556        if !EditorSettings::get_global(cx).selection_highlight {
 7557            return None;
 7558        }
 7559        if self.selections.count() != 1 || self.selections.line_mode() {
 7560            return None;
 7561        }
 7562        let selection = self.selections.newest::<Point>(&snapshot);
 7563        // If the selection spans multiple rows OR it is empty
 7564        if selection.start.row != selection.end.row
 7565            || selection.start.column == selection.end.column
 7566        {
 7567            return None;
 7568        }
 7569        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7570        let query = snapshot
 7571            .buffer_snapshot()
 7572            .text_for_range(selection_anchor_range.clone())
 7573            .collect::<String>();
 7574        if query.trim().is_empty() {
 7575            return None;
 7576        }
 7577        Some((query, selection_anchor_range))
 7578    }
 7579
 7580    #[ztracing::instrument(skip_all)]
 7581    fn update_selection_occurrence_highlights(
 7582        &mut self,
 7583        multi_buffer_snapshot: MultiBufferSnapshot,
 7584        query_text: String,
 7585        query_range: Range<Anchor>,
 7586        multi_buffer_range_to_query: Range<Point>,
 7587        use_debounce: bool,
 7588        window: &mut Window,
 7589        cx: &mut Context<Editor>,
 7590    ) -> Task<()> {
 7591        cx.spawn_in(window, async move |editor, cx| {
 7592            if use_debounce {
 7593                cx.background_executor()
 7594                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7595                    .await;
 7596            }
 7597            let match_task = cx.background_spawn(async move {
 7598                let buffer_ranges = multi_buffer_snapshot
 7599                    .range_to_buffer_ranges(
 7600                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7601                    )
 7602                    .into_iter()
 7603                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7604                let mut match_ranges = Vec::new();
 7605                let Ok(regex) = project::search::SearchQuery::text(
 7606                    query_text,
 7607                    false,
 7608                    false,
 7609                    false,
 7610                    Default::default(),
 7611                    Default::default(),
 7612                    false,
 7613                    None,
 7614                ) else {
 7615                    return Vec::default();
 7616                };
 7617                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7618                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7619                    match_ranges.extend(
 7620                        regex
 7621                            .search(
 7622                                buffer_snapshot,
 7623                                Some(search_range.start.0..search_range.end.0),
 7624                            )
 7625                            .await
 7626                            .into_iter()
 7627                            .filter_map(|match_range| {
 7628                                let match_start = buffer_snapshot
 7629                                    .anchor_after(search_range.start + match_range.start);
 7630                                let match_end = buffer_snapshot
 7631                                    .anchor_before(search_range.start + match_range.end);
 7632                                let match_anchor_range =
 7633                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7634                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7635                            }),
 7636                    );
 7637                }
 7638                match_ranges
 7639            });
 7640            let match_ranges = match_task.await;
 7641            editor
 7642                .update_in(cx, |editor, _, cx| {
 7643                    if use_debounce {
 7644                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7645                        editor.debounced_selection_highlight_complete = true;
 7646                    } else if editor.debounced_selection_highlight_complete {
 7647                        return;
 7648                    }
 7649                    if !match_ranges.is_empty() {
 7650                        editor.highlight_background(
 7651                            HighlightKey::SelectedTextHighlight,
 7652                            &match_ranges,
 7653                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7654                            cx,
 7655                        )
 7656                    }
 7657                })
 7658                .log_err();
 7659        })
 7660    }
 7661
 7662    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7663        struct NewlineFold;
 7664        let type_id = std::any::TypeId::of::<NewlineFold>();
 7665        if !self.mode.is_single_line() {
 7666            return;
 7667        }
 7668        let snapshot = self.snapshot(window, cx);
 7669        if snapshot.buffer_snapshot().max_point().row == 0 {
 7670            return;
 7671        }
 7672        let task = cx.background_spawn(async move {
 7673            let new_newlines = snapshot
 7674                .buffer_chars_at(MultiBufferOffset(0))
 7675                .filter_map(|(c, i)| {
 7676                    if c == '\n' {
 7677                        Some(
 7678                            snapshot.buffer_snapshot().anchor_after(i)
 7679                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7680                        )
 7681                    } else {
 7682                        None
 7683                    }
 7684                })
 7685                .collect::<Vec<_>>();
 7686            let existing_newlines = snapshot
 7687                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7688                .filter_map(|fold| {
 7689                    if fold.placeholder.type_tag == Some(type_id) {
 7690                        Some(fold.range.start..fold.range.end)
 7691                    } else {
 7692                        None
 7693                    }
 7694                })
 7695                .collect::<Vec<_>>();
 7696
 7697            (new_newlines, existing_newlines)
 7698        });
 7699        self.folding_newlines = cx.spawn(async move |this, cx| {
 7700            let (new_newlines, existing_newlines) = task.await;
 7701            if new_newlines == existing_newlines {
 7702                return;
 7703            }
 7704            let placeholder = FoldPlaceholder {
 7705                render: Arc::new(move |_, _, cx| {
 7706                    div()
 7707                        .bg(cx.theme().status().hint_background)
 7708                        .border_b_1()
 7709                        .size_full()
 7710                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7711                        .border_color(cx.theme().status().hint)
 7712                        .child("\\n")
 7713                        .into_any()
 7714                }),
 7715                constrain_width: false,
 7716                merge_adjacent: false,
 7717                type_tag: Some(type_id),
 7718                collapsed_text: None,
 7719            };
 7720            let creases = new_newlines
 7721                .into_iter()
 7722                .map(|range| Crease::simple(range, placeholder.clone()))
 7723                .collect();
 7724            this.update(cx, |this, cx| {
 7725                this.display_map.update(cx, |display_map, cx| {
 7726                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7727                    display_map.fold(creases, cx);
 7728                });
 7729            })
 7730            .ok();
 7731        });
 7732    }
 7733
 7734    #[ztracing::instrument(skip_all)]
 7735    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7736        if !self.lsp_data_enabled() {
 7737            return;
 7738        }
 7739        let cursor = self.selections.newest_anchor().head();
 7740        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7741
 7742        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7743            self.outline_symbols_at_cursor =
 7744                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7745            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7746            cx.notify();
 7747        } else {
 7748            let syntax = cx.theme().syntax().clone();
 7749            let background_task = cx.background_spawn(async move {
 7750                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7751            });
 7752            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7753                cx.spawn(async move |this, cx| {
 7754                    let symbols = background_task.await;
 7755                    this.update(cx, |this, cx| {
 7756                        this.outline_symbols_at_cursor = symbols;
 7757                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7758                        cx.notify();
 7759                    })
 7760                    .ok();
 7761                });
 7762        }
 7763    }
 7764
 7765    #[ztracing::instrument(skip_all)]
 7766    fn refresh_selected_text_highlights(
 7767        &mut self,
 7768        snapshot: &DisplaySnapshot,
 7769        on_buffer_edit: bool,
 7770        window: &mut Window,
 7771        cx: &mut Context<Editor>,
 7772    ) {
 7773        let Some((query_text, query_range)) =
 7774            self.prepare_highlight_query_from_selection(snapshot, cx)
 7775        else {
 7776            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7777            self.quick_selection_highlight_task.take();
 7778            self.debounced_selection_highlight_task.take();
 7779            self.debounced_selection_highlight_complete = false;
 7780            return;
 7781        };
 7782        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7783        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7784        let query_changed = self
 7785            .quick_selection_highlight_task
 7786            .as_ref()
 7787            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7788        if query_changed {
 7789            self.debounced_selection_highlight_complete = false;
 7790        }
 7791        if on_buffer_edit || query_changed {
 7792            let multi_buffer_visible_start = self
 7793                .scroll_manager
 7794                .native_anchor(&display_snapshot, cx)
 7795                .anchor
 7796                .to_point(&multi_buffer_snapshot);
 7797            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7798                multi_buffer_visible_start
 7799                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7800                Bias::Left,
 7801            );
 7802            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7803            self.quick_selection_highlight_task = Some((
 7804                query_range.clone(),
 7805                self.update_selection_occurrence_highlights(
 7806                    snapshot.buffer.clone(),
 7807                    query_text.clone(),
 7808                    query_range.clone(),
 7809                    multi_buffer_visible_range,
 7810                    false,
 7811                    window,
 7812                    cx,
 7813                ),
 7814            ));
 7815        }
 7816        if on_buffer_edit
 7817            || self
 7818                .debounced_selection_highlight_task
 7819                .as_ref()
 7820                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7821        {
 7822            let multi_buffer_start = multi_buffer_snapshot
 7823                .anchor_before(MultiBufferOffset(0))
 7824                .to_point(&multi_buffer_snapshot);
 7825            let multi_buffer_end = multi_buffer_snapshot
 7826                .anchor_after(multi_buffer_snapshot.len())
 7827                .to_point(&multi_buffer_snapshot);
 7828            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7829            self.debounced_selection_highlight_task = Some((
 7830                query_range.clone(),
 7831                self.update_selection_occurrence_highlights(
 7832                    snapshot.buffer.clone(),
 7833                    query_text,
 7834                    query_range,
 7835                    multi_buffer_full_range,
 7836                    true,
 7837                    window,
 7838                    cx,
 7839                ),
 7840            ));
 7841        }
 7842    }
 7843
 7844    pub fn refresh_edit_prediction(
 7845        &mut self,
 7846        debounce: bool,
 7847        user_requested: bool,
 7848        window: &mut Window,
 7849        cx: &mut Context<Self>,
 7850    ) -> Option<()> {
 7851        let provider = self.edit_prediction_provider()?;
 7852        let cursor = self.selections.newest_anchor().head();
 7853        let (buffer, cursor_buffer_position) =
 7854            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7855
 7856        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7857            return None;
 7858        }
 7859
 7860        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7861            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7862            return None;
 7863        }
 7864
 7865        self.update_visible_edit_prediction(window, cx);
 7866
 7867        if !user_requested
 7868            && (!self.should_show_edit_predictions()
 7869                || !self.is_focused(window)
 7870                || buffer.read(cx).is_empty())
 7871        {
 7872            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7873            return None;
 7874        }
 7875
 7876        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7877        Some(())
 7878    }
 7879
 7880    fn show_edit_predictions_in_menu(&self) -> bool {
 7881        match self.edit_prediction_settings {
 7882            EditPredictionSettings::Disabled => false,
 7883            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7884        }
 7885    }
 7886
 7887    pub fn edit_predictions_enabled(&self) -> bool {
 7888        match self.edit_prediction_settings {
 7889            EditPredictionSettings::Disabled => false,
 7890            EditPredictionSettings::Enabled { .. } => true,
 7891        }
 7892    }
 7893
 7894    fn edit_prediction_requires_modifier(&self) -> bool {
 7895        match self.edit_prediction_settings {
 7896            EditPredictionSettings::Disabled => false,
 7897            EditPredictionSettings::Enabled {
 7898                preview_requires_modifier,
 7899                ..
 7900            } => preview_requires_modifier,
 7901        }
 7902    }
 7903
 7904    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7905        if self.edit_prediction_provider.is_none() {
 7906            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7907            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7908            return;
 7909        }
 7910
 7911        let selection = self.selections.newest_anchor();
 7912        let cursor = selection.head();
 7913
 7914        if let Some((buffer, cursor_buffer_position)) =
 7915            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7916        {
 7917            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7918                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7919                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7920                return;
 7921            }
 7922            self.edit_prediction_settings =
 7923                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7924        }
 7925    }
 7926
 7927    fn edit_prediction_settings_at_position(
 7928        &self,
 7929        buffer: &Entity<Buffer>,
 7930        buffer_position: language::Anchor,
 7931        cx: &App,
 7932    ) -> EditPredictionSettings {
 7933        if !self.mode.is_full()
 7934            || !self.show_edit_predictions_override.unwrap_or(true)
 7935            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7936        {
 7937            return EditPredictionSettings::Disabled;
 7938        }
 7939
 7940        let buffer = buffer.read(cx);
 7941
 7942        let file = buffer.file();
 7943
 7944        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7945            return EditPredictionSettings::Disabled;
 7946        };
 7947
 7948        let by_provider = matches!(
 7949            self.menu_edit_predictions_policy,
 7950            MenuEditPredictionsPolicy::ByProvider
 7951        );
 7952
 7953        let show_in_menu = by_provider
 7954            && self
 7955                .edit_prediction_provider
 7956                .as_ref()
 7957                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7958
 7959        let preview_requires_modifier =
 7960            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7961
 7962        EditPredictionSettings::Enabled {
 7963            show_in_menu,
 7964            preview_requires_modifier,
 7965        }
 7966    }
 7967
 7968    fn should_show_edit_predictions(&self) -> bool {
 7969        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7970    }
 7971
 7972    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7973        matches!(
 7974            self.edit_prediction_preview,
 7975            EditPredictionPreview::Active { .. }
 7976        )
 7977    }
 7978
 7979    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7980        let cursor = self.selections.newest_anchor().head();
 7981        if let Some((buffer, cursor_position)) =
 7982            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7983        {
 7984            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7985        } else {
 7986            false
 7987        }
 7988    }
 7989
 7990    pub fn supports_minimap(&self, cx: &App) -> bool {
 7991        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7992    }
 7993
 7994    fn edit_predictions_enabled_in_buffer(
 7995        &self,
 7996        buffer: &Entity<Buffer>,
 7997        buffer_position: language::Anchor,
 7998        cx: &App,
 7999    ) -> bool {
 8000        maybe!({
 8001            if self.read_only(cx) {
 8002                return Some(false);
 8003            }
 8004            let provider = self.edit_prediction_provider()?;
 8005            if !provider.is_enabled(buffer, buffer_position, cx) {
 8006                return Some(false);
 8007            }
 8008            let buffer = buffer.read(cx);
 8009            let Some(file) = buffer.file() else {
 8010                return Some(true);
 8011            };
 8012            let settings = all_language_settings(Some(file), cx);
 8013            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8014        })
 8015        .unwrap_or(false)
 8016    }
 8017
 8018    pub fn show_edit_prediction(
 8019        &mut self,
 8020        _: &ShowEditPrediction,
 8021        window: &mut Window,
 8022        cx: &mut Context<Self>,
 8023    ) {
 8024        if !self.has_active_edit_prediction() {
 8025            self.refresh_edit_prediction(false, true, window, cx);
 8026            return;
 8027        }
 8028
 8029        self.update_visible_edit_prediction(window, cx);
 8030    }
 8031
 8032    pub fn display_cursor_names(
 8033        &mut self,
 8034        _: &DisplayCursorNames,
 8035        window: &mut Window,
 8036        cx: &mut Context<Self>,
 8037    ) {
 8038        self.show_cursor_names(window, cx);
 8039    }
 8040
 8041    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8042        self.show_cursor_names = true;
 8043        cx.notify();
 8044        cx.spawn_in(window, async move |this, cx| {
 8045            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8046            this.update(cx, |this, cx| {
 8047                this.show_cursor_names = false;
 8048                cx.notify()
 8049            })
 8050            .ok()
 8051        })
 8052        .detach();
 8053    }
 8054
 8055    pub fn accept_partial_edit_prediction(
 8056        &mut self,
 8057        granularity: EditPredictionGranularity,
 8058        window: &mut Window,
 8059        cx: &mut Context<Self>,
 8060    ) {
 8061        if self.show_edit_predictions_in_menu() {
 8062            self.hide_context_menu(window, cx);
 8063        }
 8064
 8065        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8066            return;
 8067        };
 8068
 8069        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8070            return;
 8071        }
 8072
 8073        match &active_edit_prediction.completion {
 8074            EditPrediction::MoveWithin { target, .. } => {
 8075                let target = *target;
 8076
 8077                if matches!(granularity, EditPredictionGranularity::Full) {
 8078                    if let Some(position_map) = &self.last_position_map {
 8079                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8080                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8081
 8082                        if is_visible || !self.edit_prediction_requires_modifier() {
 8083                            self.unfold_ranges(&[target..target], true, false, cx);
 8084                            self.change_selections(
 8085                                SelectionEffects::scroll(Autoscroll::newest()),
 8086                                window,
 8087                                cx,
 8088                                |selections| {
 8089                                    selections.select_anchor_ranges([target..target]);
 8090                                },
 8091                            );
 8092                            self.clear_row_highlights::<EditPredictionPreview>();
 8093                            self.edit_prediction_preview
 8094                                .set_previous_scroll_position(None);
 8095                        } else {
 8096                            // Highlight and request scroll
 8097                            self.edit_prediction_preview
 8098                                .set_previous_scroll_position(Some(
 8099                                    position_map.snapshot.scroll_anchor,
 8100                                ));
 8101                            self.highlight_rows::<EditPredictionPreview>(
 8102                                target..target,
 8103                                cx.theme().colors().editor_highlighted_line_background,
 8104                                RowHighlightOptions {
 8105                                    autoscroll: true,
 8106                                    ..Default::default()
 8107                                },
 8108                                cx,
 8109                            );
 8110                            self.request_autoscroll(Autoscroll::fit(), cx);
 8111                        }
 8112                    }
 8113                } else {
 8114                    self.change_selections(
 8115                        SelectionEffects::scroll(Autoscroll::newest()),
 8116                        window,
 8117                        cx,
 8118                        |selections| {
 8119                            selections.select_anchor_ranges([target..target]);
 8120                        },
 8121                    );
 8122                }
 8123            }
 8124            EditPrediction::MoveOutside { snapshot, target } => {
 8125                if let Some(workspace) = self.workspace() {
 8126                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8127                        .detach_and_log_err(cx);
 8128                }
 8129            }
 8130            EditPrediction::Edit {
 8131                edits,
 8132                cursor_position,
 8133                ..
 8134            } => {
 8135                self.report_edit_prediction_event(
 8136                    active_edit_prediction.completion_id.clone(),
 8137                    true,
 8138                    cx,
 8139                );
 8140
 8141                match granularity {
 8142                    EditPredictionGranularity::Full => {
 8143                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8144
 8145                        // Compute fallback cursor position BEFORE applying the edit,
 8146                        // so the anchor tracks through the edit correctly
 8147                        let fallback_cursor_target = {
 8148                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8149                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8150                        };
 8151
 8152                        self.buffer.update(cx, |buffer, cx| {
 8153                            buffer.edit(edits.iter().cloned(), None, cx)
 8154                        });
 8155
 8156                        if let Some(provider) = self.edit_prediction_provider() {
 8157                            provider.accept(cx);
 8158                        }
 8159
 8160                        // Resolve cursor position after the edit is applied
 8161                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8162                            // The anchor tracks through the edit, then we add the offset
 8163                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8164                            let base_offset = anchor.to_offset(&snapshot).0;
 8165                            let target_offset =
 8166                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8167                            snapshot.anchor_after(target_offset)
 8168                        } else {
 8169                            fallback_cursor_target
 8170                        };
 8171
 8172                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8173                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8174                        });
 8175
 8176                        let selections = self.selections.disjoint_anchors_arc();
 8177                        if let Some(transaction_id_now) =
 8178                            self.buffer.read(cx).last_transaction_id(cx)
 8179                        {
 8180                            if transaction_id_prev != Some(transaction_id_now) {
 8181                                self.selection_history
 8182                                    .insert_transaction(transaction_id_now, selections);
 8183                            }
 8184                        }
 8185
 8186                        self.update_visible_edit_prediction(window, cx);
 8187                        if self.active_edit_prediction.is_none() {
 8188                            self.refresh_edit_prediction(true, true, window, cx);
 8189                        }
 8190                        cx.notify();
 8191                    }
 8192                    _ => {
 8193                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8194                        let cursor_offset = self
 8195                            .selections
 8196                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8197                            .head();
 8198
 8199                        let insertion = edits.iter().find_map(|(range, text)| {
 8200                            let range = range.to_offset(&snapshot);
 8201                            if range.is_empty() && range.start == cursor_offset {
 8202                                Some(text)
 8203                            } else {
 8204                                None
 8205                            }
 8206                        });
 8207
 8208                        if let Some(text) = insertion {
 8209                            let text_to_insert = match granularity {
 8210                                EditPredictionGranularity::Word => {
 8211                                    let mut partial = text
 8212                                        .chars()
 8213                                        .by_ref()
 8214                                        .take_while(|c| c.is_alphabetic())
 8215                                        .collect::<String>();
 8216                                    if partial.is_empty() {
 8217                                        partial = text
 8218                                            .chars()
 8219                                            .by_ref()
 8220                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8221                                            .collect::<String>();
 8222                                    }
 8223                                    partial
 8224                                }
 8225                                EditPredictionGranularity::Line => {
 8226                                    if let Some(line) = text.split_inclusive('\n').next() {
 8227                                        line.to_string()
 8228                                    } else {
 8229                                        text.to_string()
 8230                                    }
 8231                                }
 8232                                EditPredictionGranularity::Full => unreachable!(),
 8233                            };
 8234
 8235                            cx.emit(EditorEvent::InputHandled {
 8236                                utf16_range_to_replace: None,
 8237                                text: text_to_insert.clone().into(),
 8238                            });
 8239
 8240                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8241                            self.refresh_edit_prediction(true, true, window, cx);
 8242                            cx.notify();
 8243                        } else {
 8244                            self.accept_partial_edit_prediction(
 8245                                EditPredictionGranularity::Full,
 8246                                window,
 8247                                cx,
 8248                            );
 8249                        }
 8250                    }
 8251                }
 8252            }
 8253        }
 8254
 8255        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8256    }
 8257
 8258    pub fn accept_next_word_edit_prediction(
 8259        &mut self,
 8260        _: &AcceptNextWordEditPrediction,
 8261        window: &mut Window,
 8262        cx: &mut Context<Self>,
 8263    ) {
 8264        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8265    }
 8266
 8267    pub fn accept_next_line_edit_prediction(
 8268        &mut self,
 8269        _: &AcceptNextLineEditPrediction,
 8270        window: &mut Window,
 8271        cx: &mut Context<Self>,
 8272    ) {
 8273        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8274    }
 8275
 8276    pub fn accept_edit_prediction(
 8277        &mut self,
 8278        _: &AcceptEditPrediction,
 8279        window: &mut Window,
 8280        cx: &mut Context<Self>,
 8281    ) {
 8282        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8283    }
 8284
 8285    fn discard_edit_prediction(
 8286        &mut self,
 8287        reason: EditPredictionDiscardReason,
 8288        cx: &mut Context<Self>,
 8289    ) -> bool {
 8290        if reason == EditPredictionDiscardReason::Rejected {
 8291            let completion_id = self
 8292                .active_edit_prediction
 8293                .as_ref()
 8294                .and_then(|active_completion| active_completion.completion_id.clone());
 8295
 8296            self.report_edit_prediction_event(completion_id, false, cx);
 8297        }
 8298
 8299        if let Some(provider) = self.edit_prediction_provider() {
 8300            provider.discard(reason, cx);
 8301        }
 8302
 8303        self.take_active_edit_prediction(cx)
 8304    }
 8305
 8306    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8307        let Some(provider) = self.edit_prediction_provider() else {
 8308            return;
 8309        };
 8310
 8311        let Some((_, buffer, _)) = self
 8312            .buffer
 8313            .read(cx)
 8314            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8315        else {
 8316            return;
 8317        };
 8318
 8319        let extension = buffer
 8320            .read(cx)
 8321            .file()
 8322            .and_then(|file| Some(file.path().extension()?.to_string()));
 8323
 8324        let event_type = match accepted {
 8325            true => "Edit Prediction Accepted",
 8326            false => "Edit Prediction Discarded",
 8327        };
 8328        telemetry::event!(
 8329            event_type,
 8330            provider = provider.name(),
 8331            prediction_id = id,
 8332            suggestion_accepted = accepted,
 8333            file_extension = extension,
 8334        );
 8335    }
 8336
 8337    fn open_editor_at_anchor(
 8338        snapshot: &language::BufferSnapshot,
 8339        target: language::Anchor,
 8340        workspace: &Entity<Workspace>,
 8341        window: &mut Window,
 8342        cx: &mut App,
 8343    ) -> Task<Result<()>> {
 8344        workspace.update(cx, |workspace, cx| {
 8345            let path = snapshot.file().map(|file| file.full_path(cx));
 8346            let Some(path) =
 8347                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8348            else {
 8349                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8350            };
 8351            let target = text::ToPoint::to_point(&target, snapshot);
 8352            let item = workspace.open_path(path, None, true, window, cx);
 8353            window.spawn(cx, async move |cx| {
 8354                let Some(editor) = item.await?.downcast::<Editor>() else {
 8355                    return Ok(());
 8356                };
 8357                editor
 8358                    .update_in(cx, |editor, window, cx| {
 8359                        editor.go_to_singleton_buffer_point(target, window, cx);
 8360                    })
 8361                    .ok();
 8362                anyhow::Ok(())
 8363            })
 8364        })
 8365    }
 8366
 8367    pub fn has_active_edit_prediction(&self) -> bool {
 8368        self.active_edit_prediction.is_some()
 8369    }
 8370
 8371    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8372        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8373            return false;
 8374        };
 8375
 8376        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8377        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8378        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8379        true
 8380    }
 8381
 8382    /// Returns true when we're displaying the edit prediction popover below the cursor
 8383    /// like we are not previewing and the LSP autocomplete menu is visible
 8384    /// or we are in `when_holding_modifier` mode.
 8385    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8386        if self.edit_prediction_preview_is_active()
 8387            || !self.show_edit_predictions_in_menu()
 8388            || !self.edit_predictions_enabled()
 8389        {
 8390            return false;
 8391        }
 8392
 8393        if self.has_visible_completions_menu() {
 8394            return true;
 8395        }
 8396
 8397        has_completion && self.edit_prediction_requires_modifier()
 8398    }
 8399
 8400    fn handle_modifiers_changed(
 8401        &mut self,
 8402        modifiers: Modifiers,
 8403        position_map: &PositionMap,
 8404        window: &mut Window,
 8405        cx: &mut Context<Self>,
 8406    ) {
 8407        // Ensure that the edit prediction preview is updated, even when not
 8408        // enabled, if there's an active edit prediction preview.
 8409        if self.show_edit_predictions_in_menu()
 8410            || matches!(
 8411                self.edit_prediction_preview,
 8412                EditPredictionPreview::Active { .. }
 8413            )
 8414        {
 8415            self.update_edit_prediction_preview(&modifiers, window, cx);
 8416        }
 8417
 8418        self.update_selection_mode(&modifiers, position_map, window, cx);
 8419
 8420        let mouse_position = window.mouse_position();
 8421        if !position_map.text_hitbox.is_hovered(window) {
 8422            return;
 8423        }
 8424
 8425        self.update_hovered_link(
 8426            position_map.point_for_position(mouse_position),
 8427            &position_map.snapshot,
 8428            modifiers,
 8429            window,
 8430            cx,
 8431        )
 8432    }
 8433
 8434    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8435        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8436            MultiCursorModifier::Alt => modifiers.secondary(),
 8437            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8438        }
 8439    }
 8440
 8441    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8442        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8443            MultiCursorModifier::Alt => modifiers.alt,
 8444            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8445        }
 8446    }
 8447
 8448    fn columnar_selection_mode(
 8449        modifiers: &Modifiers,
 8450        cx: &mut Context<Self>,
 8451    ) -> Option<ColumnarMode> {
 8452        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8453            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8454                Some(ColumnarMode::FromMouse)
 8455            } else if Self::is_alt_pressed(modifiers, cx) {
 8456                Some(ColumnarMode::FromSelection)
 8457            } else {
 8458                None
 8459            }
 8460        } else {
 8461            None
 8462        }
 8463    }
 8464
 8465    fn update_selection_mode(
 8466        &mut self,
 8467        modifiers: &Modifiers,
 8468        position_map: &PositionMap,
 8469        window: &mut Window,
 8470        cx: &mut Context<Self>,
 8471    ) {
 8472        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8473            return;
 8474        };
 8475        if self.selections.pending_anchor().is_none() {
 8476            return;
 8477        }
 8478
 8479        let mouse_position = window.mouse_position();
 8480        let point_for_position = position_map.point_for_position(mouse_position);
 8481        let position = point_for_position.previous_valid;
 8482
 8483        self.select(
 8484            SelectPhase::BeginColumnar {
 8485                position,
 8486                reset: false,
 8487                mode,
 8488                goal_column: point_for_position.exact_unclipped.column(),
 8489            },
 8490            window,
 8491            cx,
 8492        );
 8493    }
 8494
 8495    fn update_edit_prediction_preview(
 8496        &mut self,
 8497        modifiers: &Modifiers,
 8498        window: &mut Window,
 8499        cx: &mut Context<Self>,
 8500    ) {
 8501        let mut modifiers_held = false;
 8502
 8503        // Check bindings for all granularities.
 8504        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8505        let granularities = [
 8506            EditPredictionGranularity::Full,
 8507            EditPredictionGranularity::Line,
 8508            EditPredictionGranularity::Word,
 8509        ];
 8510
 8511        for granularity in granularities {
 8512            if let Some(keystroke) = self
 8513                .accept_edit_prediction_keybind(granularity, window, cx)
 8514                .keystroke()
 8515            {
 8516                modifiers_held = modifiers_held
 8517                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8518            }
 8519        }
 8520
 8521        if modifiers_held {
 8522            if matches!(
 8523                self.edit_prediction_preview,
 8524                EditPredictionPreview::Inactive { .. }
 8525            ) {
 8526                self.edit_prediction_preview = EditPredictionPreview::Active {
 8527                    previous_scroll_position: None,
 8528                    since: Instant::now(),
 8529                };
 8530
 8531                self.update_visible_edit_prediction(window, cx);
 8532                cx.notify();
 8533            }
 8534        } else if let EditPredictionPreview::Active {
 8535            previous_scroll_position,
 8536            since,
 8537        } = self.edit_prediction_preview
 8538        {
 8539            if let (Some(previous_scroll_position), Some(position_map)) =
 8540                (previous_scroll_position, self.last_position_map.as_ref())
 8541            {
 8542                self.set_scroll_position(
 8543                    previous_scroll_position
 8544                        .scroll_position(&position_map.snapshot.display_snapshot),
 8545                    window,
 8546                    cx,
 8547                );
 8548            }
 8549
 8550            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8551                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8552            };
 8553            self.clear_row_highlights::<EditPredictionPreview>();
 8554            self.update_visible_edit_prediction(window, cx);
 8555            cx.notify();
 8556        }
 8557    }
 8558
 8559    fn update_visible_edit_prediction(
 8560        &mut self,
 8561        _window: &mut Window,
 8562        cx: &mut Context<Self>,
 8563    ) -> Option<()> {
 8564        if self.ime_transaction.is_some() {
 8565            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8566            return None;
 8567        }
 8568
 8569        let selection = self.selections.newest_anchor();
 8570        let cursor = selection.head();
 8571        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8572
 8573        // Check project-level disable_ai setting for the current buffer
 8574        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8575            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8576                return None;
 8577            }
 8578        }
 8579        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8580        let excerpt_id = cursor.excerpt_id;
 8581
 8582        let show_in_menu = self.show_edit_predictions_in_menu();
 8583        let completions_menu_has_precedence = !show_in_menu
 8584            && (self.context_menu.borrow().is_some()
 8585                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8586
 8587        if completions_menu_has_precedence
 8588            || !offset_selection.is_empty()
 8589            || self
 8590                .active_edit_prediction
 8591                .as_ref()
 8592                .is_some_and(|completion| {
 8593                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8594                        return false;
 8595                    };
 8596                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8597                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8598                    !invalidation_range.contains(&offset_selection.head())
 8599                })
 8600        {
 8601            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8602            return None;
 8603        }
 8604
 8605        self.take_active_edit_prediction(cx);
 8606        let Some(provider) = self.edit_prediction_provider() else {
 8607            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8608            return None;
 8609        };
 8610
 8611        let (buffer, cursor_buffer_position) =
 8612            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8613
 8614        self.edit_prediction_settings =
 8615            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8616
 8617        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8618
 8619        if self.edit_prediction_indent_conflict {
 8620            let cursor_point = cursor.to_point(&multibuffer);
 8621            let mut suggested_indent = None;
 8622            multibuffer.suggested_indents_callback(
 8623                cursor_point.row..cursor_point.row + 1,
 8624                &mut |_, indent| {
 8625                    suggested_indent = Some(indent);
 8626                    ControlFlow::Break(())
 8627                },
 8628                cx,
 8629            );
 8630
 8631            if let Some(indent) = suggested_indent
 8632                && indent.len == cursor_point.column
 8633            {
 8634                self.edit_prediction_indent_conflict = false;
 8635            }
 8636        }
 8637
 8638        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8639
 8640        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8641        {
 8642            edit_prediction_types::EditPrediction::Local {
 8643                id,
 8644                edits,
 8645                cursor_position,
 8646                edit_preview,
 8647            } => (id, edits, cursor_position, edit_preview),
 8648            edit_prediction_types::EditPrediction::Jump {
 8649                id,
 8650                snapshot,
 8651                target,
 8652            } => {
 8653                if let Some(provider) = &self.edit_prediction_provider {
 8654                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8655                }
 8656                self.stale_edit_prediction_in_menu = None;
 8657                self.active_edit_prediction = Some(EditPredictionState {
 8658                    inlay_ids: vec![],
 8659                    completion: EditPrediction::MoveOutside { snapshot, target },
 8660                    completion_id: id,
 8661                    invalidation_range: None,
 8662                });
 8663                cx.notify();
 8664                return Some(());
 8665            }
 8666        };
 8667
 8668        let edits = edits
 8669            .into_iter()
 8670            .flat_map(|(range, new_text)| {
 8671                Some((
 8672                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8673                    new_text,
 8674                ))
 8675            })
 8676            .collect::<Vec<_>>();
 8677        if edits.is_empty() {
 8678            return None;
 8679        }
 8680
 8681        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8682            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8683            Some((anchor, predicted.offset))
 8684        });
 8685
 8686        let first_edit_start = edits.first().unwrap().0.start;
 8687        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8688        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8689
 8690        let last_edit_end = edits.last().unwrap().0.end;
 8691        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8692        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8693
 8694        let cursor_row = cursor.to_point(&multibuffer).row;
 8695
 8696        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8697
 8698        let mut inlay_ids = Vec::new();
 8699        let invalidation_row_range;
 8700        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8701            Some(cursor_row..edit_end_row)
 8702        } else if cursor_row > edit_end_row {
 8703            Some(edit_start_row..cursor_row)
 8704        } else {
 8705            None
 8706        };
 8707        let supports_jump = self
 8708            .edit_prediction_provider
 8709            .as_ref()
 8710            .map(|provider| provider.provider.supports_jump_to_edit())
 8711            .unwrap_or(true);
 8712
 8713        let is_move = supports_jump
 8714            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8715        let completion = if is_move {
 8716            if let Some(provider) = &self.edit_prediction_provider {
 8717                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8718            }
 8719            invalidation_row_range =
 8720                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8721            let target = first_edit_start;
 8722            EditPrediction::MoveWithin { target, snapshot }
 8723        } else {
 8724            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8725                && !self.edit_predictions_hidden_for_vim_mode;
 8726
 8727            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8728                if provider.show_tab_accept_marker() {
 8729                    EditDisplayMode::TabAccept
 8730                } else {
 8731                    EditDisplayMode::Inline
 8732                }
 8733            } else {
 8734                EditDisplayMode::DiffPopover
 8735            };
 8736
 8737            if show_completions_in_buffer {
 8738                if let Some(provider) = &self.edit_prediction_provider {
 8739                    let suggestion_display_type = match display_mode {
 8740                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8741                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8742                            SuggestionDisplayType::GhostText
 8743                        }
 8744                    };
 8745                    provider.provider.did_show(suggestion_display_type, cx);
 8746                }
 8747                if edits
 8748                    .iter()
 8749                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8750                {
 8751                    let mut inlays = Vec::new();
 8752                    for (range, new_text) in &edits {
 8753                        let inlay = Inlay::edit_prediction(
 8754                            post_inc(&mut self.next_inlay_id),
 8755                            range.start,
 8756                            new_text.as_ref(),
 8757                        );
 8758                        inlay_ids.push(inlay.id);
 8759                        inlays.push(inlay);
 8760                    }
 8761
 8762                    self.splice_inlays(&[], inlays, cx);
 8763                } else {
 8764                    let background_color = cx.theme().status().deleted_background;
 8765                    self.highlight_text(
 8766                        HighlightKey::EditPredictionHighlight,
 8767                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8768                        HighlightStyle {
 8769                            background_color: Some(background_color),
 8770                            ..Default::default()
 8771                        },
 8772                        cx,
 8773                    );
 8774                }
 8775            }
 8776
 8777            invalidation_row_range = edit_start_row..edit_end_row;
 8778
 8779            EditPrediction::Edit {
 8780                edits,
 8781                cursor_position,
 8782                edit_preview,
 8783                display_mode,
 8784                snapshot,
 8785            }
 8786        };
 8787
 8788        let invalidation_range = multibuffer
 8789            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8790            ..multibuffer.anchor_after(Point::new(
 8791                invalidation_row_range.end,
 8792                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8793            ));
 8794
 8795        self.stale_edit_prediction_in_menu = None;
 8796        self.active_edit_prediction = Some(EditPredictionState {
 8797            inlay_ids,
 8798            completion,
 8799            completion_id,
 8800            invalidation_range: Some(invalidation_range),
 8801        });
 8802
 8803        cx.notify();
 8804
 8805        Some(())
 8806    }
 8807
 8808    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8809        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8810    }
 8811
 8812    fn clear_tasks(&mut self) {
 8813        self.tasks.clear()
 8814    }
 8815
 8816    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8817        if self.tasks.insert(key, value).is_some() {
 8818            // This case should hopefully be rare, but just in case...
 8819            log::error!(
 8820                "multiple different run targets found on a single line, only the last target will be rendered"
 8821            )
 8822        }
 8823    }
 8824
 8825    /// Get all display points of breakpoints that will be rendered within editor
 8826    ///
 8827    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8828    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8829    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8830    fn active_breakpoints(
 8831        &self,
 8832        range: Range<DisplayRow>,
 8833        window: &mut Window,
 8834        cx: &mut Context<Self>,
 8835    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8836        let mut breakpoint_display_points = HashMap::default();
 8837
 8838        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8839            return breakpoint_display_points;
 8840        };
 8841
 8842        let snapshot = self.snapshot(window, cx);
 8843
 8844        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8845        let Some(project) = self.project() else {
 8846            return breakpoint_display_points;
 8847        };
 8848
 8849        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8850            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8851
 8852        for (buffer_snapshot, range, excerpt_id) in
 8853            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8854        {
 8855            let Some(buffer) = project
 8856                .read(cx)
 8857                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8858            else {
 8859                continue;
 8860            };
 8861            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8862                &buffer,
 8863                Some(
 8864                    buffer_snapshot.anchor_before(range.start)
 8865                        ..buffer_snapshot.anchor_after(range.end),
 8866                ),
 8867                buffer_snapshot,
 8868                cx,
 8869            );
 8870            for (breakpoint, state) in breakpoints {
 8871                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8872                let position = multi_buffer_anchor
 8873                    .to_point(&multi_buffer_snapshot)
 8874                    .to_display_point(&snapshot);
 8875
 8876                breakpoint_display_points.insert(
 8877                    position.row(),
 8878                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8879                );
 8880            }
 8881        }
 8882
 8883        breakpoint_display_points
 8884    }
 8885
 8886    fn breakpoint_context_menu(
 8887        &self,
 8888        anchor: Anchor,
 8889        window: &mut Window,
 8890        cx: &mut Context<Self>,
 8891    ) -> Entity<ui::ContextMenu> {
 8892        let weak_editor = cx.weak_entity();
 8893        let focus_handle = self.focus_handle(cx);
 8894
 8895        let row = self
 8896            .buffer
 8897            .read(cx)
 8898            .snapshot(cx)
 8899            .summary_for_anchor::<Point>(&anchor)
 8900            .row;
 8901
 8902        let breakpoint = self
 8903            .breakpoint_at_row(row, window, cx)
 8904            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8905
 8906        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8907            "Edit Log Breakpoint"
 8908        } else {
 8909            "Set Log Breakpoint"
 8910        };
 8911
 8912        let condition_breakpoint_msg = if breakpoint
 8913            .as_ref()
 8914            .is_some_and(|bp| bp.1.condition.is_some())
 8915        {
 8916            "Edit Condition Breakpoint"
 8917        } else {
 8918            "Set Condition Breakpoint"
 8919        };
 8920
 8921        let hit_condition_breakpoint_msg = if breakpoint
 8922            .as_ref()
 8923            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8924        {
 8925            "Edit Hit Condition Breakpoint"
 8926        } else {
 8927            "Set Hit Condition Breakpoint"
 8928        };
 8929
 8930        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8931            "Unset Breakpoint"
 8932        } else {
 8933            "Set Breakpoint"
 8934        };
 8935
 8936        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8937
 8938        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8939            BreakpointState::Enabled => Some("Disable"),
 8940            BreakpointState::Disabled => Some("Enable"),
 8941        });
 8942
 8943        let (anchor, breakpoint) =
 8944            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8945
 8946        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8947            menu.on_blur_subscription(Subscription::new(|| {}))
 8948                .context(focus_handle)
 8949                .when(run_to_cursor, |this| {
 8950                    let weak_editor = weak_editor.clone();
 8951                    this.entry("Run to Cursor", None, move |window, cx| {
 8952                        weak_editor
 8953                            .update(cx, |editor, cx| {
 8954                                editor.change_selections(
 8955                                    SelectionEffects::no_scroll(),
 8956                                    window,
 8957                                    cx,
 8958                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8959                                );
 8960                            })
 8961                            .ok();
 8962
 8963                        window.dispatch_action(Box::new(RunToCursor), cx);
 8964                    })
 8965                    .separator()
 8966                })
 8967                .when_some(toggle_state_msg, |this, msg| {
 8968                    this.entry(msg, None, {
 8969                        let weak_editor = weak_editor.clone();
 8970                        let breakpoint = breakpoint.clone();
 8971                        move |_window, cx| {
 8972                            weak_editor
 8973                                .update(cx, |this, cx| {
 8974                                    this.edit_breakpoint_at_anchor(
 8975                                        anchor,
 8976                                        breakpoint.as_ref().clone(),
 8977                                        BreakpointEditAction::InvertState,
 8978                                        cx,
 8979                                    );
 8980                                })
 8981                                .log_err();
 8982                        }
 8983                    })
 8984                })
 8985                .entry(set_breakpoint_msg, None, {
 8986                    let weak_editor = weak_editor.clone();
 8987                    let breakpoint = breakpoint.clone();
 8988                    move |_window, cx| {
 8989                        weak_editor
 8990                            .update(cx, |this, cx| {
 8991                                this.edit_breakpoint_at_anchor(
 8992                                    anchor,
 8993                                    breakpoint.as_ref().clone(),
 8994                                    BreakpointEditAction::Toggle,
 8995                                    cx,
 8996                                );
 8997                            })
 8998                            .log_err();
 8999                    }
 9000                })
 9001                .entry(log_breakpoint_msg, None, {
 9002                    let breakpoint = breakpoint.clone();
 9003                    let weak_editor = weak_editor.clone();
 9004                    move |window, cx| {
 9005                        weak_editor
 9006                            .update(cx, |this, cx| {
 9007                                this.add_edit_breakpoint_block(
 9008                                    anchor,
 9009                                    breakpoint.as_ref(),
 9010                                    BreakpointPromptEditAction::Log,
 9011                                    window,
 9012                                    cx,
 9013                                );
 9014                            })
 9015                            .log_err();
 9016                    }
 9017                })
 9018                .entry(condition_breakpoint_msg, None, {
 9019                    let breakpoint = breakpoint.clone();
 9020                    let weak_editor = weak_editor.clone();
 9021                    move |window, cx| {
 9022                        weak_editor
 9023                            .update(cx, |this, cx| {
 9024                                this.add_edit_breakpoint_block(
 9025                                    anchor,
 9026                                    breakpoint.as_ref(),
 9027                                    BreakpointPromptEditAction::Condition,
 9028                                    window,
 9029                                    cx,
 9030                                );
 9031                            })
 9032                            .log_err();
 9033                    }
 9034                })
 9035                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9036                    weak_editor
 9037                        .update(cx, |this, cx| {
 9038                            this.add_edit_breakpoint_block(
 9039                                anchor,
 9040                                breakpoint.as_ref(),
 9041                                BreakpointPromptEditAction::HitCondition,
 9042                                window,
 9043                                cx,
 9044                            );
 9045                        })
 9046                        .log_err();
 9047                })
 9048        })
 9049    }
 9050
 9051    fn render_breakpoint(
 9052        &self,
 9053        position: Anchor,
 9054        row: DisplayRow,
 9055        breakpoint: &Breakpoint,
 9056        state: Option<BreakpointSessionState>,
 9057        cx: &mut Context<Self>,
 9058    ) -> IconButton {
 9059        let is_rejected = state.is_some_and(|s| !s.verified);
 9060        // Is it a breakpoint that shows up when hovering over gutter?
 9061        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9062            (false, false),
 9063            |PhantomBreakpointIndicator {
 9064                 is_active,
 9065                 display_row,
 9066                 collides_with_existing_breakpoint,
 9067             }| {
 9068                (
 9069                    is_active && display_row == row,
 9070                    collides_with_existing_breakpoint,
 9071                )
 9072            },
 9073        );
 9074
 9075        let (color, icon) = {
 9076            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9077                (false, false) => ui::IconName::DebugBreakpoint,
 9078                (true, false) => ui::IconName::DebugLogBreakpoint,
 9079                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9080                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9081            };
 9082
 9083            let theme_colors = cx.theme().colors();
 9084
 9085            let color = if is_phantom {
 9086                if collides_with_existing {
 9087                    Color::Custom(
 9088                        theme_colors
 9089                            .debugger_accent
 9090                            .blend(theme_colors.text.opacity(0.6)),
 9091                    )
 9092                } else {
 9093                    Color::Hint
 9094                }
 9095            } else if is_rejected {
 9096                Color::Disabled
 9097            } else {
 9098                Color::Debugger
 9099            };
 9100
 9101            (color, icon)
 9102        };
 9103
 9104        let breakpoint = Arc::from(breakpoint.clone());
 9105
 9106        let alt_as_text = gpui::Keystroke {
 9107            modifiers: Modifiers::secondary_key(),
 9108            ..Default::default()
 9109        };
 9110        let primary_action_text = if breakpoint.is_disabled() {
 9111            "Enable breakpoint"
 9112        } else if is_phantom && !collides_with_existing {
 9113            "Set breakpoint"
 9114        } else {
 9115            "Unset breakpoint"
 9116        };
 9117        let focus_handle = self.focus_handle.clone();
 9118
 9119        let meta = if is_rejected {
 9120            SharedString::from("No executable code is associated with this line.")
 9121        } else if collides_with_existing && !breakpoint.is_disabled() {
 9122            SharedString::from(format!(
 9123                "{alt_as_text}-click to disable,\nright-click for more options."
 9124            ))
 9125        } else {
 9126            SharedString::from("Right-click for more options.")
 9127        };
 9128        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9129            .icon_size(IconSize::XSmall)
 9130            .size(ui::ButtonSize::None)
 9131            .when(is_rejected, |this| {
 9132                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9133            })
 9134            .icon_color(color)
 9135            .style(ButtonStyle::Transparent)
 9136            .on_click(cx.listener({
 9137                move |editor, event: &ClickEvent, window, cx| {
 9138                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9139                        BreakpointEditAction::InvertState
 9140                    } else {
 9141                        BreakpointEditAction::Toggle
 9142                    };
 9143
 9144                    window.focus(&editor.focus_handle(cx), cx);
 9145                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9146                    editor.edit_breakpoint_at_anchor(
 9147                        position,
 9148                        breakpoint.as_ref().clone(),
 9149                        edit_action,
 9150                        cx,
 9151                    );
 9152                }
 9153            }))
 9154            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9155                editor.set_breakpoint_context_menu(
 9156                    row,
 9157                    Some(position),
 9158                    event.position(),
 9159                    window,
 9160                    cx,
 9161                );
 9162            }))
 9163            .tooltip(move |_window, cx| {
 9164                Tooltip::with_meta_in(
 9165                    primary_action_text,
 9166                    Some(&ToggleBreakpoint),
 9167                    meta.clone(),
 9168                    &focus_handle,
 9169                    cx,
 9170                )
 9171            })
 9172    }
 9173
 9174    fn build_tasks_context(
 9175        project: &Entity<Project>,
 9176        buffer: &Entity<Buffer>,
 9177        buffer_row: u32,
 9178        tasks: &Arc<RunnableTasks>,
 9179        cx: &mut Context<Self>,
 9180    ) -> Task<Option<task::TaskContext>> {
 9181        let position = Point::new(buffer_row, tasks.column);
 9182        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9183        let location = Location {
 9184            buffer: buffer.clone(),
 9185            range: range_start..range_start,
 9186        };
 9187        // Fill in the environmental variables from the tree-sitter captures
 9188        let mut captured_task_variables = TaskVariables::default();
 9189        for (capture_name, value) in tasks.extra_variables.clone() {
 9190            captured_task_variables.insert(
 9191                task::VariableName::Custom(capture_name.into()),
 9192                value.clone(),
 9193            );
 9194        }
 9195        project.update(cx, |project, cx| {
 9196            project.task_store().update(cx, |task_store, cx| {
 9197                task_store.task_context_for_location(captured_task_variables, location, cx)
 9198            })
 9199        })
 9200    }
 9201
 9202    pub fn spawn_nearest_task(
 9203        &mut self,
 9204        action: &SpawnNearestTask,
 9205        window: &mut Window,
 9206        cx: &mut Context<Self>,
 9207    ) {
 9208        let Some((workspace, _)) = self.workspace.clone() else {
 9209            return;
 9210        };
 9211        let Some(project) = self.project.clone() else {
 9212            return;
 9213        };
 9214
 9215        // Try to find a closest, enclosing node using tree-sitter that has a task
 9216        let Some((buffer, buffer_row, tasks)) = self
 9217            .find_enclosing_node_task(cx)
 9218            // Or find the task that's closest in row-distance.
 9219            .or_else(|| self.find_closest_task(cx))
 9220        else {
 9221            return;
 9222        };
 9223
 9224        let reveal_strategy = action.reveal;
 9225        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9226        cx.spawn_in(window, async move |_, cx| {
 9227            let context = task_context.await?;
 9228            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9229
 9230            let resolved = &mut resolved_task.resolved;
 9231            resolved.reveal = reveal_strategy;
 9232
 9233            workspace
 9234                .update_in(cx, |workspace, window, cx| {
 9235                    workspace.schedule_resolved_task(
 9236                        task_source_kind,
 9237                        resolved_task,
 9238                        false,
 9239                        window,
 9240                        cx,
 9241                    );
 9242                })
 9243                .ok()
 9244        })
 9245        .detach();
 9246    }
 9247
 9248    fn find_closest_task(
 9249        &mut self,
 9250        cx: &mut Context<Self>,
 9251    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9252        let cursor_row = self
 9253            .selections
 9254            .newest_adjusted(&self.display_snapshot(cx))
 9255            .head()
 9256            .row;
 9257
 9258        let ((buffer_id, row), tasks) = self
 9259            .tasks
 9260            .iter()
 9261            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9262
 9263        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9264        let tasks = Arc::new(tasks.to_owned());
 9265        Some((buffer, *row, tasks))
 9266    }
 9267
 9268    fn find_enclosing_node_task(
 9269        &mut self,
 9270        cx: &mut Context<Self>,
 9271    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9272        let snapshot = self.buffer.read(cx).snapshot(cx);
 9273        let offset = self
 9274            .selections
 9275            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9276            .head();
 9277        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9278        let offset = excerpt.map_offset_to_buffer(offset);
 9279        let buffer_id = excerpt.buffer().remote_id();
 9280
 9281        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9282        let mut cursor = layer.node().walk();
 9283
 9284        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9285            if cursor.node().end_byte() == offset.0 {
 9286                cursor.goto_next_sibling();
 9287            }
 9288        }
 9289
 9290        // Ascend to the smallest ancestor that contains the range and has a task.
 9291        loop {
 9292            let node = cursor.node();
 9293            let node_range = node.byte_range();
 9294            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9295
 9296            // Check if this node contains our offset
 9297            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9298                // If it contains offset, check for task
 9299                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9300                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9301                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9302                }
 9303            }
 9304
 9305            if !cursor.goto_parent() {
 9306                break;
 9307            }
 9308        }
 9309        None
 9310    }
 9311
 9312    fn render_run_indicator(
 9313        &self,
 9314        _style: &EditorStyle,
 9315        is_active: bool,
 9316        row: DisplayRow,
 9317        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9318        cx: &mut Context<Self>,
 9319    ) -> IconButton {
 9320        let color = Color::Muted;
 9321        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9322
 9323        IconButton::new(
 9324            ("run_indicator", row.0 as usize),
 9325            ui::IconName::PlayOutlined,
 9326        )
 9327        .shape(ui::IconButtonShape::Square)
 9328        .icon_size(IconSize::XSmall)
 9329        .icon_color(color)
 9330        .toggle_state(is_active)
 9331        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9332            let quick_launch = match e {
 9333                ClickEvent::Keyboard(_) => true,
 9334                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9335            };
 9336
 9337            window.focus(&editor.focus_handle(cx), cx);
 9338            editor.toggle_code_actions(
 9339                &ToggleCodeActions {
 9340                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9341                    quick_launch,
 9342                },
 9343                window,
 9344                cx,
 9345            );
 9346        }))
 9347        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9348            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9349        }))
 9350    }
 9351
 9352    pub fn context_menu_visible(&self) -> bool {
 9353        !self.edit_prediction_preview_is_active()
 9354            && self
 9355                .context_menu
 9356                .borrow()
 9357                .as_ref()
 9358                .is_some_and(|menu| menu.visible())
 9359    }
 9360
 9361    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9362        self.context_menu
 9363            .borrow()
 9364            .as_ref()
 9365            .map(|menu| menu.origin())
 9366    }
 9367
 9368    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9369        self.context_menu_options = Some(options);
 9370    }
 9371
 9372    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9373    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9374
 9375    fn render_edit_prediction_popover(
 9376        &mut self,
 9377        text_bounds: &Bounds<Pixels>,
 9378        content_origin: gpui::Point<Pixels>,
 9379        right_margin: Pixels,
 9380        editor_snapshot: &EditorSnapshot,
 9381        visible_row_range: Range<DisplayRow>,
 9382        scroll_top: ScrollOffset,
 9383        scroll_bottom: ScrollOffset,
 9384        line_layouts: &[LineWithInvisibles],
 9385        line_height: Pixels,
 9386        scroll_position: gpui::Point<ScrollOffset>,
 9387        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9388        newest_selection_head: Option<DisplayPoint>,
 9389        editor_width: Pixels,
 9390        style: &EditorStyle,
 9391        window: &mut Window,
 9392        cx: &mut App,
 9393    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9394        if self.mode().is_minimap() {
 9395            return None;
 9396        }
 9397        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9398
 9399        if self.edit_prediction_visible_in_cursor_popover(true) {
 9400            return None;
 9401        }
 9402
 9403        match &active_edit_prediction.completion {
 9404            EditPrediction::MoveWithin { target, .. } => {
 9405                let target_display_point = target.to_display_point(editor_snapshot);
 9406
 9407                if self.edit_prediction_requires_modifier() {
 9408                    if !self.edit_prediction_preview_is_active() {
 9409                        return None;
 9410                    }
 9411
 9412                    self.render_edit_prediction_modifier_jump_popover(
 9413                        text_bounds,
 9414                        content_origin,
 9415                        visible_row_range,
 9416                        line_layouts,
 9417                        line_height,
 9418                        scroll_pixel_position,
 9419                        newest_selection_head,
 9420                        target_display_point,
 9421                        window,
 9422                        cx,
 9423                    )
 9424                } else {
 9425                    self.render_edit_prediction_eager_jump_popover(
 9426                        text_bounds,
 9427                        content_origin,
 9428                        editor_snapshot,
 9429                        visible_row_range,
 9430                        scroll_top,
 9431                        scroll_bottom,
 9432                        line_height,
 9433                        scroll_pixel_position,
 9434                        target_display_point,
 9435                        editor_width,
 9436                        window,
 9437                        cx,
 9438                    )
 9439                }
 9440            }
 9441            EditPrediction::Edit {
 9442                display_mode: EditDisplayMode::Inline,
 9443                ..
 9444            } => None,
 9445            EditPrediction::Edit {
 9446                display_mode: EditDisplayMode::TabAccept,
 9447                edits,
 9448                ..
 9449            } => {
 9450                let range = &edits.first()?.0;
 9451                let target_display_point = range.end.to_display_point(editor_snapshot);
 9452
 9453                self.render_edit_prediction_end_of_line_popover(
 9454                    "Accept",
 9455                    editor_snapshot,
 9456                    visible_row_range,
 9457                    target_display_point,
 9458                    line_height,
 9459                    scroll_pixel_position,
 9460                    content_origin,
 9461                    editor_width,
 9462                    window,
 9463                    cx,
 9464                )
 9465            }
 9466            EditPrediction::Edit {
 9467                edits,
 9468                edit_preview,
 9469                display_mode: EditDisplayMode::DiffPopover,
 9470                snapshot,
 9471                ..
 9472            } => self.render_edit_prediction_diff_popover(
 9473                text_bounds,
 9474                content_origin,
 9475                right_margin,
 9476                editor_snapshot,
 9477                visible_row_range,
 9478                line_layouts,
 9479                line_height,
 9480                scroll_position,
 9481                scroll_pixel_position,
 9482                newest_selection_head,
 9483                editor_width,
 9484                style,
 9485                edits,
 9486                edit_preview,
 9487                snapshot,
 9488                window,
 9489                cx,
 9490            ),
 9491            EditPrediction::MoveOutside { snapshot, .. } => {
 9492                let mut element = self
 9493                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9494                    .into_any();
 9495
 9496                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9497                let origin_x = text_bounds.size.width - size.width - px(30.);
 9498                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9499                element.prepaint_at(origin, window, cx);
 9500
 9501                Some((element, origin))
 9502            }
 9503        }
 9504    }
 9505
 9506    fn render_edit_prediction_modifier_jump_popover(
 9507        &mut self,
 9508        text_bounds: &Bounds<Pixels>,
 9509        content_origin: gpui::Point<Pixels>,
 9510        visible_row_range: Range<DisplayRow>,
 9511        line_layouts: &[LineWithInvisibles],
 9512        line_height: Pixels,
 9513        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9514        newest_selection_head: Option<DisplayPoint>,
 9515        target_display_point: DisplayPoint,
 9516        window: &mut Window,
 9517        cx: &mut App,
 9518    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9519        let scrolled_content_origin =
 9520            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9521
 9522        const SCROLL_PADDING_Y: Pixels = px(12.);
 9523
 9524        if target_display_point.row() < visible_row_range.start {
 9525            return self.render_edit_prediction_scroll_popover(
 9526                &|_| SCROLL_PADDING_Y,
 9527                IconName::ArrowUp,
 9528                visible_row_range,
 9529                line_layouts,
 9530                newest_selection_head,
 9531                scrolled_content_origin,
 9532                window,
 9533                cx,
 9534            );
 9535        } else if target_display_point.row() >= visible_row_range.end {
 9536            return self.render_edit_prediction_scroll_popover(
 9537                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9538                IconName::ArrowDown,
 9539                visible_row_range,
 9540                line_layouts,
 9541                newest_selection_head,
 9542                scrolled_content_origin,
 9543                window,
 9544                cx,
 9545            );
 9546        }
 9547
 9548        const POLE_WIDTH: Pixels = px(2.);
 9549
 9550        let line_layout =
 9551            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9552        let target_column = target_display_point.column() as usize;
 9553
 9554        let target_x = line_layout.x_for_index(target_column);
 9555        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9556            - scroll_pixel_position.y;
 9557
 9558        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9559
 9560        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9561        border_color.l += 0.001;
 9562
 9563        let mut element = v_flex()
 9564            .items_end()
 9565            .when(flag_on_right, |el| el.items_start())
 9566            .child(if flag_on_right {
 9567                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9568                    .rounded_bl(px(0.))
 9569                    .rounded_tl(px(0.))
 9570                    .border_l_2()
 9571                    .border_color(border_color)
 9572            } else {
 9573                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9574                    .rounded_br(px(0.))
 9575                    .rounded_tr(px(0.))
 9576                    .border_r_2()
 9577                    .border_color(border_color)
 9578            })
 9579            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9580            .into_any();
 9581
 9582        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9583
 9584        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9585            - point(
 9586                if flag_on_right {
 9587                    POLE_WIDTH
 9588                } else {
 9589                    size.width - POLE_WIDTH
 9590                },
 9591                size.height - line_height,
 9592            );
 9593
 9594        origin.x = origin.x.max(content_origin.x);
 9595
 9596        element.prepaint_at(origin, window, cx);
 9597
 9598        Some((element, origin))
 9599    }
 9600
 9601    fn render_edit_prediction_scroll_popover(
 9602        &mut self,
 9603        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9604        scroll_icon: IconName,
 9605        visible_row_range: Range<DisplayRow>,
 9606        line_layouts: &[LineWithInvisibles],
 9607        newest_selection_head: Option<DisplayPoint>,
 9608        scrolled_content_origin: gpui::Point<Pixels>,
 9609        window: &mut Window,
 9610        cx: &mut App,
 9611    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9612        let mut element = self
 9613            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9614            .into_any();
 9615
 9616        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9617
 9618        let cursor = newest_selection_head?;
 9619        let cursor_row_layout =
 9620            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9621        let cursor_column = cursor.column() as usize;
 9622
 9623        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9624
 9625        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9626
 9627        element.prepaint_at(origin, window, cx);
 9628        Some((element, origin))
 9629    }
 9630
 9631    fn render_edit_prediction_eager_jump_popover(
 9632        &mut self,
 9633        text_bounds: &Bounds<Pixels>,
 9634        content_origin: gpui::Point<Pixels>,
 9635        editor_snapshot: &EditorSnapshot,
 9636        visible_row_range: Range<DisplayRow>,
 9637        scroll_top: ScrollOffset,
 9638        scroll_bottom: ScrollOffset,
 9639        line_height: Pixels,
 9640        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9641        target_display_point: DisplayPoint,
 9642        editor_width: Pixels,
 9643        window: &mut Window,
 9644        cx: &mut App,
 9645    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9646        if target_display_point.row().as_f64() < scroll_top {
 9647            let mut element = self
 9648                .render_edit_prediction_line_popover(
 9649                    "Jump to Edit",
 9650                    Some(IconName::ArrowUp),
 9651                    window,
 9652                    cx,
 9653                )
 9654                .into_any();
 9655
 9656            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9657            let offset = point(
 9658                (text_bounds.size.width - size.width) / 2.,
 9659                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9660            );
 9661
 9662            let origin = text_bounds.origin + offset;
 9663            element.prepaint_at(origin, window, cx);
 9664            Some((element, origin))
 9665        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9666            let mut element = self
 9667                .render_edit_prediction_line_popover(
 9668                    "Jump to Edit",
 9669                    Some(IconName::ArrowDown),
 9670                    window,
 9671                    cx,
 9672                )
 9673                .into_any();
 9674
 9675            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9676            let offset = point(
 9677                (text_bounds.size.width - size.width) / 2.,
 9678                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9679            );
 9680
 9681            let origin = text_bounds.origin + offset;
 9682            element.prepaint_at(origin, window, cx);
 9683            Some((element, origin))
 9684        } else {
 9685            self.render_edit_prediction_end_of_line_popover(
 9686                "Jump to Edit",
 9687                editor_snapshot,
 9688                visible_row_range,
 9689                target_display_point,
 9690                line_height,
 9691                scroll_pixel_position,
 9692                content_origin,
 9693                editor_width,
 9694                window,
 9695                cx,
 9696            )
 9697        }
 9698    }
 9699
 9700    fn render_edit_prediction_end_of_line_popover(
 9701        self: &mut Editor,
 9702        label: &'static str,
 9703        editor_snapshot: &EditorSnapshot,
 9704        visible_row_range: Range<DisplayRow>,
 9705        target_display_point: DisplayPoint,
 9706        line_height: Pixels,
 9707        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9708        content_origin: gpui::Point<Pixels>,
 9709        editor_width: Pixels,
 9710        window: &mut Window,
 9711        cx: &mut App,
 9712    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9713        let target_line_end = DisplayPoint::new(
 9714            target_display_point.row(),
 9715            editor_snapshot.line_len(target_display_point.row()),
 9716        );
 9717
 9718        let mut element = self
 9719            .render_edit_prediction_line_popover(label, None, window, cx)
 9720            .into_any();
 9721
 9722        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9723
 9724        let line_origin =
 9725            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9726
 9727        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9728        let mut origin = start_point
 9729            + line_origin
 9730            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9731        origin.x = origin.x.max(content_origin.x);
 9732
 9733        let max_x = content_origin.x + editor_width - size.width;
 9734
 9735        if origin.x > max_x {
 9736            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9737
 9738            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9739                origin.y += offset;
 9740                IconName::ArrowUp
 9741            } else {
 9742                origin.y -= offset;
 9743                IconName::ArrowDown
 9744            };
 9745
 9746            element = self
 9747                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9748                .into_any();
 9749
 9750            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9751
 9752            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9753        }
 9754
 9755        element.prepaint_at(origin, window, cx);
 9756        Some((element, origin))
 9757    }
 9758
 9759    fn render_edit_prediction_diff_popover(
 9760        self: &Editor,
 9761        text_bounds: &Bounds<Pixels>,
 9762        content_origin: gpui::Point<Pixels>,
 9763        right_margin: Pixels,
 9764        editor_snapshot: &EditorSnapshot,
 9765        visible_row_range: Range<DisplayRow>,
 9766        line_layouts: &[LineWithInvisibles],
 9767        line_height: Pixels,
 9768        scroll_position: gpui::Point<ScrollOffset>,
 9769        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9770        newest_selection_head: Option<DisplayPoint>,
 9771        editor_width: Pixels,
 9772        style: &EditorStyle,
 9773        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9774        edit_preview: &Option<language::EditPreview>,
 9775        snapshot: &language::BufferSnapshot,
 9776        window: &mut Window,
 9777        cx: &mut App,
 9778    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9779        let edit_start = edits
 9780            .first()
 9781            .unwrap()
 9782            .0
 9783            .start
 9784            .to_display_point(editor_snapshot);
 9785        let edit_end = edits
 9786            .last()
 9787            .unwrap()
 9788            .0
 9789            .end
 9790            .to_display_point(editor_snapshot);
 9791
 9792        let is_visible = visible_row_range.contains(&edit_start.row())
 9793            || visible_row_range.contains(&edit_end.row());
 9794        if !is_visible {
 9795            return None;
 9796        }
 9797
 9798        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9799            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9800        } else {
 9801            // Fallback for providers without edit_preview
 9802            crate::edit_prediction_fallback_text(edits, cx)
 9803        };
 9804
 9805        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9806        let line_count = highlighted_edits.text.lines().count();
 9807
 9808        const BORDER_WIDTH: Pixels = px(1.);
 9809
 9810        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9811        let has_keybind = keybind.is_some();
 9812
 9813        let mut element = h_flex()
 9814            .items_start()
 9815            .child(
 9816                h_flex()
 9817                    .bg(cx.theme().colors().editor_background)
 9818                    .border(BORDER_WIDTH)
 9819                    .shadow_xs()
 9820                    .border_color(cx.theme().colors().border)
 9821                    .rounded_l_lg()
 9822                    .when(line_count > 1, |el| el.rounded_br_lg())
 9823                    .pr_1()
 9824                    .child(styled_text),
 9825            )
 9826            .child(
 9827                h_flex()
 9828                    .h(line_height + BORDER_WIDTH * 2.)
 9829                    .px_1p5()
 9830                    .gap_1()
 9831                    // Workaround: For some reason, there's a gap if we don't do this
 9832                    .ml(-BORDER_WIDTH)
 9833                    .shadow(vec![gpui::BoxShadow {
 9834                        color: gpui::black().opacity(0.05),
 9835                        offset: point(px(1.), px(1.)),
 9836                        blur_radius: px(2.),
 9837                        spread_radius: px(0.),
 9838                    }])
 9839                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9840                    .border(BORDER_WIDTH)
 9841                    .border_color(cx.theme().colors().border)
 9842                    .rounded_r_lg()
 9843                    .id("edit_prediction_diff_popover_keybind")
 9844                    .when(!has_keybind, |el| {
 9845                        let status_colors = cx.theme().status();
 9846
 9847                        el.bg(status_colors.error_background)
 9848                            .border_color(status_colors.error.opacity(0.6))
 9849                            .child(Icon::new(IconName::Info).color(Color::Error))
 9850                            .cursor_default()
 9851                            .hoverable_tooltip(move |_window, cx| {
 9852                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9853                            })
 9854                    })
 9855                    .children(keybind),
 9856            )
 9857            .into_any();
 9858
 9859        let longest_row =
 9860            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9861        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9862            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9863        } else {
 9864            layout_line(
 9865                longest_row,
 9866                editor_snapshot,
 9867                style,
 9868                editor_width,
 9869                |_| false,
 9870                window,
 9871                cx,
 9872            )
 9873            .width
 9874        };
 9875
 9876        let viewport_bounds =
 9877            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9878                right: -right_margin,
 9879                ..Default::default()
 9880            });
 9881
 9882        let x_after_longest = Pixels::from(
 9883            ScrollPixelOffset::from(
 9884                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9885            ) - scroll_pixel_position.x,
 9886        );
 9887
 9888        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9889
 9890        // Fully visible if it can be displayed within the window (allow overlapping other
 9891        // panes). However, this is only allowed if the popover starts within text_bounds.
 9892        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9893            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9894
 9895        let mut origin = if can_position_to_the_right {
 9896            point(
 9897                x_after_longest,
 9898                text_bounds.origin.y
 9899                    + Pixels::from(
 9900                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9901                            - scroll_pixel_position.y,
 9902                    ),
 9903            )
 9904        } else {
 9905            let cursor_row = newest_selection_head.map(|head| head.row());
 9906            let above_edit = edit_start
 9907                .row()
 9908                .0
 9909                .checked_sub(line_count as u32)
 9910                .map(DisplayRow);
 9911            let below_edit = Some(edit_end.row() + 1);
 9912            let above_cursor =
 9913                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9914            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9915
 9916            // Place the edit popover adjacent to the edit if there is a location
 9917            // available that is onscreen and does not obscure the cursor. Otherwise,
 9918            // place it adjacent to the cursor.
 9919            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9920                .into_iter()
 9921                .flatten()
 9922                .find(|&start_row| {
 9923                    let end_row = start_row + line_count as u32;
 9924                    visible_row_range.contains(&start_row)
 9925                        && visible_row_range.contains(&end_row)
 9926                        && cursor_row
 9927                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9928                })?;
 9929
 9930            content_origin
 9931                + point(
 9932                    Pixels::from(-scroll_pixel_position.x),
 9933                    Pixels::from(
 9934                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9935                    ),
 9936                )
 9937        };
 9938
 9939        origin.x -= BORDER_WIDTH;
 9940
 9941        window.with_content_mask(
 9942            Some(gpui::ContentMask {
 9943                bounds: *text_bounds,
 9944            }),
 9945            |window| {
 9946                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9947            },
 9948        );
 9949
 9950        // Do not return an element, since it will already be drawn due to defer_draw.
 9951        None
 9952    }
 9953
 9954    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9955        px(30.)
 9956    }
 9957
 9958    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9959        if self.read_only(cx) {
 9960            cx.theme().players().read_only()
 9961        } else {
 9962            self.style.as_ref().unwrap().local_player
 9963        }
 9964    }
 9965
 9966    fn render_edit_prediction_accept_keybind(
 9967        &self,
 9968        window: &mut Window,
 9969        cx: &mut App,
 9970    ) -> Option<AnyElement> {
 9971        let accept_binding =
 9972            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9973        let accept_keystroke = accept_binding.keystroke()?;
 9974
 9975        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9976
 9977        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9978            Color::Accent
 9979        } else {
 9980            Color::Muted
 9981        };
 9982
 9983        h_flex()
 9984            .px_0p5()
 9985            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9986            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9987            .text_size(TextSize::XSmall.rems(cx))
 9988            .child(h_flex().children(ui::render_modifiers(
 9989                accept_keystroke.modifiers(),
 9990                PlatformStyle::platform(),
 9991                Some(modifiers_color),
 9992                Some(IconSize::XSmall.rems().into()),
 9993                true,
 9994            )))
 9995            .when(is_platform_style_mac, |parent| {
 9996                parent.child(accept_keystroke.key().to_string())
 9997            })
 9998            .when(!is_platform_style_mac, |parent| {
 9999                parent.child(
10000                    Key::new(
10001                        util::capitalize(accept_keystroke.key()),
10002                        Some(Color::Default),
10003                    )
10004                    .size(Some(IconSize::XSmall.rems().into())),
10005                )
10006            })
10007            .into_any()
10008            .into()
10009    }
10010
10011    fn render_edit_prediction_line_popover(
10012        &self,
10013        label: impl Into<SharedString>,
10014        icon: Option<IconName>,
10015        window: &mut Window,
10016        cx: &mut App,
10017    ) -> Stateful<Div> {
10018        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
10019
10020        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
10021        let has_keybind = keybind.is_some();
10022        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10023
10024        h_flex()
10025            .id("ep-line-popover")
10026            .py_0p5()
10027            .pl_1()
10028            .pr(padding_right)
10029            .gap_1()
10030            .rounded_md()
10031            .border_1()
10032            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10033            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10034            .shadow_xs()
10035            .when(!has_keybind, |el| {
10036                let status_colors = cx.theme().status();
10037
10038                el.bg(status_colors.error_background)
10039                    .border_color(status_colors.error.opacity(0.6))
10040                    .pl_2()
10041                    .child(Icon::new(icons.error).color(Color::Error))
10042                    .cursor_default()
10043                    .hoverable_tooltip(move |_window, cx| {
10044                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10045                    })
10046            })
10047            .children(keybind)
10048            .child(
10049                Label::new(label)
10050                    .size(LabelSize::Small)
10051                    .when(!has_keybind, |el| {
10052                        el.color(cx.theme().status().error.into()).strikethrough()
10053                    }),
10054            )
10055            .when(!has_keybind, |el| {
10056                el.child(
10057                    h_flex().ml_1().child(
10058                        Icon::new(IconName::Info)
10059                            .size(IconSize::Small)
10060                            .color(cx.theme().status().error.into()),
10061                    ),
10062                )
10063            })
10064            .when_some(icon, |element, icon| {
10065                element.child(
10066                    div()
10067                        .mt(px(1.5))
10068                        .child(Icon::new(icon).size(IconSize::Small)),
10069                )
10070            })
10071    }
10072
10073    fn render_edit_prediction_jump_outside_popover(
10074        &self,
10075        snapshot: &BufferSnapshot,
10076        window: &mut Window,
10077        cx: &mut App,
10078    ) -> Stateful<Div> {
10079        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
10080        let has_keybind = keybind.is_some();
10081        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10082
10083        let file_name = snapshot
10084            .file()
10085            .map(|file| SharedString::new(file.file_name(cx)))
10086            .unwrap_or(SharedString::new_static("untitled"));
10087
10088        h_flex()
10089            .id("ep-jump-outside-popover")
10090            .py_1()
10091            .px_2()
10092            .gap_1()
10093            .rounded_md()
10094            .border_1()
10095            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10096            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10097            .shadow_xs()
10098            .when(!has_keybind, |el| {
10099                let status_colors = cx.theme().status();
10100
10101                el.bg(status_colors.error_background)
10102                    .border_color(status_colors.error.opacity(0.6))
10103                    .pl_2()
10104                    .child(Icon::new(icons.error).color(Color::Error))
10105                    .cursor_default()
10106                    .hoverable_tooltip(move |_window, cx| {
10107                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10108                    })
10109            })
10110            .children(keybind)
10111            .child(
10112                Label::new(file_name)
10113                    .size(LabelSize::Small)
10114                    .buffer_font(cx)
10115                    .when(!has_keybind, |el| {
10116                        el.color(cx.theme().status().error.into()).strikethrough()
10117                    }),
10118            )
10119            .when(!has_keybind, |el| {
10120                el.child(
10121                    h_flex().ml_1().child(
10122                        Icon::new(IconName::Info)
10123                            .size(IconSize::Small)
10124                            .color(cx.theme().status().error.into()),
10125                    ),
10126                )
10127            })
10128            .child(
10129                div()
10130                    .mt(px(1.5))
10131                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10132            )
10133    }
10134
10135    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10136        let accent_color = cx.theme().colors().text_accent;
10137        let editor_bg_color = cx.theme().colors().editor_background;
10138        editor_bg_color.blend(accent_color.opacity(0.1))
10139    }
10140
10141    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10142        let accent_color = cx.theme().colors().text_accent;
10143        let editor_bg_color = cx.theme().colors().editor_background;
10144        editor_bg_color.blend(accent_color.opacity(0.6))
10145    }
10146    fn get_prediction_provider_icons(
10147        provider: &Option<RegisteredEditPredictionDelegate>,
10148        cx: &App,
10149    ) -> edit_prediction_types::EditPredictionIconSet {
10150        match provider {
10151            Some(provider) => provider.provider.icons(cx),
10152            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10153        }
10154    }
10155
10156    fn render_edit_prediction_cursor_popover(
10157        &self,
10158        min_width: Pixels,
10159        max_width: Pixels,
10160        cursor_point: Point,
10161        style: &EditorStyle,
10162        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10163        _window: &Window,
10164        cx: &mut Context<Editor>,
10165    ) -> Option<AnyElement> {
10166        let provider = self.edit_prediction_provider.as_ref()?;
10167        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10168
10169        let is_refreshing = provider.provider.is_refreshing(cx);
10170
10171        fn pending_completion_container(icon: IconName) -> Div {
10172            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10173        }
10174
10175        let completion = match &self.active_edit_prediction {
10176            Some(prediction) => {
10177                if !self.has_visible_completions_menu() {
10178                    const RADIUS: Pixels = px(6.);
10179                    const BORDER_WIDTH: Pixels = px(1.);
10180
10181                    return Some(
10182                        h_flex()
10183                            .elevation_2(cx)
10184                            .border(BORDER_WIDTH)
10185                            .border_color(cx.theme().colors().border)
10186                            .when(accept_keystroke.is_none(), |el| {
10187                                el.border_color(cx.theme().status().error)
10188                            })
10189                            .rounded(RADIUS)
10190                            .rounded_tl(px(0.))
10191                            .overflow_hidden()
10192                            .child(div().px_1p5().child(match &prediction.completion {
10193                                EditPrediction::MoveWithin { target, snapshot } => {
10194                                    use text::ToPoint as _;
10195                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10196                                    {
10197                                        Icon::new(icons.down)
10198                                    } else {
10199                                        Icon::new(icons.up)
10200                                    }
10201                                }
10202                                EditPrediction::MoveOutside { .. } => {
10203                                    // TODO [zeta2] custom icon for external jump?
10204                                    Icon::new(icons.base)
10205                                }
10206                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10207                            }))
10208                            .child(
10209                                h_flex()
10210                                    .gap_1()
10211                                    .py_1()
10212                                    .px_2()
10213                                    .rounded_r(RADIUS - BORDER_WIDTH)
10214                                    .border_l_1()
10215                                    .border_color(cx.theme().colors().border)
10216                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10217                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10218                                        el.child(
10219                                            Label::new("Hold")
10220                                                .size(LabelSize::Small)
10221                                                .when(accept_keystroke.is_none(), |el| {
10222                                                    el.strikethrough()
10223                                                })
10224                                                .line_height_style(LineHeightStyle::UiLabel),
10225                                        )
10226                                    })
10227                                    .id("edit_prediction_cursor_popover_keybind")
10228                                    .when(accept_keystroke.is_none(), |el| {
10229                                        let status_colors = cx.theme().status();
10230
10231                                        el.bg(status_colors.error_background)
10232                                            .border_color(status_colors.error.opacity(0.6))
10233                                            .child(Icon::new(IconName::Info).color(Color::Error))
10234                                            .cursor_default()
10235                                            .hoverable_tooltip(move |_window, cx| {
10236                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10237                                                    .into()
10238                                            })
10239                                    })
10240                                    .when_some(
10241                                        accept_keystroke.as_ref(),
10242                                        |el, accept_keystroke| {
10243                                            el.child(h_flex().children(ui::render_modifiers(
10244                                                accept_keystroke.modifiers(),
10245                                                PlatformStyle::platform(),
10246                                                Some(Color::Default),
10247                                                Some(IconSize::XSmall.rems().into()),
10248                                                false,
10249                                            )))
10250                                        },
10251                                    ),
10252                            )
10253                            .into_any(),
10254                    );
10255                }
10256
10257                self.render_edit_prediction_cursor_popover_preview(
10258                    prediction,
10259                    cursor_point,
10260                    style,
10261                    cx,
10262                )?
10263            }
10264
10265            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10266                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10267                    stale_completion,
10268                    cursor_point,
10269                    style,
10270                    cx,
10271                )?,
10272
10273                None => pending_completion_container(icons.base)
10274                    .child(Label::new("...").size(LabelSize::Small)),
10275            },
10276
10277            None => pending_completion_container(icons.base)
10278                .child(Label::new("...").size(LabelSize::Small)),
10279        };
10280
10281        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10282            completion
10283                .with_animation(
10284                    "loading-completion",
10285                    Animation::new(Duration::from_secs(2))
10286                        .repeat()
10287                        .with_easing(pulsating_between(0.4, 0.8)),
10288                    |label, delta| label.opacity(delta),
10289                )
10290                .into_any_element()
10291        } else {
10292            completion.into_any_element()
10293        };
10294
10295        let has_completion = self.active_edit_prediction.is_some();
10296
10297        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10298        Some(
10299            h_flex()
10300                .min_w(min_width)
10301                .max_w(max_width)
10302                .flex_1()
10303                .elevation_2(cx)
10304                .border_color(cx.theme().colors().border)
10305                .child(
10306                    div()
10307                        .flex_1()
10308                        .py_1()
10309                        .px_2()
10310                        .overflow_hidden()
10311                        .child(completion),
10312                )
10313                .when_some(accept_keystroke, |el, accept_keystroke| {
10314                    if !accept_keystroke.modifiers().modified() {
10315                        return el;
10316                    }
10317
10318                    el.child(
10319                        h_flex()
10320                            .h_full()
10321                            .border_l_1()
10322                            .rounded_r_lg()
10323                            .border_color(cx.theme().colors().border)
10324                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10325                            .gap_1()
10326                            .py_1()
10327                            .px_2()
10328                            .child(
10329                                h_flex()
10330                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10331                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10332                                    .child(h_flex().children(ui::render_modifiers(
10333                                        accept_keystroke.modifiers(),
10334                                        PlatformStyle::platform(),
10335                                        Some(if !has_completion {
10336                                            Color::Muted
10337                                        } else {
10338                                            Color::Default
10339                                        }),
10340                                        None,
10341                                        false,
10342                                    ))),
10343                            )
10344                            .child(Label::new("Preview").into_any_element())
10345                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10346                    )
10347                })
10348                .into_any(),
10349        )
10350    }
10351
10352    fn render_edit_prediction_cursor_popover_preview(
10353        &self,
10354        completion: &EditPredictionState,
10355        cursor_point: Point,
10356        style: &EditorStyle,
10357        cx: &mut Context<Editor>,
10358    ) -> Option<Div> {
10359        use text::ToPoint as _;
10360
10361        fn render_relative_row_jump(
10362            prefix: impl Into<String>,
10363            current_row: u32,
10364            target_row: u32,
10365        ) -> Div {
10366            let (row_diff, arrow) = if target_row < current_row {
10367                (current_row - target_row, IconName::ArrowUp)
10368            } else {
10369                (target_row - current_row, IconName::ArrowDown)
10370            };
10371
10372            h_flex()
10373                .child(
10374                    Label::new(format!("{}{}", prefix.into(), row_diff))
10375                        .color(Color::Muted)
10376                        .size(LabelSize::Small),
10377                )
10378                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10379        }
10380
10381        let supports_jump = self
10382            .edit_prediction_provider
10383            .as_ref()
10384            .map(|provider| provider.provider.supports_jump_to_edit())
10385            .unwrap_or(true);
10386
10387        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10388
10389        match &completion.completion {
10390            EditPrediction::MoveWithin {
10391                target, snapshot, ..
10392            } => {
10393                if !supports_jump {
10394                    return None;
10395                }
10396
10397                Some(
10398                    h_flex()
10399                        .px_2()
10400                        .gap_2()
10401                        .flex_1()
10402                        .child(
10403                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10404                                Icon::new(icons.down)
10405                            } else {
10406                                Icon::new(icons.up)
10407                            },
10408                        )
10409                        .child(Label::new("Jump to Edit")),
10410                )
10411            }
10412            EditPrediction::MoveOutside { snapshot, .. } => {
10413                let file_name = snapshot
10414                    .file()
10415                    .map(|file| file.file_name(cx))
10416                    .unwrap_or("untitled");
10417                Some(
10418                    h_flex()
10419                        .px_2()
10420                        .gap_2()
10421                        .flex_1()
10422                        .child(Icon::new(icons.base))
10423                        .child(Label::new(format!("Jump to {file_name}"))),
10424                )
10425            }
10426            EditPrediction::Edit {
10427                edits,
10428                edit_preview,
10429                snapshot,
10430                ..
10431            } => {
10432                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10433
10434                let (highlighted_edits, has_more_lines) =
10435                    if let Some(edit_preview) = edit_preview.as_ref() {
10436                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10437                            .first_line_preview()
10438                    } else {
10439                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10440                    };
10441
10442                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10443                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10444
10445                let preview = h_flex()
10446                    .gap_1()
10447                    .min_w_16()
10448                    .child(styled_text)
10449                    .when(has_more_lines, |parent| parent.child(""));
10450
10451                let left = if supports_jump && first_edit_row != cursor_point.row {
10452                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10453                        .into_any_element()
10454                } else {
10455                    Icon::new(icons.base).into_any_element()
10456                };
10457
10458                Some(
10459                    h_flex()
10460                        .h_full()
10461                        .flex_1()
10462                        .gap_2()
10463                        .pr_1()
10464                        .overflow_x_hidden()
10465                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10466                        .child(left)
10467                        .child(preview),
10468                )
10469            }
10470        }
10471    }
10472
10473    pub fn render_context_menu(
10474        &mut self,
10475        max_height_in_lines: u32,
10476        window: &mut Window,
10477        cx: &mut Context<Editor>,
10478    ) -> Option<AnyElement> {
10479        let menu = self.context_menu.borrow();
10480        let menu = menu.as_ref()?;
10481        if !menu.visible() {
10482            return None;
10483        };
10484        self.style
10485            .as_ref()
10486            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10487    }
10488
10489    fn render_context_menu_aside(
10490        &mut self,
10491        max_size: Size<Pixels>,
10492        window: &mut Window,
10493        cx: &mut Context<Editor>,
10494    ) -> Option<AnyElement> {
10495        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10496            if menu.visible() {
10497                menu.render_aside(max_size, window, cx)
10498            } else {
10499                None
10500            }
10501        })
10502    }
10503
10504    fn hide_context_menu(
10505        &mut self,
10506        window: &mut Window,
10507        cx: &mut Context<Self>,
10508    ) -> Option<CodeContextMenu> {
10509        cx.notify();
10510        self.completion_tasks.clear();
10511        let context_menu = self.context_menu.borrow_mut().take();
10512        self.stale_edit_prediction_in_menu.take();
10513        self.update_visible_edit_prediction(window, cx);
10514        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10515            && let Some(completion_provider) = &self.completion_provider
10516        {
10517            completion_provider.selection_changed(None, window, cx);
10518        }
10519        context_menu
10520    }
10521
10522    fn show_snippet_choices(
10523        &mut self,
10524        choices: &Vec<String>,
10525        selection: Range<Anchor>,
10526        cx: &mut Context<Self>,
10527    ) {
10528        let Some((_, buffer, _)) = self
10529            .buffer()
10530            .read(cx)
10531            .excerpt_containing(selection.start, cx)
10532        else {
10533            return;
10534        };
10535        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10536        else {
10537            return;
10538        };
10539        if buffer != end_buffer {
10540            log::error!("expected anchor range to have matching buffer IDs");
10541            return;
10542        }
10543
10544        let id = post_inc(&mut self.next_completion_id);
10545        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10546        let mut context_menu = self.context_menu.borrow_mut();
10547        let old_menu = context_menu.take();
10548        *context_menu = Some(CodeContextMenu::Completions(
10549            CompletionsMenu::new_snippet_choices(
10550                id,
10551                true,
10552                choices,
10553                selection,
10554                buffer,
10555                old_menu.map(|menu| menu.primary_scroll_handle()),
10556                snippet_sort_order,
10557            ),
10558        ));
10559    }
10560
10561    pub fn insert_snippet(
10562        &mut self,
10563        insertion_ranges: &[Range<MultiBufferOffset>],
10564        snippet: Snippet,
10565        window: &mut Window,
10566        cx: &mut Context<Self>,
10567    ) -> Result<()> {
10568        struct Tabstop<T> {
10569            is_end_tabstop: bool,
10570            ranges: Vec<Range<T>>,
10571            choices: Option<Vec<String>>,
10572        }
10573
10574        let tabstops = self.buffer.update(cx, |buffer, cx| {
10575            let snippet_text: Arc<str> = snippet.text.clone().into();
10576            let edits = insertion_ranges
10577                .iter()
10578                .cloned()
10579                .map(|range| (range, snippet_text.clone()));
10580            let autoindent_mode = AutoindentMode::Block {
10581                original_indent_columns: Vec::new(),
10582            };
10583            buffer.edit(edits, Some(autoindent_mode), cx);
10584
10585            let snapshot = &*buffer.read(cx);
10586            let snippet = &snippet;
10587            snippet
10588                .tabstops
10589                .iter()
10590                .map(|tabstop| {
10591                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10592                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10593                    });
10594                    let mut tabstop_ranges = tabstop
10595                        .ranges
10596                        .iter()
10597                        .flat_map(|tabstop_range| {
10598                            let mut delta = 0_isize;
10599                            insertion_ranges.iter().map(move |insertion_range| {
10600                                let insertion_start = insertion_range.start + delta;
10601                                delta += snippet.text.len() as isize
10602                                    - (insertion_range.end - insertion_range.start) as isize;
10603
10604                                let start =
10605                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10606                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10607                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10608                            })
10609                        })
10610                        .collect::<Vec<_>>();
10611                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10612
10613                    Tabstop {
10614                        is_end_tabstop,
10615                        ranges: tabstop_ranges,
10616                        choices: tabstop.choices.clone(),
10617                    }
10618                })
10619                .collect::<Vec<_>>()
10620        });
10621        if let Some(tabstop) = tabstops.first() {
10622            self.change_selections(Default::default(), window, cx, |s| {
10623                // Reverse order so that the first range is the newest created selection.
10624                // Completions will use it and autoscroll will prioritize it.
10625                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10626            });
10627
10628            if let Some(choices) = &tabstop.choices
10629                && let Some(selection) = tabstop.ranges.first()
10630            {
10631                self.show_snippet_choices(choices, selection.clone(), cx)
10632            }
10633
10634            // If we're already at the last tabstop and it's at the end of the snippet,
10635            // we're done, we don't need to keep the state around.
10636            if !tabstop.is_end_tabstop {
10637                let choices = tabstops
10638                    .iter()
10639                    .map(|tabstop| tabstop.choices.clone())
10640                    .collect();
10641
10642                let ranges = tabstops
10643                    .into_iter()
10644                    .map(|tabstop| tabstop.ranges)
10645                    .collect::<Vec<_>>();
10646
10647                self.snippet_stack.push(SnippetState {
10648                    active_index: 0,
10649                    ranges,
10650                    choices,
10651                });
10652            }
10653
10654            // Check whether the just-entered snippet ends with an auto-closable bracket.
10655            if self.autoclose_regions.is_empty() {
10656                let snapshot = self.buffer.read(cx).snapshot(cx);
10657                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10658                    let selection_head = selection.head();
10659                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10660                        continue;
10661                    };
10662
10663                    let mut bracket_pair = None;
10664                    let max_lookup_length = scope
10665                        .brackets()
10666                        .map(|(pair, _)| {
10667                            pair.start
10668                                .as_str()
10669                                .chars()
10670                                .count()
10671                                .max(pair.end.as_str().chars().count())
10672                        })
10673                        .max();
10674                    if let Some(max_lookup_length) = max_lookup_length {
10675                        let next_text = snapshot
10676                            .chars_at(selection_head)
10677                            .take(max_lookup_length)
10678                            .collect::<String>();
10679                        let prev_text = snapshot
10680                            .reversed_chars_at(selection_head)
10681                            .take(max_lookup_length)
10682                            .collect::<String>();
10683
10684                        for (pair, enabled) in scope.brackets() {
10685                            if enabled
10686                                && pair.close
10687                                && prev_text.starts_with(pair.start.as_str())
10688                                && next_text.starts_with(pair.end.as_str())
10689                            {
10690                                bracket_pair = Some(pair.clone());
10691                                break;
10692                            }
10693                        }
10694                    }
10695
10696                    if let Some(pair) = bracket_pair {
10697                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10698                        let autoclose_enabled =
10699                            self.use_autoclose && snapshot_settings.use_autoclose;
10700                        if autoclose_enabled {
10701                            let start = snapshot.anchor_after(selection_head);
10702                            let end = snapshot.anchor_after(selection_head);
10703                            self.autoclose_regions.push(AutocloseRegion {
10704                                selection_id: selection.id,
10705                                range: start..end,
10706                                pair,
10707                            });
10708                        }
10709                    }
10710                }
10711            }
10712        }
10713        Ok(())
10714    }
10715
10716    pub fn move_to_next_snippet_tabstop(
10717        &mut self,
10718        window: &mut Window,
10719        cx: &mut Context<Self>,
10720    ) -> bool {
10721        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10722    }
10723
10724    pub fn move_to_prev_snippet_tabstop(
10725        &mut self,
10726        window: &mut Window,
10727        cx: &mut Context<Self>,
10728    ) -> bool {
10729        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10730    }
10731
10732    pub fn move_to_snippet_tabstop(
10733        &mut self,
10734        bias: Bias,
10735        window: &mut Window,
10736        cx: &mut Context<Self>,
10737    ) -> bool {
10738        if let Some(mut snippet) = self.snippet_stack.pop() {
10739            match bias {
10740                Bias::Left => {
10741                    if snippet.active_index > 0 {
10742                        snippet.active_index -= 1;
10743                    } else {
10744                        self.snippet_stack.push(snippet);
10745                        return false;
10746                    }
10747                }
10748                Bias::Right => {
10749                    if snippet.active_index + 1 < snippet.ranges.len() {
10750                        snippet.active_index += 1;
10751                    } else {
10752                        self.snippet_stack.push(snippet);
10753                        return false;
10754                    }
10755                }
10756            }
10757            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10758                self.change_selections(Default::default(), window, cx, |s| {
10759                    // Reverse order so that the first range is the newest created selection.
10760                    // Completions will use it and autoscroll will prioritize it.
10761                    s.select_ranges(current_ranges.iter().rev().cloned())
10762                });
10763
10764                if let Some(choices) = &snippet.choices[snippet.active_index]
10765                    && let Some(selection) = current_ranges.first()
10766                {
10767                    self.show_snippet_choices(choices, selection.clone(), cx);
10768                }
10769
10770                // If snippet state is not at the last tabstop, push it back on the stack
10771                if snippet.active_index + 1 < snippet.ranges.len() {
10772                    self.snippet_stack.push(snippet);
10773                }
10774                return true;
10775            }
10776        }
10777
10778        false
10779    }
10780
10781    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10782        self.transact(window, cx, |this, window, cx| {
10783            this.select_all(&SelectAll, window, cx);
10784            this.insert("", window, cx);
10785        });
10786    }
10787
10788    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10789        if self.read_only(cx) {
10790            return;
10791        }
10792        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10793        self.transact(window, cx, |this, window, cx| {
10794            this.select_autoclose_pair(window, cx);
10795
10796            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10797
10798            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10799            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10800            for selection in &mut selections {
10801                if selection.is_empty() {
10802                    let old_head = selection.head();
10803                    let mut new_head =
10804                        movement::left(&display_map, old_head.to_display_point(&display_map))
10805                            .to_point(&display_map);
10806                    if let Some((buffer, line_buffer_range)) = display_map
10807                        .buffer_snapshot()
10808                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10809                    {
10810                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10811                        let indent_len = match indent_size.kind {
10812                            IndentKind::Space => {
10813                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10814                            }
10815                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10816                        };
10817                        if old_head.column <= indent_size.len && old_head.column > 0 {
10818                            let indent_len = indent_len.get();
10819                            new_head = cmp::min(
10820                                new_head,
10821                                MultiBufferPoint::new(
10822                                    old_head.row,
10823                                    ((old_head.column - 1) / indent_len) * indent_len,
10824                                ),
10825                            );
10826                        }
10827                    }
10828
10829                    selection.set_head(new_head, SelectionGoal::None);
10830                }
10831            }
10832
10833            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10834            this.insert("", window, cx);
10835            linked_edits.apply_with_left_expansion(cx);
10836            this.refresh_edit_prediction(true, false, window, cx);
10837            refresh_linked_ranges(this, window, cx);
10838        });
10839    }
10840
10841    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10842        if self.read_only(cx) {
10843            return;
10844        }
10845        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10846        self.transact(window, cx, |this, window, cx| {
10847            this.change_selections(Default::default(), window, cx, |s| {
10848                s.move_with(&mut |map, selection| {
10849                    if selection.is_empty() {
10850                        let cursor = movement::right(map, selection.head());
10851                        selection.end = cursor;
10852                        selection.reversed = true;
10853                        selection.goal = SelectionGoal::None;
10854                    }
10855                })
10856            });
10857            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10858            this.insert("", window, cx);
10859            linked_edits.apply(cx);
10860            this.refresh_edit_prediction(true, false, window, cx);
10861            refresh_linked_ranges(this, window, cx);
10862        });
10863    }
10864
10865    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10866        if self.mode.is_single_line() {
10867            cx.propagate();
10868            return;
10869        }
10870
10871        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10872        if self.move_to_prev_snippet_tabstop(window, cx) {
10873            return;
10874        }
10875        self.outdent(&Outdent, window, cx);
10876    }
10877
10878    pub fn next_snippet_tabstop(
10879        &mut self,
10880        _: &NextSnippetTabstop,
10881        window: &mut Window,
10882        cx: &mut Context<Self>,
10883    ) {
10884        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10885            cx.propagate();
10886            return;
10887        }
10888
10889        if self.move_to_next_snippet_tabstop(window, cx) {
10890            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10891            return;
10892        }
10893        cx.propagate();
10894    }
10895
10896    pub fn previous_snippet_tabstop(
10897        &mut self,
10898        _: &PreviousSnippetTabstop,
10899        window: &mut Window,
10900        cx: &mut Context<Self>,
10901    ) {
10902        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10903            cx.propagate();
10904            return;
10905        }
10906
10907        if self.move_to_prev_snippet_tabstop(window, cx) {
10908            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10909            return;
10910        }
10911        cx.propagate();
10912    }
10913
10914    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10915        if self.mode.is_single_line() {
10916            cx.propagate();
10917            return;
10918        }
10919
10920        if self.move_to_next_snippet_tabstop(window, cx) {
10921            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10922            return;
10923        }
10924        if self.read_only(cx) {
10925            return;
10926        }
10927        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10928        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10929        let buffer = self.buffer.read(cx);
10930        let snapshot = buffer.snapshot(cx);
10931        let rows_iter = selections.iter().map(|s| s.head().row);
10932        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10933
10934        let has_some_cursor_in_whitespace = selections
10935            .iter()
10936            .filter(|selection| selection.is_empty())
10937            .any(|selection| {
10938                let cursor = selection.head();
10939                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10940                cursor.column < current_indent.len
10941            });
10942
10943        let mut edits = Vec::new();
10944        let mut prev_edited_row = 0;
10945        let mut row_delta = 0;
10946        for selection in &mut selections {
10947            if selection.start.row != prev_edited_row {
10948                row_delta = 0;
10949            }
10950            prev_edited_row = selection.end.row;
10951
10952            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10953            if selection.is_empty() {
10954                let cursor = selection.head();
10955                let settings = buffer.language_settings_at(cursor, cx);
10956                if settings.indent_list_on_tab {
10957                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10958                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10959                            row_delta = Self::indent_selection(
10960                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10961                            );
10962                            continue;
10963                        }
10964                    }
10965                }
10966            }
10967
10968            // If the selection is non-empty, then increase the indentation of the selected lines.
10969            if !selection.is_empty() {
10970                row_delta =
10971                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10972                continue;
10973            }
10974
10975            let cursor = selection.head();
10976            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10977            if let Some(suggested_indent) =
10978                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10979            {
10980                // Don't do anything if already at suggested indent
10981                // and there is any other cursor which is not
10982                if has_some_cursor_in_whitespace
10983                    && cursor.column == current_indent.len
10984                    && current_indent.len == suggested_indent.len
10985                {
10986                    continue;
10987                }
10988
10989                // Adjust line and move cursor to suggested indent
10990                // if cursor is not at suggested indent
10991                if cursor.column < suggested_indent.len
10992                    && cursor.column <= current_indent.len
10993                    && current_indent.len <= suggested_indent.len
10994                {
10995                    selection.start = Point::new(cursor.row, suggested_indent.len);
10996                    selection.end = selection.start;
10997                    if row_delta == 0 {
10998                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10999                            cursor.row,
11000                            current_indent,
11001                            suggested_indent,
11002                        ));
11003                        row_delta = suggested_indent.len - current_indent.len;
11004                    }
11005                    continue;
11006                }
11007
11008                // If current indent is more than suggested indent
11009                // only move cursor to current indent and skip indent
11010                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
11011                    selection.start = Point::new(cursor.row, current_indent.len);
11012                    selection.end = selection.start;
11013                    continue;
11014                }
11015            }
11016
11017            // Otherwise, insert a hard or soft tab.
11018            let settings = buffer.language_settings_at(cursor, cx);
11019            let tab_size = if settings.hard_tabs {
11020                IndentSize::tab()
11021            } else {
11022                let tab_size = settings.tab_size.get();
11023                let indent_remainder = snapshot
11024                    .text_for_range(Point::new(cursor.row, 0)..cursor)
11025                    .flat_map(str::chars)
11026                    .fold(row_delta % tab_size, |counter: u32, c| {
11027                        if c == '\t' {
11028                            0
11029                        } else {
11030                            (counter + 1) % tab_size
11031                        }
11032                    });
11033
11034                let chars_to_next_tab_stop = tab_size - indent_remainder;
11035                IndentSize::spaces(chars_to_next_tab_stop)
11036            };
11037            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11038            selection.end = selection.start;
11039            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11040            row_delta += tab_size.len;
11041        }
11042
11043        self.transact(window, cx, |this, window, cx| {
11044            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11045            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11046            this.refresh_edit_prediction(true, false, window, cx);
11047        });
11048    }
11049
11050    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11051        if self.read_only(cx) {
11052            return;
11053        }
11054        if self.mode.is_single_line() {
11055            cx.propagate();
11056            return;
11057        }
11058
11059        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11060        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11061        let mut prev_edited_row = 0;
11062        let mut row_delta = 0;
11063        let mut edits = Vec::new();
11064        let buffer = self.buffer.read(cx);
11065        let snapshot = buffer.snapshot(cx);
11066        for selection in &mut selections {
11067            if selection.start.row != prev_edited_row {
11068                row_delta = 0;
11069            }
11070            prev_edited_row = selection.end.row;
11071
11072            row_delta =
11073                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11074        }
11075
11076        self.transact(window, cx, |this, window, cx| {
11077            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11078            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11079        });
11080    }
11081
11082    fn indent_selection(
11083        buffer: &MultiBuffer,
11084        snapshot: &MultiBufferSnapshot,
11085        selection: &mut Selection<Point>,
11086        edits: &mut Vec<(Range<Point>, String)>,
11087        delta_for_start_row: u32,
11088        cx: &App,
11089    ) -> u32 {
11090        let settings = buffer.language_settings_at(selection.start, cx);
11091        let tab_size = settings.tab_size.get();
11092        let indent_kind = if settings.hard_tabs {
11093            IndentKind::Tab
11094        } else {
11095            IndentKind::Space
11096        };
11097        let mut start_row = selection.start.row;
11098        let mut end_row = selection.end.row + 1;
11099
11100        // If a selection ends at the beginning of a line, don't indent
11101        // that last line.
11102        if selection.end.column == 0 && selection.end.row > selection.start.row {
11103            end_row -= 1;
11104        }
11105
11106        // Avoid re-indenting a row that has already been indented by a
11107        // previous selection, but still update this selection's column
11108        // to reflect that indentation.
11109        if delta_for_start_row > 0 {
11110            start_row += 1;
11111            selection.start.column += delta_for_start_row;
11112            if selection.end.row == selection.start.row {
11113                selection.end.column += delta_for_start_row;
11114            }
11115        }
11116
11117        let mut delta_for_end_row = 0;
11118        let has_multiple_rows = start_row + 1 != end_row;
11119        for row in start_row..end_row {
11120            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11121            let indent_delta = match (current_indent.kind, indent_kind) {
11122                (IndentKind::Space, IndentKind::Space) => {
11123                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11124                    IndentSize::spaces(columns_to_next_tab_stop)
11125                }
11126                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11127                (_, IndentKind::Tab) => IndentSize::tab(),
11128            };
11129
11130            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11131                0
11132            } else {
11133                selection.start.column
11134            };
11135            let row_start = Point::new(row, start);
11136            edits.push((
11137                row_start..row_start,
11138                indent_delta.chars().collect::<String>(),
11139            ));
11140
11141            // Update this selection's endpoints to reflect the indentation.
11142            if row == selection.start.row {
11143                selection.start.column += indent_delta.len;
11144            }
11145            if row == selection.end.row {
11146                selection.end.column += indent_delta.len;
11147                delta_for_end_row = indent_delta.len;
11148            }
11149        }
11150
11151        if selection.start.row == selection.end.row {
11152            delta_for_start_row + delta_for_end_row
11153        } else {
11154            delta_for_end_row
11155        }
11156    }
11157
11158    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11159        if self.read_only(cx) {
11160            return;
11161        }
11162        if self.mode.is_single_line() {
11163            cx.propagate();
11164            return;
11165        }
11166
11167        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11168        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11169        let selections = self.selections.all::<Point>(&display_map);
11170        let mut deletion_ranges = Vec::new();
11171        let mut last_outdent = None;
11172        {
11173            let buffer = self.buffer.read(cx);
11174            let snapshot = buffer.snapshot(cx);
11175            for selection in &selections {
11176                let settings = buffer.language_settings_at(selection.start, cx);
11177                let tab_size = settings.tab_size.get();
11178                let mut rows = selection.spanned_rows(false, &display_map);
11179
11180                // Avoid re-outdenting a row that has already been outdented by a
11181                // previous selection.
11182                if let Some(last_row) = last_outdent
11183                    && last_row == rows.start
11184                {
11185                    rows.start = rows.start.next_row();
11186                }
11187                let has_multiple_rows = rows.len() > 1;
11188                for row in rows.iter_rows() {
11189                    let indent_size = snapshot.indent_size_for_line(row);
11190                    if indent_size.len > 0 {
11191                        let deletion_len = match indent_size.kind {
11192                            IndentKind::Space => {
11193                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11194                                if columns_to_prev_tab_stop == 0 {
11195                                    tab_size
11196                                } else {
11197                                    columns_to_prev_tab_stop
11198                                }
11199                            }
11200                            IndentKind::Tab => 1,
11201                        };
11202                        let start = if has_multiple_rows
11203                            || deletion_len > selection.start.column
11204                            || indent_size.len < selection.start.column
11205                        {
11206                            0
11207                        } else {
11208                            selection.start.column - deletion_len
11209                        };
11210                        deletion_ranges.push(
11211                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11212                        );
11213                        last_outdent = Some(row);
11214                    }
11215                }
11216            }
11217        }
11218
11219        self.transact(window, cx, |this, window, cx| {
11220            this.buffer.update(cx, |buffer, cx| {
11221                let empty_str: Arc<str> = Arc::default();
11222                buffer.edit(
11223                    deletion_ranges
11224                        .into_iter()
11225                        .map(|range| (range, empty_str.clone())),
11226                    None,
11227                    cx,
11228                );
11229            });
11230            let selections = this
11231                .selections
11232                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11233            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11234        });
11235    }
11236
11237    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11238        if self.read_only(cx) {
11239            return;
11240        }
11241        if self.mode.is_single_line() {
11242            cx.propagate();
11243            return;
11244        }
11245
11246        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11247        let selections = self
11248            .selections
11249            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11250            .into_iter()
11251            .map(|s| s.range());
11252
11253        self.transact(window, cx, |this, window, cx| {
11254            this.buffer.update(cx, |buffer, cx| {
11255                buffer.autoindent_ranges(selections, cx);
11256            });
11257            let selections = this
11258                .selections
11259                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11260            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11261        });
11262    }
11263
11264    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11265        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11266        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11267        let selections = self.selections.all::<Point>(&display_map);
11268
11269        let mut new_cursors = Vec::new();
11270        let mut edit_ranges = Vec::new();
11271        let mut selections = selections.iter().peekable();
11272        while let Some(selection) = selections.next() {
11273            let mut rows = selection.spanned_rows(false, &display_map);
11274
11275            // Accumulate contiguous regions of rows that we want to delete.
11276            while let Some(next_selection) = selections.peek() {
11277                let next_rows = next_selection.spanned_rows(false, &display_map);
11278                if next_rows.start <= rows.end {
11279                    rows.end = next_rows.end;
11280                    selections.next().unwrap();
11281                } else {
11282                    break;
11283                }
11284            }
11285
11286            let buffer = display_map.buffer_snapshot();
11287            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11288            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11289                // If there's a line after the range, delete the \n from the end of the row range
11290                (
11291                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11292                    rows.end,
11293                )
11294            } else {
11295                // If there isn't a line after the range, delete the \n from the line before the
11296                // start of the row range
11297                edit_start = edit_start.saturating_sub_usize(1);
11298                (buffer.len(), rows.start.previous_row())
11299            };
11300
11301            let text_layout_details = self.text_layout_details(window, cx);
11302            let x = display_map.x_for_display_point(
11303                selection.head().to_display_point(&display_map),
11304                &text_layout_details,
11305            );
11306            let row = Point::new(target_row.0, 0)
11307                .to_display_point(&display_map)
11308                .row();
11309            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11310
11311            new_cursors.push((
11312                selection.id,
11313                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11314                SelectionGoal::None,
11315            ));
11316            edit_ranges.push(edit_start..edit_end);
11317        }
11318
11319        self.transact(window, cx, |this, window, cx| {
11320            let buffer = this.buffer.update(cx, |buffer, cx| {
11321                let empty_str: Arc<str> = Arc::default();
11322                buffer.edit(
11323                    edit_ranges
11324                        .into_iter()
11325                        .map(|range| (range, empty_str.clone())),
11326                    None,
11327                    cx,
11328                );
11329                buffer.snapshot(cx)
11330            });
11331            let new_selections = new_cursors
11332                .into_iter()
11333                .map(|(id, cursor, goal)| {
11334                    let cursor = cursor.to_point(&buffer);
11335                    Selection {
11336                        id,
11337                        start: cursor,
11338                        end: cursor,
11339                        reversed: false,
11340                        goal,
11341                    }
11342                })
11343                .collect();
11344
11345            this.change_selections(Default::default(), window, cx, |s| {
11346                s.select(new_selections);
11347            });
11348        });
11349    }
11350
11351    pub fn join_lines_impl(
11352        &mut self,
11353        insert_whitespace: bool,
11354        window: &mut Window,
11355        cx: &mut Context<Self>,
11356    ) {
11357        if self.read_only(cx) {
11358            return;
11359        }
11360        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11361        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11362            let start = MultiBufferRow(selection.start.row);
11363            // Treat single line selections as if they include the next line. Otherwise this action
11364            // would do nothing for single line selections individual cursors.
11365            let end = if selection.start.row == selection.end.row {
11366                MultiBufferRow(selection.start.row + 1)
11367            } else if selection.end.column == 0 {
11368                // If the selection ends at the start of a line, it's logically at the end of the
11369                // previous line (plus its newline).
11370                // Don't include the end line unless there's only one line selected.
11371                if selection.start.row + 1 == selection.end.row {
11372                    MultiBufferRow(selection.end.row)
11373                } else {
11374                    MultiBufferRow(selection.end.row - 1)
11375                }
11376            } else {
11377                MultiBufferRow(selection.end.row)
11378            };
11379
11380            if let Some(last_row_range) = row_ranges.last_mut()
11381                && start <= last_row_range.end
11382            {
11383                last_row_range.end = end;
11384                continue;
11385            }
11386            row_ranges.push(start..end);
11387        }
11388
11389        let snapshot = self.buffer.read(cx).snapshot(cx);
11390        let mut cursor_positions = Vec::new();
11391        for row_range in &row_ranges {
11392            let anchor = snapshot.anchor_before(Point::new(
11393                row_range.end.previous_row().0,
11394                snapshot.line_len(row_range.end.previous_row()),
11395            ));
11396            cursor_positions.push(anchor..anchor);
11397        }
11398
11399        self.transact(window, cx, |this, window, cx| {
11400            for row_range in row_ranges.into_iter().rev() {
11401                for row in row_range.iter_rows().rev() {
11402                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11403                    let next_line_row = row.next_row();
11404                    let indent = snapshot.indent_size_for_line(next_line_row);
11405                    let mut join_start_column = indent.len;
11406
11407                    if let Some(language_scope) =
11408                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11409                    {
11410                        let line_end =
11411                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11412                        let line_text_after_indent = snapshot
11413                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11414                            .collect::<String>();
11415
11416                        if !line_text_after_indent.is_empty() {
11417                            let block_prefix = language_scope
11418                                .block_comment()
11419                                .map(|c| c.prefix.as_ref())
11420                                .filter(|p| !p.is_empty());
11421                            let doc_prefix = language_scope
11422                                .documentation_comment()
11423                                .map(|c| c.prefix.as_ref())
11424                                .filter(|p| !p.is_empty());
11425                            let all_prefixes = language_scope
11426                                .line_comment_prefixes()
11427                                .iter()
11428                                .map(|p| p.as_ref())
11429                                .chain(block_prefix)
11430                                .chain(doc_prefix)
11431                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11432
11433                            let mut longest_prefix_len = None;
11434                            for prefix in all_prefixes {
11435                                let trimmed = prefix.trim_end();
11436                                if line_text_after_indent.starts_with(trimmed) {
11437                                    let candidate_len =
11438                                        if line_text_after_indent.starts_with(prefix) {
11439                                            prefix.len()
11440                                        } else {
11441                                            trimmed.len()
11442                                        };
11443                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11444                                        longest_prefix_len = Some(candidate_len);
11445                                    }
11446                                }
11447                            }
11448
11449                            if let Some(prefix_len) = longest_prefix_len {
11450                                join_start_column =
11451                                    join_start_column.saturating_add(prefix_len as u32);
11452                            }
11453                        }
11454                    }
11455
11456                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11457
11458                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11459                        && insert_whitespace
11460                    {
11461                        " "
11462                    } else {
11463                        ""
11464                    };
11465
11466                    this.buffer.update(cx, |buffer, cx| {
11467                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11468                    });
11469                }
11470            }
11471
11472            this.change_selections(Default::default(), window, cx, |s| {
11473                s.select_anchor_ranges(cursor_positions)
11474            });
11475        });
11476    }
11477
11478    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11479        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11480        self.join_lines_impl(true, window, cx);
11481    }
11482
11483    pub fn sort_lines_case_sensitive(
11484        &mut self,
11485        _: &SortLinesCaseSensitive,
11486        window: &mut Window,
11487        cx: &mut Context<Self>,
11488    ) {
11489        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11490    }
11491
11492    pub fn sort_lines_by_length(
11493        &mut self,
11494        _: &SortLinesByLength,
11495        window: &mut Window,
11496        cx: &mut Context<Self>,
11497    ) {
11498        self.manipulate_immutable_lines(window, cx, |lines| {
11499            lines.sort_by_key(|&line| line.chars().count())
11500        })
11501    }
11502
11503    pub fn sort_lines_case_insensitive(
11504        &mut self,
11505        _: &SortLinesCaseInsensitive,
11506        window: &mut Window,
11507        cx: &mut Context<Self>,
11508    ) {
11509        self.manipulate_immutable_lines(window, cx, |lines| {
11510            lines.sort_by_key(|line| line.to_lowercase())
11511        })
11512    }
11513
11514    pub fn unique_lines_case_insensitive(
11515        &mut self,
11516        _: &UniqueLinesCaseInsensitive,
11517        window: &mut Window,
11518        cx: &mut Context<Self>,
11519    ) {
11520        self.manipulate_immutable_lines(window, cx, |lines| {
11521            let mut seen = HashSet::default();
11522            lines.retain(|line| seen.insert(line.to_lowercase()));
11523        })
11524    }
11525
11526    pub fn unique_lines_case_sensitive(
11527        &mut self,
11528        _: &UniqueLinesCaseSensitive,
11529        window: &mut Window,
11530        cx: &mut Context<Self>,
11531    ) {
11532        self.manipulate_immutable_lines(window, cx, |lines| {
11533            let mut seen = HashSet::default();
11534            lines.retain(|line| seen.insert(*line));
11535        })
11536    }
11537
11538    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11539        let snapshot = self.buffer.read(cx).snapshot(cx);
11540        for selection in self.selections.disjoint_anchors_arc().iter() {
11541            if snapshot
11542                .language_at(selection.start)
11543                .and_then(|lang| lang.config().wrap_characters.as_ref())
11544                .is_some()
11545            {
11546                return true;
11547            }
11548        }
11549        false
11550    }
11551
11552    fn wrap_selections_in_tag(
11553        &mut self,
11554        _: &WrapSelectionsInTag,
11555        window: &mut Window,
11556        cx: &mut Context<Self>,
11557    ) {
11558        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11559
11560        let snapshot = self.buffer.read(cx).snapshot(cx);
11561
11562        let mut edits = Vec::new();
11563        let mut boundaries = Vec::new();
11564
11565        for selection in self
11566            .selections
11567            .all_adjusted(&self.display_snapshot(cx))
11568            .iter()
11569        {
11570            let Some(wrap_config) = snapshot
11571                .language_at(selection.start)
11572                .and_then(|lang| lang.config().wrap_characters.clone())
11573            else {
11574                continue;
11575            };
11576
11577            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11578            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11579
11580            let start_before = snapshot.anchor_before(selection.start);
11581            let end_after = snapshot.anchor_after(selection.end);
11582
11583            edits.push((start_before..start_before, open_tag));
11584            edits.push((end_after..end_after, close_tag));
11585
11586            boundaries.push((
11587                start_before,
11588                end_after,
11589                wrap_config.start_prefix.len(),
11590                wrap_config.end_suffix.len(),
11591            ));
11592        }
11593
11594        if edits.is_empty() {
11595            return;
11596        }
11597
11598        self.transact(window, cx, |this, window, cx| {
11599            let buffer = this.buffer.update(cx, |buffer, cx| {
11600                buffer.edit(edits, None, cx);
11601                buffer.snapshot(cx)
11602            });
11603
11604            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11605            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11606                boundaries.into_iter()
11607            {
11608                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11609                let close_offset = end_after
11610                    .to_offset(&buffer)
11611                    .saturating_sub_usize(end_suffix_len);
11612                new_selections.push(open_offset..open_offset);
11613                new_selections.push(close_offset..close_offset);
11614            }
11615
11616            this.change_selections(Default::default(), window, cx, |s| {
11617                s.select_ranges(new_selections);
11618            });
11619
11620            this.request_autoscroll(Autoscroll::fit(), cx);
11621        });
11622    }
11623
11624    pub fn toggle_read_only(
11625        &mut self,
11626        _: &workspace::ToggleReadOnlyFile,
11627        _: &mut Window,
11628        cx: &mut Context<Self>,
11629    ) {
11630        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11631            buffer.update(cx, |buffer, cx| {
11632                buffer.set_capability(
11633                    match buffer.capability() {
11634                        Capability::ReadWrite => Capability::Read,
11635                        Capability::Read => Capability::ReadWrite,
11636                        Capability::ReadOnly => Capability::ReadOnly,
11637                    },
11638                    cx,
11639                );
11640            })
11641        }
11642    }
11643
11644    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11645        let Some(project) = self.project.clone() else {
11646            return;
11647        };
11648        let task = self.reload(project, window, cx);
11649        self.detach_and_notify_err(task, window, cx);
11650    }
11651
11652    pub fn restore_file(
11653        &mut self,
11654        _: &::git::RestoreFile,
11655        window: &mut Window,
11656        cx: &mut Context<Self>,
11657    ) {
11658        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11659        let mut buffer_ids = HashSet::default();
11660        let snapshot = self.buffer().read(cx).snapshot(cx);
11661        for selection in self
11662            .selections
11663            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11664        {
11665            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11666        }
11667
11668        let buffer = self.buffer().read(cx);
11669        let ranges = buffer_ids
11670            .into_iter()
11671            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11672            .collect::<Vec<_>>();
11673
11674        self.restore_hunks_in_ranges(ranges, window, cx);
11675    }
11676
11677    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11678        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11679        let selections = self
11680            .selections
11681            .all(&self.display_snapshot(cx))
11682            .into_iter()
11683            .map(|s| s.range())
11684            .collect();
11685        self.restore_hunks_in_ranges(selections, window, cx);
11686    }
11687
11688    /// Restores the diff hunks in the editor's selections and moves the cursor
11689    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11690    /// not all diff hunks are expanded.
11691    pub fn restore_and_next(
11692        &mut self,
11693        _: &::git::RestoreAndNext,
11694        window: &mut Window,
11695        cx: &mut Context<Self>,
11696    ) {
11697        let selections = self
11698            .selections
11699            .all(&self.display_snapshot(cx))
11700            .into_iter()
11701            .map(|selection| selection.range())
11702            .collect();
11703
11704        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11705        self.restore_hunks_in_ranges(selections, window, cx);
11706
11707        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11708        let wrap_around = !all_diff_hunks_expanded;
11709        let snapshot = self.snapshot(window, cx);
11710        let position = self
11711            .selections
11712            .newest::<Point>(&snapshot.display_snapshot)
11713            .head();
11714
11715        self.go_to_hunk_before_or_after_position(
11716            &snapshot,
11717            position,
11718            Direction::Next,
11719            wrap_around,
11720            window,
11721            cx,
11722        );
11723    }
11724
11725    pub fn restore_hunks_in_ranges(
11726        &mut self,
11727        ranges: Vec<Range<Point>>,
11728        window: &mut Window,
11729        cx: &mut Context<Editor>,
11730    ) {
11731        if self.delegate_stage_and_restore {
11732            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11733            if !hunks.is_empty() {
11734                cx.emit(EditorEvent::RestoreRequested { hunks });
11735            }
11736            return;
11737        }
11738        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11739        self.transact(window, cx, |editor, window, cx| {
11740            editor.restore_diff_hunks(hunks, cx);
11741            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11742                selections.refresh()
11743            });
11744        });
11745    }
11746
11747    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11748        let mut revert_changes = HashMap::default();
11749        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11750        for (buffer_id, hunks) in &chunk_by {
11751            let hunks = hunks.collect::<Vec<_>>();
11752            for hunk in &hunks {
11753                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11754            }
11755            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11756        }
11757        if !revert_changes.is_empty() {
11758            self.buffer().update(cx, |multi_buffer, cx| {
11759                for (buffer_id, changes) in revert_changes {
11760                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11761                        buffer.update(cx, |buffer, cx| {
11762                            buffer.edit(
11763                                changes
11764                                    .into_iter()
11765                                    .map(|(range, text)| (range, text.to_string())),
11766                                None,
11767                                cx,
11768                            );
11769                        });
11770                    }
11771                }
11772            });
11773        }
11774    }
11775
11776    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11777        if let Some(status) = self
11778            .addons
11779            .iter()
11780            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11781        {
11782            return Some(status);
11783        }
11784        self.project
11785            .as_ref()?
11786            .read(cx)
11787            .status_for_buffer_id(buffer_id, cx)
11788    }
11789
11790    pub fn open_active_item_in_terminal(
11791        &mut self,
11792        _: &OpenInTerminal,
11793        window: &mut Window,
11794        cx: &mut Context<Self>,
11795    ) {
11796        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11797            let project_path = buffer.read(cx).project_path(cx)?;
11798            let project = self.project()?.read(cx);
11799            let entry = project.entry_for_path(&project_path, cx)?;
11800            let parent = match &entry.canonical_path {
11801                Some(canonical_path) => canonical_path.to_path_buf(),
11802                None => project.absolute_path(&project_path, cx)?,
11803            }
11804            .parent()?
11805            .to_path_buf();
11806            Some(parent)
11807        }) {
11808            window.dispatch_action(
11809                OpenTerminal {
11810                    working_directory,
11811                    local: false,
11812                }
11813                .boxed_clone(),
11814                cx,
11815            );
11816        }
11817    }
11818
11819    fn set_breakpoint_context_menu(
11820        &mut self,
11821        display_row: DisplayRow,
11822        position: Option<Anchor>,
11823        clicked_point: gpui::Point<Pixels>,
11824        window: &mut Window,
11825        cx: &mut Context<Self>,
11826    ) {
11827        let source = self
11828            .buffer
11829            .read(cx)
11830            .snapshot(cx)
11831            .anchor_before(Point::new(display_row.0, 0u32));
11832
11833        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11834
11835        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11836            self,
11837            source,
11838            clicked_point,
11839            context_menu,
11840            window,
11841            cx,
11842        );
11843    }
11844
11845    fn add_edit_breakpoint_block(
11846        &mut self,
11847        anchor: Anchor,
11848        breakpoint: &Breakpoint,
11849        edit_action: BreakpointPromptEditAction,
11850        window: &mut Window,
11851        cx: &mut Context<Self>,
11852    ) {
11853        let weak_editor = cx.weak_entity();
11854        let bp_prompt = cx.new(|cx| {
11855            BreakpointPromptEditor::new(
11856                weak_editor,
11857                anchor,
11858                breakpoint.clone(),
11859                edit_action,
11860                window,
11861                cx,
11862            )
11863        });
11864
11865        let height = bp_prompt.update(cx, |this, cx| {
11866            this.prompt
11867                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11868        });
11869        let cloned_prompt = bp_prompt.clone();
11870        let blocks = vec![BlockProperties {
11871            style: BlockStyle::Sticky,
11872            placement: BlockPlacement::Above(anchor),
11873            height: Some(height),
11874            render: Arc::new(move |cx| {
11875                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11876                cloned_prompt.clone().into_any_element()
11877            }),
11878            priority: 0,
11879        }];
11880
11881        let focus_handle = bp_prompt.focus_handle(cx);
11882        window.focus(&focus_handle, cx);
11883
11884        let block_ids = self.insert_blocks(blocks, None, cx);
11885        bp_prompt.update(cx, |prompt, _| {
11886            prompt.add_block_ids(block_ids);
11887        });
11888    }
11889
11890    pub(crate) fn breakpoint_at_row(
11891        &self,
11892        row: u32,
11893        window: &mut Window,
11894        cx: &mut Context<Self>,
11895    ) -> Option<(Anchor, Breakpoint)> {
11896        let snapshot = self.snapshot(window, cx);
11897        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11898
11899        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11900    }
11901
11902    pub(crate) fn breakpoint_at_anchor(
11903        &self,
11904        breakpoint_position: Anchor,
11905        snapshot: &EditorSnapshot,
11906        cx: &mut Context<Self>,
11907    ) -> Option<(Anchor, Breakpoint)> {
11908        let buffer = self
11909            .buffer
11910            .read(cx)
11911            .buffer_for_anchor(breakpoint_position, cx)?;
11912
11913        let enclosing_excerpt = breakpoint_position.excerpt_id;
11914        let buffer_snapshot = buffer.read(cx).snapshot();
11915
11916        let row = buffer_snapshot
11917            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11918            .row;
11919
11920        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11921        let anchor_end = snapshot
11922            .buffer_snapshot()
11923            .anchor_after(Point::new(row, line_len));
11924
11925        self.breakpoint_store
11926            .as_ref()?
11927            .read_with(cx, |breakpoint_store, cx| {
11928                breakpoint_store
11929                    .breakpoints(
11930                        &buffer,
11931                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11932                        &buffer_snapshot,
11933                        cx,
11934                    )
11935                    .next()
11936                    .and_then(|(bp, _)| {
11937                        let breakpoint_row = buffer_snapshot
11938                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11939                            .row;
11940
11941                        if breakpoint_row == row {
11942                            snapshot
11943                                .buffer_snapshot()
11944                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11945                                .map(|position| (position, bp.bp.clone()))
11946                        } else {
11947                            None
11948                        }
11949                    })
11950            })
11951    }
11952
11953    pub fn edit_log_breakpoint(
11954        &mut self,
11955        _: &EditLogBreakpoint,
11956        window: &mut Window,
11957        cx: &mut Context<Self>,
11958    ) {
11959        if self.breakpoint_store.is_none() {
11960            return;
11961        }
11962
11963        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11964            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11965                message: None,
11966                state: BreakpointState::Enabled,
11967                condition: None,
11968                hit_condition: None,
11969            });
11970
11971            self.add_edit_breakpoint_block(
11972                anchor,
11973                &breakpoint,
11974                BreakpointPromptEditAction::Log,
11975                window,
11976                cx,
11977            );
11978        }
11979    }
11980
11981    fn breakpoints_at_cursors(
11982        &self,
11983        window: &mut Window,
11984        cx: &mut Context<Self>,
11985    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11986        let snapshot = self.snapshot(window, cx);
11987        let cursors = self
11988            .selections
11989            .disjoint_anchors_arc()
11990            .iter()
11991            .map(|selection| {
11992                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11993
11994                let breakpoint_position = self
11995                    .breakpoint_at_row(cursor_position.row, window, cx)
11996                    .map(|bp| bp.0)
11997                    .unwrap_or_else(|| {
11998                        snapshot
11999                            .display_snapshot
12000                            .buffer_snapshot()
12001                            .anchor_after(Point::new(cursor_position.row, 0))
12002                    });
12003
12004                let breakpoint = self
12005                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
12006                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
12007
12008                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
12009            })
12010            // 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.
12011            .collect::<HashMap<Anchor, _>>();
12012
12013        cursors.into_iter().collect()
12014    }
12015
12016    pub fn enable_breakpoint(
12017        &mut self,
12018        _: &crate::actions::EnableBreakpoint,
12019        window: &mut Window,
12020        cx: &mut Context<Self>,
12021    ) {
12022        if self.breakpoint_store.is_none() {
12023            return;
12024        }
12025
12026        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12027            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
12028                continue;
12029            };
12030            self.edit_breakpoint_at_anchor(
12031                anchor,
12032                breakpoint,
12033                BreakpointEditAction::InvertState,
12034                cx,
12035            );
12036        }
12037    }
12038
12039    pub fn disable_breakpoint(
12040        &mut self,
12041        _: &crate::actions::DisableBreakpoint,
12042        window: &mut Window,
12043        cx: &mut Context<Self>,
12044    ) {
12045        if self.breakpoint_store.is_none() {
12046            return;
12047        }
12048
12049        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12050            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12051                continue;
12052            };
12053            self.edit_breakpoint_at_anchor(
12054                anchor,
12055                breakpoint,
12056                BreakpointEditAction::InvertState,
12057                cx,
12058            );
12059        }
12060    }
12061
12062    pub fn toggle_breakpoint(
12063        &mut self,
12064        _: &crate::actions::ToggleBreakpoint,
12065        window: &mut Window,
12066        cx: &mut Context<Self>,
12067    ) {
12068        if self.breakpoint_store.is_none() {
12069            return;
12070        }
12071
12072        let snapshot = self.snapshot(window, cx);
12073        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12074            if self.gutter_breakpoint_indicator.0.is_some() {
12075                let display_row = anchor
12076                    .to_point(snapshot.buffer_snapshot())
12077                    .to_display_point(&snapshot.display_snapshot)
12078                    .row();
12079                self.update_breakpoint_collision_on_toggle(
12080                    display_row,
12081                    &BreakpointEditAction::Toggle,
12082                );
12083            }
12084
12085            if let Some(breakpoint) = breakpoint {
12086                self.edit_breakpoint_at_anchor(
12087                    anchor,
12088                    breakpoint,
12089                    BreakpointEditAction::Toggle,
12090                    cx,
12091                );
12092            } else {
12093                self.edit_breakpoint_at_anchor(
12094                    anchor,
12095                    Breakpoint::new_standard(),
12096                    BreakpointEditAction::Toggle,
12097                    cx,
12098                );
12099            }
12100        }
12101    }
12102
12103    fn update_breakpoint_collision_on_toggle(
12104        &mut self,
12105        display_row: DisplayRow,
12106        edit_action: &BreakpointEditAction,
12107    ) {
12108        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12109            if breakpoint_indicator.display_row == display_row
12110                && matches!(edit_action, BreakpointEditAction::Toggle)
12111            {
12112                breakpoint_indicator.collides_with_existing_breakpoint =
12113                    !breakpoint_indicator.collides_with_existing_breakpoint;
12114            }
12115        }
12116    }
12117
12118    pub fn edit_breakpoint_at_anchor(
12119        &mut self,
12120        breakpoint_position: Anchor,
12121        breakpoint: Breakpoint,
12122        edit_action: BreakpointEditAction,
12123        cx: &mut Context<Self>,
12124    ) {
12125        let Some(breakpoint_store) = &self.breakpoint_store else {
12126            return;
12127        };
12128
12129        let Some(buffer) = self
12130            .buffer
12131            .read(cx)
12132            .buffer_for_anchor(breakpoint_position, cx)
12133        else {
12134            return;
12135        };
12136
12137        breakpoint_store.update(cx, |breakpoint_store, cx| {
12138            breakpoint_store.toggle_breakpoint(
12139                buffer,
12140                BreakpointWithPosition {
12141                    position: breakpoint_position.text_anchor,
12142                    bp: breakpoint,
12143                },
12144                edit_action,
12145                cx,
12146            );
12147        });
12148
12149        cx.notify();
12150    }
12151
12152    #[cfg(any(test, feature = "test-support"))]
12153    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12154        self.breakpoint_store.clone()
12155    }
12156
12157    pub fn prepare_restore_change(
12158        &self,
12159        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12160        hunk: &MultiBufferDiffHunk,
12161        cx: &mut App,
12162    ) -> Option<()> {
12163        if hunk.is_created_file() {
12164            return None;
12165        }
12166        let buffer = self.buffer.read(cx);
12167        let diff = buffer.diff_for(hunk.buffer_id)?;
12168        let buffer = buffer.buffer(hunk.buffer_id)?;
12169        let buffer = buffer.read(cx);
12170        let original_text = diff
12171            .read(cx)
12172            .base_text(cx)
12173            .as_rope()
12174            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12175        let buffer_snapshot = buffer.snapshot();
12176        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12177        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12178            probe
12179                .0
12180                .start
12181                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12182                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12183        }) {
12184            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12185            Some(())
12186        } else {
12187            None
12188        }
12189    }
12190
12191    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12192        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12193    }
12194
12195    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12196        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12197    }
12198
12199    pub fn rotate_selections_forward(
12200        &mut self,
12201        _: &RotateSelectionsForward,
12202        window: &mut Window,
12203        cx: &mut Context<Self>,
12204    ) {
12205        self.rotate_selections(window, cx, false)
12206    }
12207
12208    pub fn rotate_selections_backward(
12209        &mut self,
12210        _: &RotateSelectionsBackward,
12211        window: &mut Window,
12212        cx: &mut Context<Self>,
12213    ) {
12214        self.rotate_selections(window, cx, true)
12215    }
12216
12217    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12218        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12219        let display_snapshot = self.display_snapshot(cx);
12220        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12221
12222        if selections.len() < 2 {
12223            return;
12224        }
12225
12226        let (edits, new_selections) = {
12227            let buffer = self.buffer.read(cx).read(cx);
12228            let has_selections = selections.iter().any(|s| !s.is_empty());
12229            if has_selections {
12230                let mut selected_texts: Vec<String> = selections
12231                    .iter()
12232                    .map(|selection| {
12233                        buffer
12234                            .text_for_range(selection.start..selection.end)
12235                            .collect()
12236                    })
12237                    .collect();
12238
12239                if reverse {
12240                    selected_texts.rotate_left(1);
12241                } else {
12242                    selected_texts.rotate_right(1);
12243                }
12244
12245                let mut offset_delta: i64 = 0;
12246                let mut new_selections = Vec::new();
12247                let edits: Vec<_> = selections
12248                    .iter()
12249                    .zip(selected_texts.iter())
12250                    .map(|(selection, new_text)| {
12251                        let old_len = (selection.end.0 - selection.start.0) as i64;
12252                        let new_len = new_text.len() as i64;
12253                        let adjusted_start =
12254                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12255                        let adjusted_end =
12256                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12257
12258                        new_selections.push(Selection {
12259                            id: selection.id,
12260                            start: adjusted_start,
12261                            end: adjusted_end,
12262                            reversed: selection.reversed,
12263                            goal: selection.goal,
12264                        });
12265
12266                        offset_delta += new_len - old_len;
12267                        (selection.start..selection.end, new_text.clone())
12268                    })
12269                    .collect();
12270                (edits, new_selections)
12271            } else {
12272                let mut all_rows: Vec<u32> = selections
12273                    .iter()
12274                    .map(|selection| buffer.offset_to_point(selection.start).row)
12275                    .collect();
12276                all_rows.sort_unstable();
12277                all_rows.dedup();
12278
12279                if all_rows.len() < 2 {
12280                    return;
12281                }
12282
12283                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12284                    .iter()
12285                    .map(|&row| {
12286                        let start = Point::new(row, 0);
12287                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12288                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12289                    })
12290                    .collect();
12291
12292                let mut line_texts: Vec<String> = line_ranges
12293                    .iter()
12294                    .map(|range| buffer.text_for_range(range.clone()).collect())
12295                    .collect();
12296
12297                if reverse {
12298                    line_texts.rotate_left(1);
12299                } else {
12300                    line_texts.rotate_right(1);
12301                }
12302
12303                let edits = line_ranges
12304                    .iter()
12305                    .zip(line_texts.iter())
12306                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12307                    .collect();
12308
12309                let num_rows = all_rows.len();
12310                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12311                    .iter()
12312                    .enumerate()
12313                    .map(|(i, &row)| (row, i))
12314                    .collect();
12315
12316                // Compute new line start offsets after rotation (handles CRLF)
12317                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12318                let first_line_start = line_ranges[0].start.0;
12319                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12320                for text in line_texts.iter().take(num_rows - 1) {
12321                    let prev_start = *new_line_starts.last().unwrap();
12322                    new_line_starts.push(prev_start + text.len() + newline_len);
12323                }
12324
12325                let new_selections = selections
12326                    .iter()
12327                    .map(|selection| {
12328                        let point = buffer.offset_to_point(selection.start);
12329                        let old_index = row_to_index[&point.row];
12330                        let new_index = if reverse {
12331                            (old_index + num_rows - 1) % num_rows
12332                        } else {
12333                            (old_index + 1) % num_rows
12334                        };
12335                        let new_offset =
12336                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12337                        Selection {
12338                            id: selection.id,
12339                            start: new_offset,
12340                            end: new_offset,
12341                            reversed: selection.reversed,
12342                            goal: selection.goal,
12343                        }
12344                    })
12345                    .collect();
12346
12347                (edits, new_selections)
12348            }
12349        };
12350
12351        self.transact(window, cx, |this, window, cx| {
12352            this.buffer.update(cx, |buffer, cx| {
12353                buffer.edit(edits, None, cx);
12354            });
12355            this.change_selections(Default::default(), window, cx, |s| {
12356                s.select(new_selections);
12357            });
12358        });
12359    }
12360
12361    fn manipulate_lines<M>(
12362        &mut self,
12363        window: &mut Window,
12364        cx: &mut Context<Self>,
12365        mut manipulate: M,
12366    ) where
12367        M: FnMut(&str) -> LineManipulationResult,
12368    {
12369        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12370
12371        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12372        let buffer = self.buffer.read(cx).snapshot(cx);
12373
12374        let mut edits = Vec::new();
12375
12376        let selections = self.selections.all::<Point>(&display_map);
12377        let mut selections = selections.iter().peekable();
12378        let mut contiguous_row_selections = Vec::new();
12379        let mut new_selections = Vec::new();
12380        let mut added_lines = 0;
12381        let mut removed_lines = 0;
12382
12383        while let Some(selection) = selections.next() {
12384            let (start_row, end_row) = consume_contiguous_rows(
12385                &mut contiguous_row_selections,
12386                selection,
12387                &display_map,
12388                &mut selections,
12389            );
12390
12391            let start_point = Point::new(start_row.0, 0);
12392            let end_point = Point::new(
12393                end_row.previous_row().0,
12394                buffer.line_len(end_row.previous_row()),
12395            );
12396            let text = buffer
12397                .text_for_range(start_point..end_point)
12398                .collect::<String>();
12399
12400            let LineManipulationResult {
12401                new_text,
12402                line_count_before,
12403                line_count_after,
12404            } = manipulate(&text);
12405
12406            edits.push((start_point..end_point, new_text));
12407
12408            // Selections must change based on added and removed line count
12409            let start_row =
12410                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12411            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12412            new_selections.push(Selection {
12413                id: selection.id,
12414                start: start_row,
12415                end: end_row,
12416                goal: SelectionGoal::None,
12417                reversed: selection.reversed,
12418            });
12419
12420            if line_count_after > line_count_before {
12421                added_lines += line_count_after - line_count_before;
12422            } else if line_count_before > line_count_after {
12423                removed_lines += line_count_before - line_count_after;
12424            }
12425        }
12426
12427        self.transact(window, cx, |this, window, cx| {
12428            let buffer = this.buffer.update(cx, |buffer, cx| {
12429                buffer.edit(edits, None, cx);
12430                buffer.snapshot(cx)
12431            });
12432
12433            // Recalculate offsets on newly edited buffer
12434            let new_selections = new_selections
12435                .iter()
12436                .map(|s| {
12437                    let start_point = Point::new(s.start.0, 0);
12438                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12439                    Selection {
12440                        id: s.id,
12441                        start: buffer.point_to_offset(start_point),
12442                        end: buffer.point_to_offset(end_point),
12443                        goal: s.goal,
12444                        reversed: s.reversed,
12445                    }
12446                })
12447                .collect();
12448
12449            this.change_selections(Default::default(), window, cx, |s| {
12450                s.select(new_selections);
12451            });
12452
12453            this.request_autoscroll(Autoscroll::fit(), cx);
12454        });
12455    }
12456
12457    fn manipulate_immutable_lines<Fn>(
12458        &mut self,
12459        window: &mut Window,
12460        cx: &mut Context<Self>,
12461        mut callback: Fn,
12462    ) where
12463        Fn: FnMut(&mut Vec<&str>),
12464    {
12465        self.manipulate_lines(window, cx, |text| {
12466            let mut lines: Vec<&str> = text.split('\n').collect();
12467            let line_count_before = lines.len();
12468
12469            callback(&mut lines);
12470
12471            LineManipulationResult {
12472                new_text: lines.join("\n"),
12473                line_count_before,
12474                line_count_after: lines.len(),
12475            }
12476        });
12477    }
12478
12479    fn manipulate_mutable_lines<Fn>(
12480        &mut self,
12481        window: &mut Window,
12482        cx: &mut Context<Self>,
12483        mut callback: Fn,
12484    ) where
12485        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12486    {
12487        self.manipulate_lines(window, cx, |text| {
12488            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12489            let line_count_before = lines.len();
12490
12491            callback(&mut lines);
12492
12493            LineManipulationResult {
12494                new_text: lines.join("\n"),
12495                line_count_before,
12496                line_count_after: lines.len(),
12497            }
12498        });
12499    }
12500
12501    pub fn convert_indentation_to_spaces(
12502        &mut self,
12503        _: &ConvertIndentationToSpaces,
12504        window: &mut Window,
12505        cx: &mut Context<Self>,
12506    ) {
12507        let settings = self.buffer.read(cx).language_settings(cx);
12508        let tab_size = settings.tab_size.get() as usize;
12509
12510        self.manipulate_mutable_lines(window, cx, |lines| {
12511            // Allocates a reasonably sized scratch buffer once for the whole loop
12512            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12513            // Avoids recomputing spaces that could be inserted many times
12514            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12515                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12516                .collect();
12517
12518            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12519                let mut chars = line.as_ref().chars();
12520                let mut col = 0;
12521                let mut changed = false;
12522
12523                for ch in chars.by_ref() {
12524                    match ch {
12525                        ' ' => {
12526                            reindented_line.push(' ');
12527                            col += 1;
12528                        }
12529                        '\t' => {
12530                            // \t are converted to spaces depending on the current column
12531                            let spaces_len = tab_size - (col % tab_size);
12532                            reindented_line.extend(&space_cache[spaces_len - 1]);
12533                            col += spaces_len;
12534                            changed = true;
12535                        }
12536                        _ => {
12537                            // If we dont append before break, the character is consumed
12538                            reindented_line.push(ch);
12539                            break;
12540                        }
12541                    }
12542                }
12543
12544                if !changed {
12545                    reindented_line.clear();
12546                    continue;
12547                }
12548                // Append the rest of the line and replace old reference with new one
12549                reindented_line.extend(chars);
12550                *line = Cow::Owned(reindented_line.clone());
12551                reindented_line.clear();
12552            }
12553        });
12554    }
12555
12556    pub fn convert_indentation_to_tabs(
12557        &mut self,
12558        _: &ConvertIndentationToTabs,
12559        window: &mut Window,
12560        cx: &mut Context<Self>,
12561    ) {
12562        let settings = self.buffer.read(cx).language_settings(cx);
12563        let tab_size = settings.tab_size.get() as usize;
12564
12565        self.manipulate_mutable_lines(window, cx, |lines| {
12566            // Allocates a reasonably sized buffer once for the whole loop
12567            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12568            // Avoids recomputing spaces that could be inserted many times
12569            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12570                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12571                .collect();
12572
12573            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12574                let mut chars = line.chars();
12575                let mut spaces_count = 0;
12576                let mut first_non_indent_char = None;
12577                let mut changed = false;
12578
12579                for ch in chars.by_ref() {
12580                    match ch {
12581                        ' ' => {
12582                            // Keep track of spaces. Append \t when we reach tab_size
12583                            spaces_count += 1;
12584                            changed = true;
12585                            if spaces_count == tab_size {
12586                                reindented_line.push('\t');
12587                                spaces_count = 0;
12588                            }
12589                        }
12590                        '\t' => {
12591                            reindented_line.push('\t');
12592                            spaces_count = 0;
12593                        }
12594                        _ => {
12595                            // Dont append it yet, we might have remaining spaces
12596                            first_non_indent_char = Some(ch);
12597                            break;
12598                        }
12599                    }
12600                }
12601
12602                if !changed {
12603                    reindented_line.clear();
12604                    continue;
12605                }
12606                // Remaining spaces that didn't make a full tab stop
12607                if spaces_count > 0 {
12608                    reindented_line.extend(&space_cache[spaces_count - 1]);
12609                }
12610                // If we consume an extra character that was not indentation, add it back
12611                if let Some(extra_char) = first_non_indent_char {
12612                    reindented_line.push(extra_char);
12613                }
12614                // Append the rest of the line and replace old reference with new one
12615                reindented_line.extend(chars);
12616                *line = Cow::Owned(reindented_line.clone());
12617                reindented_line.clear();
12618            }
12619        });
12620    }
12621
12622    pub fn convert_to_upper_case(
12623        &mut self,
12624        _: &ConvertToUpperCase,
12625        window: &mut Window,
12626        cx: &mut Context<Self>,
12627    ) {
12628        self.manipulate_text(window, cx, |text| text.to_uppercase())
12629    }
12630
12631    pub fn convert_to_lower_case(
12632        &mut self,
12633        _: &ConvertToLowerCase,
12634        window: &mut Window,
12635        cx: &mut Context<Self>,
12636    ) {
12637        self.manipulate_text(window, cx, |text| text.to_lowercase())
12638    }
12639
12640    pub fn convert_to_title_case(
12641        &mut self,
12642        _: &ConvertToTitleCase,
12643        window: &mut Window,
12644        cx: &mut Context<Self>,
12645    ) {
12646        self.manipulate_text(window, cx, |text| {
12647            text.split('\n')
12648                .map(|line| line.to_case(Case::Title))
12649                .join("\n")
12650        })
12651    }
12652
12653    pub fn convert_to_snake_case(
12654        &mut self,
12655        _: &ConvertToSnakeCase,
12656        window: &mut Window,
12657        cx: &mut Context<Self>,
12658    ) {
12659        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12660    }
12661
12662    pub fn convert_to_kebab_case(
12663        &mut self,
12664        _: &ConvertToKebabCase,
12665        window: &mut Window,
12666        cx: &mut Context<Self>,
12667    ) {
12668        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12669    }
12670
12671    pub fn convert_to_upper_camel_case(
12672        &mut self,
12673        _: &ConvertToUpperCamelCase,
12674        window: &mut Window,
12675        cx: &mut Context<Self>,
12676    ) {
12677        self.manipulate_text(window, cx, |text| {
12678            text.split('\n')
12679                .map(|line| line.to_case(Case::UpperCamel))
12680                .join("\n")
12681        })
12682    }
12683
12684    pub fn convert_to_lower_camel_case(
12685        &mut self,
12686        _: &ConvertToLowerCamelCase,
12687        window: &mut Window,
12688        cx: &mut Context<Self>,
12689    ) {
12690        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12691    }
12692
12693    pub fn convert_to_opposite_case(
12694        &mut self,
12695        _: &ConvertToOppositeCase,
12696        window: &mut Window,
12697        cx: &mut Context<Self>,
12698    ) {
12699        self.manipulate_text(window, cx, |text| {
12700            text.chars()
12701                .fold(String::with_capacity(text.len()), |mut t, c| {
12702                    if c.is_uppercase() {
12703                        t.extend(c.to_lowercase());
12704                    } else {
12705                        t.extend(c.to_uppercase());
12706                    }
12707                    t
12708                })
12709        })
12710    }
12711
12712    pub fn convert_to_sentence_case(
12713        &mut self,
12714        _: &ConvertToSentenceCase,
12715        window: &mut Window,
12716        cx: &mut Context<Self>,
12717    ) {
12718        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12719    }
12720
12721    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12722        self.manipulate_text(window, cx, |text| {
12723            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12724            if has_upper_case_characters {
12725                text.to_lowercase()
12726            } else {
12727                text.to_uppercase()
12728            }
12729        })
12730    }
12731
12732    pub fn convert_to_rot13(
12733        &mut self,
12734        _: &ConvertToRot13,
12735        window: &mut Window,
12736        cx: &mut Context<Self>,
12737    ) {
12738        self.manipulate_text(window, cx, |text| {
12739            text.chars()
12740                .map(|c| match c {
12741                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12742                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12743                    _ => c,
12744                })
12745                .collect()
12746        })
12747    }
12748
12749    pub fn convert_to_rot47(
12750        &mut self,
12751        _: &ConvertToRot47,
12752        window: &mut Window,
12753        cx: &mut Context<Self>,
12754    ) {
12755        self.manipulate_text(window, cx, |text| {
12756            text.chars()
12757                .map(|c| {
12758                    let code_point = c as u32;
12759                    if code_point >= 33 && code_point <= 126 {
12760                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12761                    }
12762                    c
12763                })
12764                .collect()
12765        })
12766    }
12767
12768    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12769    where
12770        Fn: FnMut(&str) -> String,
12771    {
12772        let buffer = self.buffer.read(cx).snapshot(cx);
12773
12774        let mut new_selections = Vec::new();
12775        let mut edits = Vec::new();
12776        let mut selection_adjustment = 0isize;
12777
12778        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12779            let selection_is_empty = selection.is_empty();
12780
12781            let (start, end) = if selection_is_empty {
12782                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12783                (word_range.start, word_range.end)
12784            } else {
12785                (
12786                    buffer.point_to_offset(selection.start),
12787                    buffer.point_to_offset(selection.end),
12788                )
12789            };
12790
12791            let text = buffer.text_for_range(start..end).collect::<String>();
12792            let old_length = text.len() as isize;
12793            let text = callback(&text);
12794
12795            new_selections.push(Selection {
12796                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12797                end: MultiBufferOffset(
12798                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12799                ),
12800                goal: SelectionGoal::None,
12801                id: selection.id,
12802                reversed: selection.reversed,
12803            });
12804
12805            selection_adjustment += old_length - text.len() as isize;
12806
12807            edits.push((start..end, text));
12808        }
12809
12810        self.transact(window, cx, |this, window, cx| {
12811            this.buffer.update(cx, |buffer, cx| {
12812                buffer.edit(edits, None, cx);
12813            });
12814
12815            this.change_selections(Default::default(), window, cx, |s| {
12816                s.select(new_selections);
12817            });
12818
12819            this.request_autoscroll(Autoscroll::fit(), cx);
12820        });
12821    }
12822
12823    pub fn move_selection_on_drop(
12824        &mut self,
12825        selection: &Selection<Anchor>,
12826        target: DisplayPoint,
12827        is_cut: bool,
12828        window: &mut Window,
12829        cx: &mut Context<Self>,
12830    ) {
12831        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12832        let buffer = display_map.buffer_snapshot();
12833        let mut edits = Vec::new();
12834        let insert_point = display_map
12835            .clip_point(target, Bias::Left)
12836            .to_point(&display_map);
12837        let text = buffer
12838            .text_for_range(selection.start..selection.end)
12839            .collect::<String>();
12840        if is_cut {
12841            edits.push(((selection.start..selection.end), String::new()));
12842        }
12843        let insert_anchor = buffer.anchor_before(insert_point);
12844        edits.push(((insert_anchor..insert_anchor), text));
12845        let last_edit_start = insert_anchor.bias_left(buffer);
12846        let last_edit_end = insert_anchor.bias_right(buffer);
12847        self.transact(window, cx, |this, window, cx| {
12848            this.buffer.update(cx, |buffer, cx| {
12849                buffer.edit(edits, None, cx);
12850            });
12851            this.change_selections(Default::default(), window, cx, |s| {
12852                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12853            });
12854        });
12855    }
12856
12857    pub fn clear_selection_drag_state(&mut self) {
12858        self.selection_drag_state = SelectionDragState::None;
12859    }
12860
12861    pub fn duplicate(
12862        &mut self,
12863        upwards: bool,
12864        whole_lines: bool,
12865        window: &mut Window,
12866        cx: &mut Context<Self>,
12867    ) {
12868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12869
12870        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12871        let buffer = display_map.buffer_snapshot();
12872        let selections = self.selections.all::<Point>(&display_map);
12873
12874        let mut edits = Vec::new();
12875        let mut selections_iter = selections.iter().peekable();
12876        while let Some(selection) = selections_iter.next() {
12877            let mut rows = selection.spanned_rows(false, &display_map);
12878            // duplicate line-wise
12879            if whole_lines || selection.start == selection.end {
12880                // Avoid duplicating the same lines twice.
12881                while let Some(next_selection) = selections_iter.peek() {
12882                    let next_rows = next_selection.spanned_rows(false, &display_map);
12883                    if next_rows.start < rows.end {
12884                        rows.end = next_rows.end;
12885                        selections_iter.next().unwrap();
12886                    } else {
12887                        break;
12888                    }
12889                }
12890
12891                // Copy the text from the selected row region and splice it either at the start
12892                // or end of the region.
12893                let start = Point::new(rows.start.0, 0);
12894                let end = Point::new(
12895                    rows.end.previous_row().0,
12896                    buffer.line_len(rows.end.previous_row()),
12897                );
12898
12899                let mut text = buffer.text_for_range(start..end).collect::<String>();
12900
12901                let insert_location = if upwards {
12902                    // When duplicating upward, we need to insert before the current line.
12903                    // If we're on the last line and it doesn't end with a newline,
12904                    // we need to add a newline before the duplicated content.
12905                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12906                        && buffer.max_point().column > 0
12907                        && !text.ends_with('\n');
12908
12909                    if needs_leading_newline {
12910                        text.insert(0, '\n');
12911                        end
12912                    } else {
12913                        text.push('\n');
12914                        Point::new(rows.start.0, 0)
12915                    }
12916                } else {
12917                    text.push('\n');
12918                    start
12919                };
12920                edits.push((insert_location..insert_location, text));
12921            } else {
12922                // duplicate character-wise
12923                let start = selection.start;
12924                let end = selection.end;
12925                let text = buffer.text_for_range(start..end).collect::<String>();
12926                edits.push((selection.end..selection.end, text));
12927            }
12928        }
12929
12930        self.transact(window, cx, |this, window, cx| {
12931            this.buffer.update(cx, |buffer, cx| {
12932                buffer.edit(edits, None, cx);
12933            });
12934
12935            // When duplicating upward with whole lines, move the cursor to the duplicated line
12936            if upwards && whole_lines {
12937                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12938
12939                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12940                    let mut new_ranges = Vec::new();
12941                    let selections = s.all::<Point>(&display_map);
12942                    let mut selections_iter = selections.iter().peekable();
12943
12944                    while let Some(first_selection) = selections_iter.next() {
12945                        // Group contiguous selections together to find the total row span
12946                        let mut group_selections = vec![first_selection];
12947                        let mut rows = first_selection.spanned_rows(false, &display_map);
12948
12949                        while let Some(next_selection) = selections_iter.peek() {
12950                            let next_rows = next_selection.spanned_rows(false, &display_map);
12951                            if next_rows.start < rows.end {
12952                                rows.end = next_rows.end;
12953                                group_selections.push(selections_iter.next().unwrap());
12954                            } else {
12955                                break;
12956                            }
12957                        }
12958
12959                        let row_count = rows.end.0 - rows.start.0;
12960
12961                        // Move all selections in this group up by the total number of duplicated rows
12962                        for selection in group_selections {
12963                            let new_start = Point::new(
12964                                selection.start.row.saturating_sub(row_count),
12965                                selection.start.column,
12966                            );
12967
12968                            let new_end = Point::new(
12969                                selection.end.row.saturating_sub(row_count),
12970                                selection.end.column,
12971                            );
12972
12973                            new_ranges.push(new_start..new_end);
12974                        }
12975                    }
12976
12977                    s.select_ranges(new_ranges);
12978                });
12979            }
12980
12981            this.request_autoscroll(Autoscroll::fit(), cx);
12982        });
12983    }
12984
12985    pub fn duplicate_line_up(
12986        &mut self,
12987        _: &DuplicateLineUp,
12988        window: &mut Window,
12989        cx: &mut Context<Self>,
12990    ) {
12991        self.duplicate(true, true, window, cx);
12992    }
12993
12994    pub fn duplicate_line_down(
12995        &mut self,
12996        _: &DuplicateLineDown,
12997        window: &mut Window,
12998        cx: &mut Context<Self>,
12999    ) {
13000        self.duplicate(false, true, window, cx);
13001    }
13002
13003    pub fn duplicate_selection(
13004        &mut self,
13005        _: &DuplicateSelection,
13006        window: &mut Window,
13007        cx: &mut Context<Self>,
13008    ) {
13009        self.duplicate(false, false, window, cx);
13010    }
13011
13012    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13013        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13014        if self.mode.is_single_line() {
13015            cx.propagate();
13016            return;
13017        }
13018
13019        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13020        let buffer = self.buffer.read(cx).snapshot(cx);
13021
13022        let mut edits = Vec::new();
13023        let mut unfold_ranges = Vec::new();
13024        let mut refold_creases = Vec::new();
13025
13026        let selections = self.selections.all::<Point>(&display_map);
13027        let mut selections = selections.iter().peekable();
13028        let mut contiguous_row_selections = Vec::new();
13029        let mut new_selections = Vec::new();
13030
13031        while let Some(selection) = selections.next() {
13032            // Find all the selections that span a contiguous row range
13033            let (start_row, end_row) = consume_contiguous_rows(
13034                &mut contiguous_row_selections,
13035                selection,
13036                &display_map,
13037                &mut selections,
13038            );
13039
13040            // Move the text spanned by the row range to be before the line preceding the row range
13041            if start_row.0 > 0 {
13042                let range_to_move = Point::new(
13043                    start_row.previous_row().0,
13044                    buffer.line_len(start_row.previous_row()),
13045                )
13046                    ..Point::new(
13047                        end_row.previous_row().0,
13048                        buffer.line_len(end_row.previous_row()),
13049                    );
13050                let insertion_point = display_map
13051                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13052                    .0;
13053
13054                // Don't move lines across excerpts
13055                if buffer
13056                    .excerpt_containing(insertion_point..range_to_move.end)
13057                    .is_some()
13058                {
13059                    let text = buffer
13060                        .text_for_range(range_to_move.clone())
13061                        .flat_map(|s| s.chars())
13062                        .skip(1)
13063                        .chain(['\n'])
13064                        .collect::<String>();
13065
13066                    edits.push((
13067                        buffer.anchor_after(range_to_move.start)
13068                            ..buffer.anchor_before(range_to_move.end),
13069                        String::new(),
13070                    ));
13071                    let insertion_anchor = buffer.anchor_after(insertion_point);
13072                    edits.push((insertion_anchor..insertion_anchor, text));
13073
13074                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13075
13076                    // Move selections up
13077                    new_selections.extend(contiguous_row_selections.drain(..).map(
13078                        |mut selection| {
13079                            selection.start.row -= row_delta;
13080                            selection.end.row -= row_delta;
13081                            selection
13082                        },
13083                    ));
13084
13085                    // Move folds up
13086                    unfold_ranges.push(range_to_move.clone());
13087                    for fold in display_map.folds_in_range(
13088                        buffer.anchor_before(range_to_move.start)
13089                            ..buffer.anchor_after(range_to_move.end),
13090                    ) {
13091                        let mut start = fold.range.start.to_point(&buffer);
13092                        let mut end = fold.range.end.to_point(&buffer);
13093                        start.row -= row_delta;
13094                        end.row -= row_delta;
13095                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13096                    }
13097                }
13098            }
13099
13100            // If we didn't move line(s), preserve the existing selections
13101            new_selections.append(&mut contiguous_row_selections);
13102        }
13103
13104        self.transact(window, cx, |this, window, cx| {
13105            this.unfold_ranges(&unfold_ranges, true, true, cx);
13106            this.buffer.update(cx, |buffer, cx| {
13107                for (range, text) in edits {
13108                    buffer.edit([(range, text)], None, cx);
13109                }
13110            });
13111            this.fold_creases(refold_creases, true, window, cx);
13112            this.change_selections(Default::default(), window, cx, |s| {
13113                s.select(new_selections);
13114            })
13115        });
13116    }
13117
13118    pub fn move_line_down(
13119        &mut self,
13120        _: &MoveLineDown,
13121        window: &mut Window,
13122        cx: &mut Context<Self>,
13123    ) {
13124        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13125        if self.mode.is_single_line() {
13126            cx.propagate();
13127            return;
13128        }
13129
13130        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13131        let buffer = self.buffer.read(cx).snapshot(cx);
13132
13133        let mut edits = Vec::new();
13134        let mut unfold_ranges = Vec::new();
13135        let mut refold_creases = Vec::new();
13136
13137        let selections = self.selections.all::<Point>(&display_map);
13138        let mut selections = selections.iter().peekable();
13139        let mut contiguous_row_selections = Vec::new();
13140        let mut new_selections = Vec::new();
13141
13142        while let Some(selection) = selections.next() {
13143            // Find all the selections that span a contiguous row range
13144            let (start_row, end_row) = consume_contiguous_rows(
13145                &mut contiguous_row_selections,
13146                selection,
13147                &display_map,
13148                &mut selections,
13149            );
13150
13151            // Move the text spanned by the row range to be after the last line of the row range
13152            if end_row.0 <= buffer.max_point().row {
13153                let range_to_move =
13154                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13155                let insertion_point = display_map
13156                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13157                    .0;
13158
13159                // Don't move lines across excerpt boundaries
13160                if buffer
13161                    .excerpt_containing(range_to_move.start..insertion_point)
13162                    .is_some()
13163                {
13164                    let mut text = String::from("\n");
13165                    text.extend(buffer.text_for_range(range_to_move.clone()));
13166                    text.pop(); // Drop trailing newline
13167                    edits.push((
13168                        buffer.anchor_after(range_to_move.start)
13169                            ..buffer.anchor_before(range_to_move.end),
13170                        String::new(),
13171                    ));
13172                    let insertion_anchor = buffer.anchor_after(insertion_point);
13173                    edits.push((insertion_anchor..insertion_anchor, text));
13174
13175                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13176
13177                    // Move selections down
13178                    new_selections.extend(contiguous_row_selections.drain(..).map(
13179                        |mut selection| {
13180                            selection.start.row += row_delta;
13181                            selection.end.row += row_delta;
13182                            selection
13183                        },
13184                    ));
13185
13186                    // Move folds down
13187                    unfold_ranges.push(range_to_move.clone());
13188                    for fold in display_map.folds_in_range(
13189                        buffer.anchor_before(range_to_move.start)
13190                            ..buffer.anchor_after(range_to_move.end),
13191                    ) {
13192                        let mut start = fold.range.start.to_point(&buffer);
13193                        let mut end = fold.range.end.to_point(&buffer);
13194                        start.row += row_delta;
13195                        end.row += row_delta;
13196                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13197                    }
13198                }
13199            }
13200
13201            // If we didn't move line(s), preserve the existing selections
13202            new_selections.append(&mut contiguous_row_selections);
13203        }
13204
13205        self.transact(window, cx, |this, window, cx| {
13206            this.unfold_ranges(&unfold_ranges, true, true, cx);
13207            this.buffer.update(cx, |buffer, cx| {
13208                for (range, text) in edits {
13209                    buffer.edit([(range, text)], None, cx);
13210                }
13211            });
13212            this.fold_creases(refold_creases, true, window, cx);
13213            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13214        });
13215    }
13216
13217    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13218        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13219        let text_layout_details = &self.text_layout_details(window, cx);
13220        self.transact(window, cx, |this, window, cx| {
13221            let edits = this.change_selections(Default::default(), window, cx, |s| {
13222                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13223                s.move_with(&mut |display_map, selection| {
13224                    if !selection.is_empty() {
13225                        return;
13226                    }
13227
13228                    let mut head = selection.head();
13229                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13230                    if head.column() == display_map.line_len(head.row()) {
13231                        transpose_offset = display_map
13232                            .buffer_snapshot()
13233                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13234                    }
13235
13236                    if transpose_offset == MultiBufferOffset(0) {
13237                        return;
13238                    }
13239
13240                    *head.column_mut() += 1;
13241                    head = display_map.clip_point(head, Bias::Right);
13242                    let goal = SelectionGoal::HorizontalPosition(
13243                        display_map
13244                            .x_for_display_point(head, text_layout_details)
13245                            .into(),
13246                    );
13247                    selection.collapse_to(head, goal);
13248
13249                    let transpose_start = display_map
13250                        .buffer_snapshot()
13251                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13252                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13253                        let transpose_end = display_map
13254                            .buffer_snapshot()
13255                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13256                        if let Some(ch) = display_map
13257                            .buffer_snapshot()
13258                            .chars_at(transpose_start)
13259                            .next()
13260                        {
13261                            edits.push((transpose_start..transpose_offset, String::new()));
13262                            edits.push((transpose_end..transpose_end, ch.to_string()));
13263                        }
13264                    }
13265                });
13266                edits
13267            });
13268            this.buffer
13269                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13270            let selections = this
13271                .selections
13272                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13273            this.change_selections(Default::default(), window, cx, |s| {
13274                s.select(selections);
13275            });
13276        });
13277    }
13278
13279    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13280        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13281        if self.mode.is_single_line() {
13282            cx.propagate();
13283            return;
13284        }
13285
13286        self.rewrap_impl(RewrapOptions::default(), cx)
13287    }
13288
13289    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13290        let buffer = self.buffer.read(cx).snapshot(cx);
13291        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13292
13293        #[derive(Clone, Debug, PartialEq)]
13294        enum CommentFormat {
13295            /// single line comment, with prefix for line
13296            Line(String),
13297            /// single line within a block comment, with prefix for line
13298            BlockLine(String),
13299            /// a single line of a block comment that includes the initial delimiter
13300            BlockCommentWithStart(BlockCommentConfig),
13301            /// a single line of a block comment that includes the ending delimiter
13302            BlockCommentWithEnd(BlockCommentConfig),
13303        }
13304
13305        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13306        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13307            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13308                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13309                .peekable();
13310
13311            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13312                row
13313            } else {
13314                return Vec::new();
13315            };
13316
13317            let language_settings = buffer.language_settings_at(selection.head(), cx);
13318            let language_scope = buffer.language_scope_at(selection.head());
13319
13320            let indent_and_prefix_for_row =
13321                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13322                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13323                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13324                        &language_scope
13325                    {
13326                        let indent_end = Point::new(row, indent.len);
13327                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13328                        let line_text_after_indent = buffer
13329                            .text_for_range(indent_end..line_end)
13330                            .collect::<String>();
13331
13332                        let is_within_comment_override = buffer
13333                            .language_scope_at(indent_end)
13334                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13335                        let comment_delimiters = if is_within_comment_override {
13336                            // we are within a comment syntax node, but we don't
13337                            // yet know what kind of comment: block, doc or line
13338                            match (
13339                                language_scope.documentation_comment(),
13340                                language_scope.block_comment(),
13341                            ) {
13342                                (Some(config), _) | (_, Some(config))
13343                                    if buffer.contains_str_at(indent_end, &config.start) =>
13344                                {
13345                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13346                                }
13347                                (Some(config), _) | (_, Some(config))
13348                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13349                                {
13350                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13351                                }
13352                                (Some(config), _) | (_, Some(config))
13353                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13354                                {
13355                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13356                                }
13357                                (_, _) => language_scope
13358                                    .line_comment_prefixes()
13359                                    .iter()
13360                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13361                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13362                            }
13363                        } else {
13364                            // we not in an overridden comment node, but we may
13365                            // be within a non-overridden line comment node
13366                            language_scope
13367                                .line_comment_prefixes()
13368                                .iter()
13369                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13370                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13371                        };
13372
13373                        let rewrap_prefix = language_scope
13374                            .rewrap_prefixes()
13375                            .iter()
13376                            .find_map(|prefix_regex| {
13377                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13378                                    if mat.start() == 0 {
13379                                        Some(mat.as_str().to_string())
13380                                    } else {
13381                                        None
13382                                    }
13383                                })
13384                            })
13385                            .flatten();
13386                        (comment_delimiters, rewrap_prefix)
13387                    } else {
13388                        (None, None)
13389                    };
13390                    (indent, comment_prefix, rewrap_prefix)
13391                };
13392
13393            let mut ranges = Vec::new();
13394            let from_empty_selection = selection.is_empty();
13395
13396            let mut current_range_start = first_row;
13397            let mut prev_row = first_row;
13398            let (
13399                mut current_range_indent,
13400                mut current_range_comment_delimiters,
13401                mut current_range_rewrap_prefix,
13402            ) = indent_and_prefix_for_row(first_row);
13403
13404            for row in non_blank_rows_iter.skip(1) {
13405                let has_paragraph_break = row > prev_row + 1;
13406
13407                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13408                    indent_and_prefix_for_row(row);
13409
13410                let has_indent_change = row_indent != current_range_indent;
13411                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13412
13413                let has_boundary_change = has_comment_change
13414                    || row_rewrap_prefix.is_some()
13415                    || (has_indent_change && current_range_comment_delimiters.is_some());
13416
13417                if has_paragraph_break || has_boundary_change {
13418                    ranges.push((
13419                        language_settings.clone(),
13420                        Point::new(current_range_start, 0)
13421                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13422                        current_range_indent,
13423                        current_range_comment_delimiters.clone(),
13424                        current_range_rewrap_prefix.clone(),
13425                        from_empty_selection,
13426                    ));
13427                    current_range_start = row;
13428                    current_range_indent = row_indent;
13429                    current_range_comment_delimiters = row_comment_delimiters;
13430                    current_range_rewrap_prefix = row_rewrap_prefix;
13431                }
13432                prev_row = row;
13433            }
13434
13435            ranges.push((
13436                language_settings.clone(),
13437                Point::new(current_range_start, 0)
13438                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13439                current_range_indent,
13440                current_range_comment_delimiters,
13441                current_range_rewrap_prefix,
13442                from_empty_selection,
13443            ));
13444
13445            ranges
13446        });
13447
13448        let mut edits = Vec::new();
13449        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13450
13451        for (
13452            language_settings,
13453            wrap_range,
13454            mut indent_size,
13455            comment_prefix,
13456            rewrap_prefix,
13457            from_empty_selection,
13458        ) in wrap_ranges
13459        {
13460            let mut start_row = wrap_range.start.row;
13461            let mut end_row = wrap_range.end.row;
13462
13463            // Skip selections that overlap with a range that has already been rewrapped.
13464            let selection_range = start_row..end_row;
13465            if rewrapped_row_ranges
13466                .iter()
13467                .any(|range| range.overlaps(&selection_range))
13468            {
13469                continue;
13470            }
13471
13472            let tab_size = language_settings.tab_size;
13473
13474            let (line_prefix, inside_comment) = match &comment_prefix {
13475                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13476                    (Some(prefix.as_str()), true)
13477                }
13478                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13479                    (Some(prefix.as_ref()), true)
13480                }
13481                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13482                    start: _,
13483                    end: _,
13484                    prefix,
13485                    tab_size,
13486                })) => {
13487                    indent_size.len += tab_size;
13488                    (Some(prefix.as_ref()), true)
13489                }
13490                None => (None, false),
13491            };
13492            let indent_prefix = indent_size.chars().collect::<String>();
13493            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13494
13495            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13496                RewrapBehavior::InComments => inside_comment,
13497                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13498                RewrapBehavior::Anywhere => true,
13499            };
13500
13501            let should_rewrap = options.override_language_settings
13502                || allow_rewrap_based_on_language
13503                || self.hard_wrap.is_some();
13504            if !should_rewrap {
13505                continue;
13506            }
13507
13508            if from_empty_selection {
13509                'expand_upwards: while start_row > 0 {
13510                    let prev_row = start_row - 1;
13511                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13512                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13513                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13514                    {
13515                        start_row = prev_row;
13516                    } else {
13517                        break 'expand_upwards;
13518                    }
13519                }
13520
13521                'expand_downwards: while end_row < buffer.max_point().row {
13522                    let next_row = end_row + 1;
13523                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13524                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13525                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13526                    {
13527                        end_row = next_row;
13528                    } else {
13529                        break 'expand_downwards;
13530                    }
13531                }
13532            }
13533
13534            let start = Point::new(start_row, 0);
13535            let start_offset = ToOffset::to_offset(&start, &buffer);
13536            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13537            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13538            let mut first_line_delimiter = None;
13539            let mut last_line_delimiter = None;
13540            let Some(lines_without_prefixes) = selection_text
13541                .lines()
13542                .enumerate()
13543                .map(|(ix, line)| {
13544                    let line_trimmed = line.trim_start();
13545                    if rewrap_prefix.is_some() && ix > 0 {
13546                        Ok(line_trimmed)
13547                    } else if let Some(
13548                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13549                            start,
13550                            prefix,
13551                            end,
13552                            tab_size,
13553                        })
13554                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13555                            start,
13556                            prefix,
13557                            end,
13558                            tab_size,
13559                        }),
13560                    ) = &comment_prefix
13561                    {
13562                        let line_trimmed = line_trimmed
13563                            .strip_prefix(start.as_ref())
13564                            .map(|s| {
13565                                let mut indent_size = indent_size;
13566                                indent_size.len -= tab_size;
13567                                let indent_prefix: String = indent_size.chars().collect();
13568                                first_line_delimiter = Some((indent_prefix, start));
13569                                s.trim_start()
13570                            })
13571                            .unwrap_or(line_trimmed);
13572                        let line_trimmed = line_trimmed
13573                            .strip_suffix(end.as_ref())
13574                            .map(|s| {
13575                                last_line_delimiter = Some(end);
13576                                s.trim_end()
13577                            })
13578                            .unwrap_or(line_trimmed);
13579                        let line_trimmed = line_trimmed
13580                            .strip_prefix(prefix.as_ref())
13581                            .unwrap_or(line_trimmed);
13582                        Ok(line_trimmed)
13583                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13584                        line_trimmed.strip_prefix(prefix).with_context(|| {
13585                            format!("line did not start with prefix {prefix:?}: {line:?}")
13586                        })
13587                    } else {
13588                        line_trimmed
13589                            .strip_prefix(&line_prefix.trim_start())
13590                            .with_context(|| {
13591                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13592                            })
13593                    }
13594                })
13595                .collect::<Result<Vec<_>, _>>()
13596                .log_err()
13597            else {
13598                continue;
13599            };
13600
13601            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13602                buffer
13603                    .language_settings_at(Point::new(start_row, 0), cx)
13604                    .preferred_line_length as usize
13605            });
13606
13607            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13608                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13609            } else {
13610                line_prefix.clone()
13611            };
13612
13613            let wrapped_text = {
13614                let mut wrapped_text = wrap_with_prefix(
13615                    line_prefix,
13616                    subsequent_lines_prefix,
13617                    lines_without_prefixes.join("\n"),
13618                    wrap_column,
13619                    tab_size,
13620                    options.preserve_existing_whitespace,
13621                );
13622
13623                if let Some((indent, delimiter)) = first_line_delimiter {
13624                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13625                }
13626                if let Some(last_line) = last_line_delimiter {
13627                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13628                }
13629
13630                wrapped_text
13631            };
13632
13633            // TODO: should always use char-based diff while still supporting cursor behavior that
13634            // matches vim.
13635            let mut diff_options = DiffOptions::default();
13636            if options.override_language_settings {
13637                diff_options.max_word_diff_len = 0;
13638                diff_options.max_word_diff_line_count = 0;
13639            } else {
13640                diff_options.max_word_diff_len = usize::MAX;
13641                diff_options.max_word_diff_line_count = usize::MAX;
13642            }
13643
13644            for (old_range, new_text) in
13645                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13646            {
13647                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13648                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13649                edits.push((edit_start..edit_end, new_text));
13650            }
13651
13652            rewrapped_row_ranges.push(start_row..=end_row);
13653        }
13654
13655        self.buffer
13656            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13657    }
13658
13659    pub fn cut_common(
13660        &mut self,
13661        cut_no_selection_line: bool,
13662        window: &mut Window,
13663        cx: &mut Context<Self>,
13664    ) -> ClipboardItem {
13665        let mut text = String::new();
13666        let buffer = self.buffer.read(cx).snapshot(cx);
13667        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13668        let mut clipboard_selections = Vec::with_capacity(selections.len());
13669        {
13670            let max_point = buffer.max_point();
13671            let mut is_first = true;
13672            let mut prev_selection_was_entire_line = false;
13673            for selection in &mut selections {
13674                let is_entire_line =
13675                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13676                if is_entire_line {
13677                    selection.start = Point::new(selection.start.row, 0);
13678                    if !selection.is_empty() && selection.end.column == 0 {
13679                        selection.end = cmp::min(max_point, selection.end);
13680                    } else {
13681                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13682                    }
13683                    selection.goal = SelectionGoal::None;
13684                }
13685                if is_first {
13686                    is_first = false;
13687                } else if !prev_selection_was_entire_line {
13688                    text += "\n";
13689                }
13690                prev_selection_was_entire_line = is_entire_line;
13691                let mut len = 0;
13692                for chunk in buffer.text_for_range(selection.start..selection.end) {
13693                    text.push_str(chunk);
13694                    len += chunk.len();
13695                }
13696
13697                clipboard_selections.push(ClipboardSelection::for_buffer(
13698                    len,
13699                    is_entire_line,
13700                    selection.range(),
13701                    &buffer,
13702                    self.project.as_ref(),
13703                    cx,
13704                ));
13705            }
13706        }
13707
13708        self.transact(window, cx, |this, window, cx| {
13709            this.change_selections(Default::default(), window, cx, |s| {
13710                s.select(selections);
13711            });
13712            this.insert("", window, cx);
13713        });
13714        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13715    }
13716
13717    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13718        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13719        let item = self.cut_common(true, window, cx);
13720        cx.write_to_clipboard(item);
13721    }
13722
13723    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13724        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13725        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13726            s.move_with(&mut |snapshot, sel| {
13727                if sel.is_empty() {
13728                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13729                }
13730                if sel.is_empty() {
13731                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13732                }
13733            });
13734        });
13735        let item = self.cut_common(false, window, cx);
13736        cx.set_global(KillRing(item))
13737    }
13738
13739    pub fn kill_ring_yank(
13740        &mut self,
13741        _: &KillRingYank,
13742        window: &mut Window,
13743        cx: &mut Context<Self>,
13744    ) {
13745        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13746        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13747            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13748                (kill_ring.text().to_string(), kill_ring.metadata_json())
13749            } else {
13750                return;
13751            }
13752        } else {
13753            return;
13754        };
13755        self.do_paste(&text, metadata, false, window, cx);
13756    }
13757
13758    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13759        self.do_copy(true, cx);
13760    }
13761
13762    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13763        self.do_copy(false, cx);
13764    }
13765
13766    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13767        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13768        let buffer = self.buffer.read(cx).read(cx);
13769        let mut text = String::new();
13770        let mut clipboard_selections = Vec::with_capacity(selections.len());
13771
13772        let max_point = buffer.max_point();
13773        let mut is_first = true;
13774        for selection in &selections {
13775            let mut start = selection.start;
13776            let mut end = selection.end;
13777            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13778            let mut add_trailing_newline = false;
13779            if is_entire_line {
13780                start = Point::new(start.row, 0);
13781                let next_line_start = Point::new(end.row + 1, 0);
13782                if next_line_start <= max_point {
13783                    end = next_line_start;
13784                } else {
13785                    // We're on the last line without a trailing newline.
13786                    // Copy to the end of the line and add a newline afterwards.
13787                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13788                    add_trailing_newline = true;
13789                }
13790            }
13791
13792            let mut trimmed_selections = Vec::new();
13793            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13794                let row = MultiBufferRow(start.row);
13795                let first_indent = buffer.indent_size_for_line(row);
13796                if first_indent.len == 0 || start.column > first_indent.len {
13797                    trimmed_selections.push(start..end);
13798                } else {
13799                    trimmed_selections.push(
13800                        Point::new(row.0, first_indent.len)
13801                            ..Point::new(row.0, buffer.line_len(row)),
13802                    );
13803                    for row in start.row + 1..=end.row {
13804                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13805                        if row == end.row {
13806                            line_len = end.column;
13807                        }
13808                        if line_len == 0 {
13809                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13810                            continue;
13811                        }
13812                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13813                        if row_indent_size.len >= first_indent.len {
13814                            trimmed_selections
13815                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13816                        } else {
13817                            trimmed_selections.clear();
13818                            trimmed_selections.push(start..end);
13819                            break;
13820                        }
13821                    }
13822                }
13823            } else {
13824                trimmed_selections.push(start..end);
13825            }
13826
13827            let is_multiline_trim = trimmed_selections.len() > 1;
13828            let mut selection_len: usize = 0;
13829            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13830
13831            for trimmed_range in trimmed_selections {
13832                if is_first {
13833                    is_first = false;
13834                } else if is_multiline_trim || !prev_selection_was_entire_line {
13835                    text.push('\n');
13836                    if is_multiline_trim {
13837                        selection_len += 1;
13838                    }
13839                }
13840                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13841                    text.push_str(chunk);
13842                    selection_len += chunk.len();
13843                }
13844                if add_trailing_newline {
13845                    text.push('\n');
13846                    selection_len += 1;
13847                }
13848            }
13849
13850            clipboard_selections.push(ClipboardSelection::for_buffer(
13851                selection_len,
13852                is_entire_line,
13853                start..end,
13854                &buffer,
13855                self.project.as_ref(),
13856                cx,
13857            ));
13858        }
13859
13860        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13861            text,
13862            clipboard_selections,
13863        ));
13864    }
13865
13866    pub fn do_paste(
13867        &mut self,
13868        text: &String,
13869        clipboard_selections: Option<Vec<ClipboardSelection>>,
13870        handle_entire_lines: bool,
13871        window: &mut Window,
13872        cx: &mut Context<Self>,
13873    ) {
13874        if self.read_only(cx) {
13875            return;
13876        }
13877
13878        let clipboard_text = Cow::Borrowed(text.as_str());
13879
13880        self.transact(window, cx, |this, window, cx| {
13881            let had_active_edit_prediction = this.has_active_edit_prediction();
13882            let display_map = this.display_snapshot(cx);
13883            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13884            let cursor_offset = this
13885                .selections
13886                .last::<MultiBufferOffset>(&display_map)
13887                .head();
13888
13889            if let Some(mut clipboard_selections) = clipboard_selections {
13890                let all_selections_were_entire_line =
13891                    clipboard_selections.iter().all(|s| s.is_entire_line);
13892                let first_selection_indent_column =
13893                    clipboard_selections.first().map(|s| s.first_line_indent);
13894                if clipboard_selections.len() != old_selections.len() {
13895                    clipboard_selections.drain(..);
13896                }
13897                let mut auto_indent_on_paste = true;
13898
13899                this.buffer.update(cx, |buffer, cx| {
13900                    let snapshot = buffer.read(cx);
13901                    auto_indent_on_paste = snapshot
13902                        .language_settings_at(cursor_offset, cx)
13903                        .auto_indent_on_paste;
13904
13905                    let mut start_offset = 0;
13906                    let mut edits = Vec::new();
13907                    let mut original_indent_columns = Vec::new();
13908                    for (ix, selection) in old_selections.iter().enumerate() {
13909                        let to_insert;
13910                        let entire_line;
13911                        let original_indent_column;
13912                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13913                            let end_offset = start_offset + clipboard_selection.len;
13914                            to_insert = &clipboard_text[start_offset..end_offset];
13915                            entire_line = clipboard_selection.is_entire_line;
13916                            start_offset = if entire_line {
13917                                end_offset
13918                            } else {
13919                                end_offset + 1
13920                            };
13921                            original_indent_column = Some(clipboard_selection.first_line_indent);
13922                        } else {
13923                            to_insert = &*clipboard_text;
13924                            entire_line = all_selections_were_entire_line;
13925                            original_indent_column = first_selection_indent_column
13926                        }
13927
13928                        let (range, to_insert) =
13929                            if selection.is_empty() && handle_entire_lines && entire_line {
13930                                // If the corresponding selection was empty when this slice of the
13931                                // clipboard text was written, then the entire line containing the
13932                                // selection was copied. If this selection is also currently empty,
13933                                // then paste the line before the current line of the buffer.
13934                                let column = selection.start.to_point(&snapshot).column as usize;
13935                                let line_start = selection.start - column;
13936                                (line_start..line_start, Cow::Borrowed(to_insert))
13937                            } else {
13938                                let language = snapshot.language_at(selection.head());
13939                                let range = selection.range();
13940                                if let Some(language) = language
13941                                    && language.name() == "Markdown"
13942                                {
13943                                    edit_for_markdown_paste(
13944                                        &snapshot,
13945                                        range,
13946                                        to_insert,
13947                                        url::Url::parse(to_insert).ok(),
13948                                    )
13949                                } else {
13950                                    (range, Cow::Borrowed(to_insert))
13951                                }
13952                            };
13953
13954                        edits.push((range, to_insert));
13955                        original_indent_columns.push(original_indent_column);
13956                    }
13957                    drop(snapshot);
13958
13959                    buffer.edit(
13960                        edits,
13961                        if auto_indent_on_paste {
13962                            Some(AutoindentMode::Block {
13963                                original_indent_columns,
13964                            })
13965                        } else {
13966                            None
13967                        },
13968                        cx,
13969                    );
13970                });
13971
13972                let selections = this
13973                    .selections
13974                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13975                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13976            } else {
13977                let url = url::Url::parse(&clipboard_text).ok();
13978
13979                let auto_indent_mode = if !clipboard_text.is_empty() {
13980                    Some(AutoindentMode::Block {
13981                        original_indent_columns: Vec::new(),
13982                    })
13983                } else {
13984                    None
13985                };
13986
13987                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13988                    let snapshot = buffer.snapshot(cx);
13989
13990                    let anchors = old_selections
13991                        .iter()
13992                        .map(|s| {
13993                            let anchor = snapshot.anchor_after(s.head());
13994                            s.map(|_| anchor)
13995                        })
13996                        .collect::<Vec<_>>();
13997
13998                    let mut edits = Vec::new();
13999
14000                    // When pasting text without metadata (e.g. copied from an
14001                    // external editor using multiple cursors) and the number of
14002                    // lines matches the number of selections, distribute one
14003                    // line per cursor instead of pasting the whole text at each.
14004                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14005                    let distribute_lines =
14006                        old_selections.len() > 1 && lines.len() == old_selections.len();
14007
14008                    for (ix, selection) in old_selections.iter().enumerate() {
14009                        let language = snapshot.language_at(selection.head());
14010                        let range = selection.range();
14011
14012                        let text_for_cursor: &str = if distribute_lines {
14013                            lines[ix]
14014                        } else {
14015                            &clipboard_text
14016                        };
14017
14018                        let (edit_range, edit_text) = if let Some(language) = language
14019                            && language.name() == "Markdown"
14020                        {
14021                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14022                        } else {
14023                            (range, Cow::Borrowed(text_for_cursor))
14024                        };
14025
14026                        edits.push((edit_range, edit_text));
14027                    }
14028
14029                    drop(snapshot);
14030                    buffer.edit(edits, auto_indent_mode, cx);
14031
14032                    anchors
14033                });
14034
14035                this.change_selections(Default::default(), window, cx, |s| {
14036                    s.select_anchors(selection_anchors);
14037                });
14038            }
14039
14040            //   🤔                 |    ..     | show_in_menu |
14041            // | ..                  |   true        true
14042            // | had_edit_prediction |   false       true
14043
14044            let trigger_in_words =
14045                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14046
14047            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14048        });
14049    }
14050
14051    pub fn diff_clipboard_with_selection(
14052        &mut self,
14053        _: &DiffClipboardWithSelection,
14054        window: &mut Window,
14055        cx: &mut Context<Self>,
14056    ) {
14057        let selections = self
14058            .selections
14059            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14060
14061        if selections.is_empty() {
14062            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14063            return;
14064        };
14065
14066        let clipboard_text = match cx.read_from_clipboard() {
14067            Some(item) => match item.entries().first() {
14068                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
14069                _ => None,
14070            },
14071            None => None,
14072        };
14073
14074        let Some(clipboard_text) = clipboard_text else {
14075            log::warn!("Clipboard doesn't contain text.");
14076            return;
14077        };
14078
14079        window.dispatch_action(
14080            Box::new(DiffClipboardWithSelectionData {
14081                clipboard_text,
14082                editor: cx.entity(),
14083            }),
14084            cx,
14085        );
14086    }
14087
14088    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14089        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14090        if let Some(item) = cx.read_from_clipboard() {
14091            let entries = item.entries();
14092
14093            match entries.first() {
14094                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
14095                // of all the pasted entries.
14096                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
14097                    .do_paste(
14098                        clipboard_string.text(),
14099                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14100                        true,
14101                        window,
14102                        cx,
14103                    ),
14104                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14105            }
14106        }
14107    }
14108
14109    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14110        if self.read_only(cx) {
14111            return;
14112        }
14113
14114        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14115
14116        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14117            if let Some((selections, _)) =
14118                self.selection_history.transaction(transaction_id).cloned()
14119            {
14120                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14121                    s.select_anchors(selections.to_vec());
14122                });
14123            } else {
14124                log::error!(
14125                    "No entry in selection_history found for undo. \
14126                     This may correspond to a bug where undo does not update the selection. \
14127                     If this is occurring, please add details to \
14128                     https://github.com/zed-industries/zed/issues/22692"
14129                );
14130            }
14131            self.request_autoscroll(Autoscroll::fit(), cx);
14132            self.unmark_text(window, cx);
14133            self.refresh_edit_prediction(true, false, window, cx);
14134            cx.emit(EditorEvent::Edited { transaction_id });
14135            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14136        }
14137    }
14138
14139    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14140        if self.read_only(cx) {
14141            return;
14142        }
14143
14144        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14145
14146        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14147            if let Some((_, Some(selections))) =
14148                self.selection_history.transaction(transaction_id).cloned()
14149            {
14150                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14151                    s.select_anchors(selections.to_vec());
14152                });
14153            } else {
14154                log::error!(
14155                    "No entry in selection_history found for redo. \
14156                     This may correspond to a bug where undo does not update the selection. \
14157                     If this is occurring, please add details to \
14158                     https://github.com/zed-industries/zed/issues/22692"
14159                );
14160            }
14161            self.request_autoscroll(Autoscroll::fit(), cx);
14162            self.unmark_text(window, cx);
14163            self.refresh_edit_prediction(true, false, window, cx);
14164            cx.emit(EditorEvent::Edited { transaction_id });
14165        }
14166    }
14167
14168    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14169        self.buffer
14170            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14171    }
14172
14173    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14174        self.buffer
14175            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14176    }
14177
14178    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14179        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14180        self.change_selections(Default::default(), window, cx, |s| {
14181            s.move_with(&mut |map, selection| {
14182                let cursor = if selection.is_empty() {
14183                    movement::left(map, selection.start)
14184                } else {
14185                    selection.start
14186                };
14187                selection.collapse_to(cursor, SelectionGoal::None);
14188            });
14189        })
14190    }
14191
14192    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14193        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14194        self.change_selections(Default::default(), window, cx, |s| {
14195            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14196        })
14197    }
14198
14199    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14200        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14201        self.change_selections(Default::default(), window, cx, |s| {
14202            s.move_with(&mut |map, selection| {
14203                let cursor = if selection.is_empty() {
14204                    movement::right(map, selection.end)
14205                } else {
14206                    selection.end
14207                };
14208                selection.collapse_to(cursor, SelectionGoal::None)
14209            });
14210        })
14211    }
14212
14213    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14214        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14215        self.change_selections(Default::default(), window, cx, |s| {
14216            s.move_heads_with(&mut |map, head, _| {
14217                (movement::right(map, head), SelectionGoal::None)
14218            });
14219        });
14220    }
14221
14222    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14223        if self.take_rename(true, window, cx).is_some() {
14224            return;
14225        }
14226
14227        if self.mode.is_single_line() {
14228            cx.propagate();
14229            return;
14230        }
14231
14232        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14233
14234        let text_layout_details = &self.text_layout_details(window, cx);
14235        let selection_count = self.selections.count();
14236        let first_selection = self.selections.first_anchor();
14237
14238        self.change_selections(Default::default(), window, cx, |s| {
14239            s.move_with(&mut |map, selection| {
14240                if !selection.is_empty() {
14241                    selection.goal = SelectionGoal::None;
14242                }
14243                let (cursor, goal) = movement::up(
14244                    map,
14245                    selection.start,
14246                    selection.goal,
14247                    false,
14248                    text_layout_details,
14249                );
14250                selection.collapse_to(cursor, goal);
14251            });
14252        });
14253
14254        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14255        {
14256            cx.propagate();
14257        }
14258    }
14259
14260    pub fn move_up_by_lines(
14261        &mut self,
14262        action: &MoveUpByLines,
14263        window: &mut Window,
14264        cx: &mut Context<Self>,
14265    ) {
14266        if self.take_rename(true, window, cx).is_some() {
14267            return;
14268        }
14269
14270        if self.mode.is_single_line() {
14271            cx.propagate();
14272            return;
14273        }
14274
14275        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14276
14277        let text_layout_details = &self.text_layout_details(window, cx);
14278
14279        self.change_selections(Default::default(), window, cx, |s| {
14280            s.move_with(&mut |map, selection| {
14281                if !selection.is_empty() {
14282                    selection.goal = SelectionGoal::None;
14283                }
14284                let (cursor, goal) = movement::up_by_rows(
14285                    map,
14286                    selection.start,
14287                    action.lines,
14288                    selection.goal,
14289                    false,
14290                    text_layout_details,
14291                );
14292                selection.collapse_to(cursor, goal);
14293            });
14294        })
14295    }
14296
14297    pub fn move_down_by_lines(
14298        &mut self,
14299        action: &MoveDownByLines,
14300        window: &mut Window,
14301        cx: &mut Context<Self>,
14302    ) {
14303        if self.take_rename(true, window, cx).is_some() {
14304            return;
14305        }
14306
14307        if self.mode.is_single_line() {
14308            cx.propagate();
14309            return;
14310        }
14311
14312        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14313
14314        let text_layout_details = &self.text_layout_details(window, cx);
14315
14316        self.change_selections(Default::default(), window, cx, |s| {
14317            s.move_with(&mut |map, selection| {
14318                if !selection.is_empty() {
14319                    selection.goal = SelectionGoal::None;
14320                }
14321                let (cursor, goal) = movement::down_by_rows(
14322                    map,
14323                    selection.start,
14324                    action.lines,
14325                    selection.goal,
14326                    false,
14327                    text_layout_details,
14328                );
14329                selection.collapse_to(cursor, goal);
14330            });
14331        })
14332    }
14333
14334    pub fn select_down_by_lines(
14335        &mut self,
14336        action: &SelectDownByLines,
14337        window: &mut Window,
14338        cx: &mut Context<Self>,
14339    ) {
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::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14345            })
14346        })
14347    }
14348
14349    pub fn select_up_by_lines(
14350        &mut self,
14351        action: &SelectUpByLines,
14352        window: &mut Window,
14353        cx: &mut Context<Self>,
14354    ) {
14355        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14356        let text_layout_details = &self.text_layout_details(window, cx);
14357        self.change_selections(Default::default(), window, cx, |s| {
14358            s.move_heads_with(&mut |map, head, goal| {
14359                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14360            })
14361        })
14362    }
14363
14364    pub fn select_page_up(
14365        &mut self,
14366        _: &SelectPageUp,
14367        window: &mut Window,
14368        cx: &mut Context<Self>,
14369    ) {
14370        let Some(row_count) = self.visible_row_count() else {
14371            return;
14372        };
14373
14374        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14375
14376        let text_layout_details = &self.text_layout_details(window, cx);
14377
14378        self.change_selections(Default::default(), window, cx, |s| {
14379            s.move_heads_with(&mut |map, head, goal| {
14380                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14381            })
14382        })
14383    }
14384
14385    pub fn move_page_up(
14386        &mut self,
14387        action: &MovePageUp,
14388        window: &mut Window,
14389        cx: &mut Context<Self>,
14390    ) {
14391        if self.take_rename(true, window, cx).is_some() {
14392            return;
14393        }
14394
14395        if self
14396            .context_menu
14397            .borrow_mut()
14398            .as_mut()
14399            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14400            .unwrap_or(false)
14401        {
14402            return;
14403        }
14404
14405        if matches!(self.mode, EditorMode::SingleLine) {
14406            cx.propagate();
14407            return;
14408        }
14409
14410        let Some(row_count) = self.visible_row_count() else {
14411            return;
14412        };
14413
14414        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14415
14416        let effects = if action.center_cursor {
14417            SelectionEffects::scroll(Autoscroll::center())
14418        } else {
14419            SelectionEffects::default()
14420        };
14421
14422        let text_layout_details = &self.text_layout_details(window, cx);
14423
14424        self.change_selections(effects, window, cx, |s| {
14425            s.move_with(&mut |map, selection| {
14426                if !selection.is_empty() {
14427                    selection.goal = SelectionGoal::None;
14428                }
14429                let (cursor, goal) = movement::up_by_rows(
14430                    map,
14431                    selection.end,
14432                    row_count,
14433                    selection.goal,
14434                    false,
14435                    text_layout_details,
14436                );
14437                selection.collapse_to(cursor, goal);
14438            });
14439        });
14440    }
14441
14442    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14443        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14444        let text_layout_details = &self.text_layout_details(window, cx);
14445        self.change_selections(Default::default(), window, cx, |s| {
14446            s.move_heads_with(&mut |map, head, goal| {
14447                movement::up(map, head, goal, false, text_layout_details)
14448            })
14449        })
14450    }
14451
14452    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14453        self.take_rename(true, window, cx);
14454
14455        if self.mode.is_single_line() {
14456            cx.propagate();
14457            return;
14458        }
14459
14460        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14461
14462        let text_layout_details = &self.text_layout_details(window, cx);
14463        let selection_count = self.selections.count();
14464        let first_selection = self.selections.first_anchor();
14465
14466        self.change_selections(Default::default(), window, cx, |s| {
14467            s.move_with(&mut |map, selection| {
14468                if !selection.is_empty() {
14469                    selection.goal = SelectionGoal::None;
14470                }
14471                let (cursor, goal) = movement::down(
14472                    map,
14473                    selection.end,
14474                    selection.goal,
14475                    false,
14476                    text_layout_details,
14477                );
14478                selection.collapse_to(cursor, goal);
14479            });
14480        });
14481
14482        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14483        {
14484            cx.propagate();
14485        }
14486    }
14487
14488    pub fn select_page_down(
14489        &mut self,
14490        _: &SelectPageDown,
14491        window: &mut Window,
14492        cx: &mut Context<Self>,
14493    ) {
14494        let Some(row_count) = self.visible_row_count() else {
14495            return;
14496        };
14497
14498        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14499
14500        let text_layout_details = &self.text_layout_details(window, cx);
14501
14502        self.change_selections(Default::default(), window, cx, |s| {
14503            s.move_heads_with(&mut |map, head, goal| {
14504                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14505            })
14506        })
14507    }
14508
14509    pub fn move_page_down(
14510        &mut self,
14511        action: &MovePageDown,
14512        window: &mut Window,
14513        cx: &mut Context<Self>,
14514    ) {
14515        if self.take_rename(true, window, cx).is_some() {
14516            return;
14517        }
14518
14519        if self
14520            .context_menu
14521            .borrow_mut()
14522            .as_mut()
14523            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14524            .unwrap_or(false)
14525        {
14526            return;
14527        }
14528
14529        if matches!(self.mode, EditorMode::SingleLine) {
14530            cx.propagate();
14531            return;
14532        }
14533
14534        let Some(row_count) = self.visible_row_count() else {
14535            return;
14536        };
14537
14538        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14539
14540        let effects = if action.center_cursor {
14541            SelectionEffects::scroll(Autoscroll::center())
14542        } else {
14543            SelectionEffects::default()
14544        };
14545
14546        let text_layout_details = &self.text_layout_details(window, cx);
14547        self.change_selections(effects, window, cx, |s| {
14548            s.move_with(&mut |map, selection| {
14549                if !selection.is_empty() {
14550                    selection.goal = SelectionGoal::None;
14551                }
14552                let (cursor, goal) = movement::down_by_rows(
14553                    map,
14554                    selection.end,
14555                    row_count,
14556                    selection.goal,
14557                    false,
14558                    text_layout_details,
14559                );
14560                selection.collapse_to(cursor, goal);
14561            });
14562        });
14563    }
14564
14565    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14566        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14567        let text_layout_details = &self.text_layout_details(window, cx);
14568        self.change_selections(Default::default(), window, cx, |s| {
14569            s.move_heads_with(&mut |map, head, goal| {
14570                movement::down(map, head, goal, false, text_layout_details)
14571            })
14572        });
14573    }
14574
14575    pub fn context_menu_first(
14576        &mut self,
14577        _: &ContextMenuFirst,
14578        window: &mut Window,
14579        cx: &mut Context<Self>,
14580    ) {
14581        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14582            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14583        }
14584    }
14585
14586    pub fn context_menu_prev(
14587        &mut self,
14588        _: &ContextMenuPrevious,
14589        window: &mut Window,
14590        cx: &mut Context<Self>,
14591    ) {
14592        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14593            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14594        }
14595    }
14596
14597    pub fn context_menu_next(
14598        &mut self,
14599        _: &ContextMenuNext,
14600        window: &mut Window,
14601        cx: &mut Context<Self>,
14602    ) {
14603        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14604            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14605        }
14606    }
14607
14608    pub fn context_menu_last(
14609        &mut self,
14610        _: &ContextMenuLast,
14611        window: &mut Window,
14612        cx: &mut Context<Self>,
14613    ) {
14614        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14615            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14616        }
14617    }
14618
14619    pub fn signature_help_prev(
14620        &mut self,
14621        _: &SignatureHelpPrevious,
14622        _: &mut Window,
14623        cx: &mut Context<Self>,
14624    ) {
14625        if let Some(popover) = self.signature_help_state.popover_mut() {
14626            if popover.current_signature == 0 {
14627                popover.current_signature = popover.signatures.len() - 1;
14628            } else {
14629                popover.current_signature -= 1;
14630            }
14631            cx.notify();
14632        }
14633    }
14634
14635    pub fn signature_help_next(
14636        &mut self,
14637        _: &SignatureHelpNext,
14638        _: &mut Window,
14639        cx: &mut Context<Self>,
14640    ) {
14641        if let Some(popover) = self.signature_help_state.popover_mut() {
14642            if popover.current_signature + 1 == popover.signatures.len() {
14643                popover.current_signature = 0;
14644            } else {
14645                popover.current_signature += 1;
14646            }
14647            cx.notify();
14648        }
14649    }
14650
14651    pub fn move_to_previous_word_start(
14652        &mut self,
14653        _: &MoveToPreviousWordStart,
14654        window: &mut Window,
14655        cx: &mut Context<Self>,
14656    ) {
14657        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14658        self.change_selections(Default::default(), window, cx, |s| {
14659            s.move_cursors_with(&mut |map, head, _| {
14660                (
14661                    movement::previous_word_start(map, head),
14662                    SelectionGoal::None,
14663                )
14664            });
14665        })
14666    }
14667
14668    pub fn move_to_previous_subword_start(
14669        &mut self,
14670        _: &MoveToPreviousSubwordStart,
14671        window: &mut Window,
14672        cx: &mut Context<Self>,
14673    ) {
14674        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14675        self.change_selections(Default::default(), window, cx, |s| {
14676            s.move_cursors_with(&mut |map, head, _| {
14677                (
14678                    movement::previous_subword_start(map, head),
14679                    SelectionGoal::None,
14680                )
14681            });
14682        })
14683    }
14684
14685    pub fn select_to_previous_word_start(
14686        &mut self,
14687        _: &SelectToPreviousWordStart,
14688        window: &mut Window,
14689        cx: &mut Context<Self>,
14690    ) {
14691        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14692        self.change_selections(Default::default(), window, cx, |s| {
14693            s.move_heads_with(&mut |map, head, _| {
14694                (
14695                    movement::previous_word_start(map, head),
14696                    SelectionGoal::None,
14697                )
14698            });
14699        })
14700    }
14701
14702    pub fn select_to_previous_subword_start(
14703        &mut self,
14704        _: &SelectToPreviousSubwordStart,
14705        window: &mut Window,
14706        cx: &mut Context<Self>,
14707    ) {
14708        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14709        self.change_selections(Default::default(), window, cx, |s| {
14710            s.move_heads_with(&mut |map, head, _| {
14711                (
14712                    movement::previous_subword_start(map, head),
14713                    SelectionGoal::None,
14714                )
14715            });
14716        })
14717    }
14718
14719    pub fn delete_to_previous_word_start(
14720        &mut self,
14721        action: &DeleteToPreviousWordStart,
14722        window: &mut Window,
14723        cx: &mut Context<Self>,
14724    ) {
14725        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14726        self.transact(window, cx, |this, window, cx| {
14727            this.select_autoclose_pair(window, cx);
14728            this.change_selections(Default::default(), window, cx, |s| {
14729                s.move_with(&mut |map, selection| {
14730                    if selection.is_empty() {
14731                        let mut cursor = if action.ignore_newlines {
14732                            movement::previous_word_start(map, selection.head())
14733                        } else {
14734                            movement::previous_word_start_or_newline(map, selection.head())
14735                        };
14736                        cursor = movement::adjust_greedy_deletion(
14737                            map,
14738                            selection.head(),
14739                            cursor,
14740                            action.ignore_brackets,
14741                        );
14742                        selection.set_head(cursor, SelectionGoal::None);
14743                    }
14744                });
14745            });
14746            this.insert("", window, cx);
14747        });
14748    }
14749
14750    pub fn delete_to_previous_subword_start(
14751        &mut self,
14752        action: &DeleteToPreviousSubwordStart,
14753        window: &mut Window,
14754        cx: &mut Context<Self>,
14755    ) {
14756        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14757        self.transact(window, cx, |this, window, cx| {
14758            this.select_autoclose_pair(window, cx);
14759            this.change_selections(Default::default(), window, cx, |s| {
14760                s.move_with(&mut |map, selection| {
14761                    if selection.is_empty() {
14762                        let mut cursor = if action.ignore_newlines {
14763                            movement::previous_subword_start(map, selection.head())
14764                        } else {
14765                            movement::previous_subword_start_or_newline(map, selection.head())
14766                        };
14767                        cursor = movement::adjust_greedy_deletion(
14768                            map,
14769                            selection.head(),
14770                            cursor,
14771                            action.ignore_brackets,
14772                        );
14773                        selection.set_head(cursor, SelectionGoal::None);
14774                    }
14775                });
14776            });
14777            this.insert("", window, cx);
14778        });
14779    }
14780
14781    pub fn move_to_next_word_end(
14782        &mut self,
14783        _: &MoveToNextWordEnd,
14784        window: &mut Window,
14785        cx: &mut Context<Self>,
14786    ) {
14787        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14788        self.change_selections(Default::default(), window, cx, |s| {
14789            s.move_cursors_with(&mut |map, head, _| {
14790                (movement::next_word_end(map, head), SelectionGoal::None)
14791            });
14792        })
14793    }
14794
14795    pub fn move_to_next_subword_end(
14796        &mut self,
14797        _: &MoveToNextSubwordEnd,
14798        window: &mut Window,
14799        cx: &mut Context<Self>,
14800    ) {
14801        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14802        self.change_selections(Default::default(), window, cx, |s| {
14803            s.move_cursors_with(&mut |map, head, _| {
14804                (movement::next_subword_end(map, head), SelectionGoal::None)
14805            });
14806        })
14807    }
14808
14809    pub fn select_to_next_word_end(
14810        &mut self,
14811        _: &SelectToNextWordEnd,
14812        window: &mut Window,
14813        cx: &mut Context<Self>,
14814    ) {
14815        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14816        self.change_selections(Default::default(), window, cx, |s| {
14817            s.move_heads_with(&mut |map, head, _| {
14818                (movement::next_word_end(map, head), SelectionGoal::None)
14819            });
14820        })
14821    }
14822
14823    pub fn select_to_next_subword_end(
14824        &mut self,
14825        _: &SelectToNextSubwordEnd,
14826        window: &mut Window,
14827        cx: &mut Context<Self>,
14828    ) {
14829        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14830        self.change_selections(Default::default(), window, cx, |s| {
14831            s.move_heads_with(&mut |map, head, _| {
14832                (movement::next_subword_end(map, head), SelectionGoal::None)
14833            });
14834        })
14835    }
14836
14837    pub fn delete_to_next_word_end(
14838        &mut self,
14839        action: &DeleteToNextWordEnd,
14840        window: &mut Window,
14841        cx: &mut Context<Self>,
14842    ) {
14843        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14844        self.transact(window, cx, |this, window, cx| {
14845            this.change_selections(Default::default(), window, cx, |s| {
14846                s.move_with(&mut |map, selection| {
14847                    if selection.is_empty() {
14848                        let mut cursor = if action.ignore_newlines {
14849                            movement::next_word_end(map, selection.head())
14850                        } else {
14851                            movement::next_word_end_or_newline(map, selection.head())
14852                        };
14853                        cursor = movement::adjust_greedy_deletion(
14854                            map,
14855                            selection.head(),
14856                            cursor,
14857                            action.ignore_brackets,
14858                        );
14859                        selection.set_head(cursor, SelectionGoal::None);
14860                    }
14861                });
14862            });
14863            this.insert("", window, cx);
14864        });
14865    }
14866
14867    pub fn delete_to_next_subword_end(
14868        &mut self,
14869        action: &DeleteToNextSubwordEnd,
14870        window: &mut Window,
14871        cx: &mut Context<Self>,
14872    ) {
14873        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14874        self.transact(window, cx, |this, window, cx| {
14875            this.change_selections(Default::default(), window, cx, |s| {
14876                s.move_with(&mut |map, selection| {
14877                    if selection.is_empty() {
14878                        let mut cursor = if action.ignore_newlines {
14879                            movement::next_subword_end(map, selection.head())
14880                        } else {
14881                            movement::next_subword_end_or_newline(map, selection.head())
14882                        };
14883                        cursor = movement::adjust_greedy_deletion(
14884                            map,
14885                            selection.head(),
14886                            cursor,
14887                            action.ignore_brackets,
14888                        );
14889                        selection.set_head(cursor, SelectionGoal::None);
14890                    }
14891                });
14892            });
14893            this.insert("", window, cx);
14894        });
14895    }
14896
14897    pub fn move_to_beginning_of_line(
14898        &mut self,
14899        action: &MoveToBeginningOfLine,
14900        window: &mut Window,
14901        cx: &mut Context<Self>,
14902    ) {
14903        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14904        self.change_selections(Default::default(), window, cx, |s| {
14905            s.move_cursors_with(&mut |map, head, _| {
14906                (
14907                    movement::indented_line_beginning(
14908                        map,
14909                        head,
14910                        action.stop_at_soft_wraps,
14911                        action.stop_at_indent,
14912                    ),
14913                    SelectionGoal::None,
14914                )
14915            });
14916        })
14917    }
14918
14919    pub fn select_to_beginning_of_line(
14920        &mut self,
14921        action: &SelectToBeginningOfLine,
14922        window: &mut Window,
14923        cx: &mut Context<Self>,
14924    ) {
14925        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14926        self.change_selections(Default::default(), window, cx, |s| {
14927            s.move_heads_with(&mut |map, head, _| {
14928                (
14929                    movement::indented_line_beginning(
14930                        map,
14931                        head,
14932                        action.stop_at_soft_wraps,
14933                        action.stop_at_indent,
14934                    ),
14935                    SelectionGoal::None,
14936                )
14937            });
14938        });
14939    }
14940
14941    pub fn delete_to_beginning_of_line(
14942        &mut self,
14943        action: &DeleteToBeginningOfLine,
14944        window: &mut Window,
14945        cx: &mut Context<Self>,
14946    ) {
14947        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14948        self.transact(window, cx, |this, window, cx| {
14949            this.change_selections(Default::default(), window, cx, |s| {
14950                s.move_with(&mut |_, selection| {
14951                    selection.reversed = true;
14952                });
14953            });
14954
14955            this.select_to_beginning_of_line(
14956                &SelectToBeginningOfLine {
14957                    stop_at_soft_wraps: false,
14958                    stop_at_indent: action.stop_at_indent,
14959                },
14960                window,
14961                cx,
14962            );
14963            this.backspace(&Backspace, window, cx);
14964        });
14965    }
14966
14967    pub fn move_to_end_of_line(
14968        &mut self,
14969        action: &MoveToEndOfLine,
14970        window: &mut Window,
14971        cx: &mut Context<Self>,
14972    ) {
14973        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14974        self.change_selections(Default::default(), window, cx, |s| {
14975            s.move_cursors_with(&mut |map, head, _| {
14976                (
14977                    movement::line_end(map, head, action.stop_at_soft_wraps),
14978                    SelectionGoal::None,
14979                )
14980            });
14981        })
14982    }
14983
14984    pub fn select_to_end_of_line(
14985        &mut self,
14986        action: &SelectToEndOfLine,
14987        window: &mut Window,
14988        cx: &mut Context<Self>,
14989    ) {
14990        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14991        self.change_selections(Default::default(), window, cx, |s| {
14992            s.move_heads_with(&mut |map, head, _| {
14993                (
14994                    movement::line_end(map, head, action.stop_at_soft_wraps),
14995                    SelectionGoal::None,
14996                )
14997            });
14998        })
14999    }
15000
15001    pub fn delete_to_end_of_line(
15002        &mut self,
15003        _: &DeleteToEndOfLine,
15004        window: &mut Window,
15005        cx: &mut Context<Self>,
15006    ) {
15007        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15008        self.transact(window, cx, |this, window, cx| {
15009            this.select_to_end_of_line(
15010                &SelectToEndOfLine {
15011                    stop_at_soft_wraps: false,
15012                },
15013                window,
15014                cx,
15015            );
15016            this.delete(&Delete, window, cx);
15017        });
15018    }
15019
15020    pub fn cut_to_end_of_line(
15021        &mut self,
15022        action: &CutToEndOfLine,
15023        window: &mut Window,
15024        cx: &mut Context<Self>,
15025    ) {
15026        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15027        self.transact(window, cx, |this, window, cx| {
15028            this.select_to_end_of_line(
15029                &SelectToEndOfLine {
15030                    stop_at_soft_wraps: false,
15031                },
15032                window,
15033                cx,
15034            );
15035            if !action.stop_at_newlines {
15036                this.change_selections(Default::default(), window, cx, |s| {
15037                    s.move_with(&mut |_, sel| {
15038                        if sel.is_empty() {
15039                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15040                        }
15041                    });
15042                });
15043            }
15044            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15045            let item = this.cut_common(false, window, cx);
15046            cx.write_to_clipboard(item);
15047        });
15048    }
15049
15050    pub fn move_to_start_of_paragraph(
15051        &mut self,
15052        _: &MoveToStartOfParagraph,
15053        window: &mut Window,
15054        cx: &mut Context<Self>,
15055    ) {
15056        if matches!(self.mode, EditorMode::SingleLine) {
15057            cx.propagate();
15058            return;
15059        }
15060        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15061        self.change_selections(Default::default(), window, cx, |s| {
15062            s.move_with(&mut |map, selection| {
15063                selection.collapse_to(
15064                    movement::start_of_paragraph(map, selection.head(), 1),
15065                    SelectionGoal::None,
15066                )
15067            });
15068        })
15069    }
15070
15071    pub fn move_to_end_of_paragraph(
15072        &mut self,
15073        _: &MoveToEndOfParagraph,
15074        window: &mut Window,
15075        cx: &mut Context<Self>,
15076    ) {
15077        if matches!(self.mode, EditorMode::SingleLine) {
15078            cx.propagate();
15079            return;
15080        }
15081        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15082        self.change_selections(Default::default(), window, cx, |s| {
15083            s.move_with(&mut |map, selection| {
15084                selection.collapse_to(
15085                    movement::end_of_paragraph(map, selection.head(), 1),
15086                    SelectionGoal::None,
15087                )
15088            });
15089        })
15090    }
15091
15092    pub fn select_to_start_of_paragraph(
15093        &mut self,
15094        _: &SelectToStartOfParagraph,
15095        window: &mut Window,
15096        cx: &mut Context<Self>,
15097    ) {
15098        if matches!(self.mode, EditorMode::SingleLine) {
15099            cx.propagate();
15100            return;
15101        }
15102        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15103        self.change_selections(Default::default(), window, cx, |s| {
15104            s.move_heads_with(&mut |map, head, _| {
15105                (
15106                    movement::start_of_paragraph(map, head, 1),
15107                    SelectionGoal::None,
15108                )
15109            });
15110        })
15111    }
15112
15113    pub fn select_to_end_of_paragraph(
15114        &mut self,
15115        _: &SelectToEndOfParagraph,
15116        window: &mut Window,
15117        cx: &mut Context<Self>,
15118    ) {
15119        if matches!(self.mode, EditorMode::SingleLine) {
15120            cx.propagate();
15121            return;
15122        }
15123        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15124        self.change_selections(Default::default(), window, cx, |s| {
15125            s.move_heads_with(&mut |map, head, _| {
15126                (
15127                    movement::end_of_paragraph(map, head, 1),
15128                    SelectionGoal::None,
15129                )
15130            });
15131        })
15132    }
15133
15134    pub fn move_to_start_of_excerpt(
15135        &mut self,
15136        _: &MoveToStartOfExcerpt,
15137        window: &mut Window,
15138        cx: &mut Context<Self>,
15139    ) {
15140        if matches!(self.mode, EditorMode::SingleLine) {
15141            cx.propagate();
15142            return;
15143        }
15144        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15145        self.change_selections(Default::default(), window, cx, |s| {
15146            s.move_with(&mut |map, selection| {
15147                selection.collapse_to(
15148                    movement::start_of_excerpt(
15149                        map,
15150                        selection.head(),
15151                        workspace::searchable::Direction::Prev,
15152                    ),
15153                    SelectionGoal::None,
15154                )
15155            });
15156        })
15157    }
15158
15159    pub fn move_to_start_of_next_excerpt(
15160        &mut self,
15161        _: &MoveToStartOfNextExcerpt,
15162        window: &mut Window,
15163        cx: &mut Context<Self>,
15164    ) {
15165        if matches!(self.mode, EditorMode::SingleLine) {
15166            cx.propagate();
15167            return;
15168        }
15169
15170        self.change_selections(Default::default(), window, cx, |s| {
15171            s.move_with(&mut |map, selection| {
15172                selection.collapse_to(
15173                    movement::start_of_excerpt(
15174                        map,
15175                        selection.head(),
15176                        workspace::searchable::Direction::Next,
15177                    ),
15178                    SelectionGoal::None,
15179                )
15180            });
15181        })
15182    }
15183
15184    pub fn move_to_end_of_excerpt(
15185        &mut self,
15186        _: &MoveToEndOfExcerpt,
15187        window: &mut Window,
15188        cx: &mut Context<Self>,
15189    ) {
15190        if matches!(self.mode, EditorMode::SingleLine) {
15191            cx.propagate();
15192            return;
15193        }
15194        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15195        self.change_selections(Default::default(), window, cx, |s| {
15196            s.move_with(&mut |map, selection| {
15197                selection.collapse_to(
15198                    movement::end_of_excerpt(
15199                        map,
15200                        selection.head(),
15201                        workspace::searchable::Direction::Next,
15202                    ),
15203                    SelectionGoal::None,
15204                )
15205            });
15206        })
15207    }
15208
15209    pub fn move_to_end_of_previous_excerpt(
15210        &mut self,
15211        _: &MoveToEndOfPreviousExcerpt,
15212        window: &mut Window,
15213        cx: &mut Context<Self>,
15214    ) {
15215        if matches!(self.mode, EditorMode::SingleLine) {
15216            cx.propagate();
15217            return;
15218        }
15219        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15220        self.change_selections(Default::default(), window, cx, |s| {
15221            s.move_with(&mut |map, selection| {
15222                selection.collapse_to(
15223                    movement::end_of_excerpt(
15224                        map,
15225                        selection.head(),
15226                        workspace::searchable::Direction::Prev,
15227                    ),
15228                    SelectionGoal::None,
15229                )
15230            });
15231        })
15232    }
15233
15234    pub fn select_to_start_of_excerpt(
15235        &mut self,
15236        _: &SelectToStartOfExcerpt,
15237        window: &mut Window,
15238        cx: &mut Context<Self>,
15239    ) {
15240        if matches!(self.mode, EditorMode::SingleLine) {
15241            cx.propagate();
15242            return;
15243        }
15244        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15245        self.change_selections(Default::default(), window, cx, |s| {
15246            s.move_heads_with(&mut |map, head, _| {
15247                (
15248                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15249                    SelectionGoal::None,
15250                )
15251            });
15252        })
15253    }
15254
15255    pub fn select_to_start_of_next_excerpt(
15256        &mut self,
15257        _: &SelectToStartOfNextExcerpt,
15258        window: &mut Window,
15259        cx: &mut Context<Self>,
15260    ) {
15261        if matches!(self.mode, EditorMode::SingleLine) {
15262            cx.propagate();
15263            return;
15264        }
15265        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15266        self.change_selections(Default::default(), window, cx, |s| {
15267            s.move_heads_with(&mut |map, head, _| {
15268                (
15269                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15270                    SelectionGoal::None,
15271                )
15272            });
15273        })
15274    }
15275
15276    pub fn select_to_end_of_excerpt(
15277        &mut self,
15278        _: &SelectToEndOfExcerpt,
15279        window: &mut Window,
15280        cx: &mut Context<Self>,
15281    ) {
15282        if matches!(self.mode, EditorMode::SingleLine) {
15283            cx.propagate();
15284            return;
15285        }
15286        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15287        self.change_selections(Default::default(), window, cx, |s| {
15288            s.move_heads_with(&mut |map, head, _| {
15289                (
15290                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15291                    SelectionGoal::None,
15292                )
15293            });
15294        })
15295    }
15296
15297    pub fn select_to_end_of_previous_excerpt(
15298        &mut self,
15299        _: &SelectToEndOfPreviousExcerpt,
15300        window: &mut Window,
15301        cx: &mut Context<Self>,
15302    ) {
15303        if matches!(self.mode, EditorMode::SingleLine) {
15304            cx.propagate();
15305            return;
15306        }
15307        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15308        self.change_selections(Default::default(), window, cx, |s| {
15309            s.move_heads_with(&mut |map, head, _| {
15310                (
15311                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15312                    SelectionGoal::None,
15313                )
15314            });
15315        })
15316    }
15317
15318    pub fn move_to_beginning(
15319        &mut self,
15320        _: &MoveToBeginning,
15321        window: &mut Window,
15322        cx: &mut Context<Self>,
15323    ) {
15324        if matches!(self.mode, EditorMode::SingleLine) {
15325            cx.propagate();
15326            return;
15327        }
15328        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15329        self.change_selections(Default::default(), window, cx, |s| {
15330            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15331        });
15332    }
15333
15334    pub fn select_to_beginning(
15335        &mut self,
15336        _: &SelectToBeginning,
15337        window: &mut Window,
15338        cx: &mut Context<Self>,
15339    ) {
15340        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15341        selection.set_head(Point::zero(), SelectionGoal::None);
15342        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15343        self.change_selections(Default::default(), window, cx, |s| {
15344            s.select(vec![selection]);
15345        });
15346    }
15347
15348    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15349        if matches!(self.mode, EditorMode::SingleLine) {
15350            cx.propagate();
15351            return;
15352        }
15353        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15354        let cursor = self.buffer.read(cx).read(cx).len();
15355        self.change_selections(Default::default(), window, cx, |s| {
15356            s.select_ranges(vec![cursor..cursor])
15357        });
15358    }
15359
15360    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15361        self.nav_history = nav_history;
15362    }
15363
15364    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15365        self.nav_history.as_ref()
15366    }
15367
15368    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15369        self.push_to_nav_history(
15370            self.selections.newest_anchor().head(),
15371            None,
15372            false,
15373            true,
15374            cx,
15375        );
15376    }
15377
15378    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15379        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15380        let buffer = self.buffer.read(cx).read(cx);
15381        let cursor_position = cursor_anchor.to_point(&buffer);
15382        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15383        let scroll_top_row = scroll_anchor.top_row(&buffer);
15384        drop(buffer);
15385
15386        NavigationData {
15387            cursor_anchor,
15388            cursor_position,
15389            scroll_anchor,
15390            scroll_top_row,
15391        }
15392    }
15393
15394    fn navigation_entry(
15395        &self,
15396        cursor_anchor: Anchor,
15397        cx: &mut Context<Self>,
15398    ) -> Option<NavigationEntry> {
15399        let Some(history) = self.nav_history.clone() else {
15400            return None;
15401        };
15402        let data = self.navigation_data(cursor_anchor, cx);
15403        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15404    }
15405
15406    fn push_to_nav_history(
15407        &mut self,
15408        cursor_anchor: Anchor,
15409        new_position: Option<Point>,
15410        is_deactivate: bool,
15411        always: bool,
15412        cx: &mut Context<Self>,
15413    ) {
15414        let data = self.navigation_data(cursor_anchor, cx);
15415        if let Some(nav_history) = self.nav_history.as_mut() {
15416            if let Some(new_position) = new_position {
15417                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15418                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15419                    return;
15420                }
15421            }
15422
15423            nav_history.push(Some(data), cx);
15424            cx.emit(EditorEvent::PushedToNavHistory {
15425                anchor: cursor_anchor,
15426                is_deactivate,
15427            })
15428        }
15429    }
15430
15431    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15432        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15433        let buffer = self.buffer.read(cx).snapshot(cx);
15434        let mut selection = self
15435            .selections
15436            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15437        selection.set_head(buffer.len(), SelectionGoal::None);
15438        self.change_selections(Default::default(), window, cx, |s| {
15439            s.select(vec![selection]);
15440        });
15441    }
15442
15443    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15444        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15445        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15446            s.select_ranges([Anchor::min()..Anchor::max()]);
15447        });
15448    }
15449
15450    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15451        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15452        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15453        let mut selections = self.selections.all::<Point>(&display_map);
15454        let max_point = display_map.buffer_snapshot().max_point();
15455        for selection in &mut selections {
15456            let rows = selection.spanned_rows(true, &display_map);
15457            selection.start = Point::new(rows.start.0, 0);
15458            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15459            selection.reversed = false;
15460        }
15461        self.change_selections(Default::default(), window, cx, |s| {
15462            s.select(selections);
15463        });
15464    }
15465
15466    pub fn split_selection_into_lines(
15467        &mut self,
15468        action: &SplitSelectionIntoLines,
15469        window: &mut Window,
15470        cx: &mut Context<Self>,
15471    ) {
15472        let selections = self
15473            .selections
15474            .all::<Point>(&self.display_snapshot(cx))
15475            .into_iter()
15476            .map(|selection| selection.start..selection.end)
15477            .collect::<Vec<_>>();
15478        self.unfold_ranges(&selections, true, false, cx);
15479
15480        let mut new_selection_ranges = Vec::new();
15481        {
15482            let buffer = self.buffer.read(cx).read(cx);
15483            for selection in selections {
15484                for row in selection.start.row..selection.end.row {
15485                    let line_start = Point::new(row, 0);
15486                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15487
15488                    if action.keep_selections {
15489                        // Keep the selection range for each line
15490                        let selection_start = if row == selection.start.row {
15491                            selection.start
15492                        } else {
15493                            line_start
15494                        };
15495                        new_selection_ranges.push(selection_start..line_end);
15496                    } else {
15497                        // Collapse to cursor at end of line
15498                        new_selection_ranges.push(line_end..line_end);
15499                    }
15500                }
15501
15502                let is_multiline_selection = selection.start.row != selection.end.row;
15503                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15504                // so this action feels more ergonomic when paired with other selection operations
15505                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15506                if !should_skip_last {
15507                    if action.keep_selections {
15508                        if is_multiline_selection {
15509                            let line_start = Point::new(selection.end.row, 0);
15510                            new_selection_ranges.push(line_start..selection.end);
15511                        } else {
15512                            new_selection_ranges.push(selection.start..selection.end);
15513                        }
15514                    } else {
15515                        new_selection_ranges.push(selection.end..selection.end);
15516                    }
15517                }
15518            }
15519        }
15520        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15521            s.select_ranges(new_selection_ranges);
15522        });
15523    }
15524
15525    pub fn add_selection_above(
15526        &mut self,
15527        action: &AddSelectionAbove,
15528        window: &mut Window,
15529        cx: &mut Context<Self>,
15530    ) {
15531        self.add_selection(true, action.skip_soft_wrap, window, cx);
15532    }
15533
15534    pub fn add_selection_below(
15535        &mut self,
15536        action: &AddSelectionBelow,
15537        window: &mut Window,
15538        cx: &mut Context<Self>,
15539    ) {
15540        self.add_selection(false, action.skip_soft_wrap, window, cx);
15541    }
15542
15543    fn add_selection(
15544        &mut self,
15545        above: bool,
15546        skip_soft_wrap: bool,
15547        window: &mut Window,
15548        cx: &mut Context<Self>,
15549    ) {
15550        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15551
15552        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15553        let all_selections = self.selections.all::<Point>(&display_map);
15554        let text_layout_details = self.text_layout_details(window, cx);
15555
15556        let (mut columnar_selections, new_selections_to_columnarize) = {
15557            if let Some(state) = self.add_selections_state.as_ref() {
15558                let columnar_selection_ids: HashSet<_> = state
15559                    .groups
15560                    .iter()
15561                    .flat_map(|group| group.stack.iter())
15562                    .copied()
15563                    .collect();
15564
15565                all_selections
15566                    .into_iter()
15567                    .partition(|s| columnar_selection_ids.contains(&s.id))
15568            } else {
15569                (Vec::new(), all_selections)
15570            }
15571        };
15572
15573        let mut state = self
15574            .add_selections_state
15575            .take()
15576            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15577
15578        for selection in new_selections_to_columnarize {
15579            let range = selection.display_range(&display_map).sorted();
15580            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15581            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15582            let positions = start_x.min(end_x)..start_x.max(end_x);
15583            let mut stack = Vec::new();
15584            for row in range.start.row().0..=range.end.row().0 {
15585                if let Some(selection) = self.selections.build_columnar_selection(
15586                    &display_map,
15587                    DisplayRow(row),
15588                    &positions,
15589                    selection.reversed,
15590                    &text_layout_details,
15591                ) {
15592                    stack.push(selection.id);
15593                    columnar_selections.push(selection);
15594                }
15595            }
15596            if !stack.is_empty() {
15597                if above {
15598                    stack.reverse();
15599                }
15600                state.groups.push(AddSelectionsGroup { above, stack });
15601            }
15602        }
15603
15604        let mut final_selections = Vec::new();
15605        let end_row = if above {
15606            DisplayRow(0)
15607        } else {
15608            display_map.max_point().row()
15609        };
15610
15611        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15612        // positions to place new selections, so we need to keep track of the
15613        // column range of the oldest selection in each group, because
15614        // intermediate selections may have been clamped to shorter lines.
15615        // selections may have been clamped to shorter lines.
15616        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15617            let mut map = HashMap::default();
15618            for group in state.groups.iter() {
15619                if let Some(oldest_id) = group.stack.first() {
15620                    if let Some(oldest_selection) =
15621                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15622                    {
15623                        let start_col = oldest_selection.start.column;
15624                        let end_col = oldest_selection.end.column;
15625                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15626                        for id in &group.stack {
15627                            map.insert(*id, goal_columns.clone());
15628                        }
15629                    }
15630                }
15631            }
15632            map
15633        } else {
15634            HashMap::default()
15635        };
15636
15637        let mut last_added_item_per_group = HashMap::default();
15638        for group in state.groups.iter_mut() {
15639            if let Some(last_id) = group.stack.last() {
15640                last_added_item_per_group.insert(*last_id, group);
15641            }
15642        }
15643
15644        for selection in columnar_selections {
15645            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15646                if above == group.above {
15647                    let range = selection.display_range(&display_map).sorted();
15648                    debug_assert_eq!(range.start.row(), range.end.row());
15649                    let row = range.start.row();
15650                    let positions =
15651                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15652                            Pixels::from(start)..Pixels::from(end)
15653                        } else {
15654                            let start_x =
15655                                display_map.x_for_display_point(range.start, &text_layout_details);
15656                            let end_x =
15657                                display_map.x_for_display_point(range.end, &text_layout_details);
15658                            start_x.min(end_x)..start_x.max(end_x)
15659                        };
15660
15661                    let maybe_new_selection = if skip_soft_wrap {
15662                        let goal_columns = goal_columns_by_selection_id
15663                            .remove(&selection.id)
15664                            .unwrap_or_else(|| {
15665                                let start_col = selection.start.column;
15666                                let end_col = selection.end.column;
15667                                start_col.min(end_col)..start_col.max(end_col)
15668                            });
15669                        self.selections.find_next_columnar_selection_by_buffer_row(
15670                            &display_map,
15671                            row,
15672                            end_row,
15673                            above,
15674                            &goal_columns,
15675                            selection.reversed,
15676                            &text_layout_details,
15677                        )
15678                    } else {
15679                        self.selections.find_next_columnar_selection_by_display_row(
15680                            &display_map,
15681                            row,
15682                            end_row,
15683                            above,
15684                            &positions,
15685                            selection.reversed,
15686                            &text_layout_details,
15687                        )
15688                    };
15689
15690                    if let Some(new_selection) = maybe_new_selection {
15691                        group.stack.push(new_selection.id);
15692                        if above {
15693                            final_selections.push(new_selection);
15694                            final_selections.push(selection);
15695                        } else {
15696                            final_selections.push(selection);
15697                            final_selections.push(new_selection);
15698                        }
15699                    } else {
15700                        final_selections.push(selection);
15701                    }
15702                } else {
15703                    group.stack.pop();
15704                }
15705            } else {
15706                final_selections.push(selection);
15707            }
15708        }
15709
15710        self.change_selections(Default::default(), window, cx, |s| {
15711            s.select(final_selections);
15712        });
15713
15714        let final_selection_ids: HashSet<_> = self
15715            .selections
15716            .all::<Point>(&display_map)
15717            .iter()
15718            .map(|s| s.id)
15719            .collect();
15720        state.groups.retain_mut(|group| {
15721            // selections might get merged above so we remove invalid items from stacks
15722            group.stack.retain(|id| final_selection_ids.contains(id));
15723
15724            // single selection in stack can be treated as initial state
15725            group.stack.len() > 1
15726        });
15727
15728        if !state.groups.is_empty() {
15729            self.add_selections_state = Some(state);
15730        }
15731    }
15732
15733    pub fn insert_snippet_at_selections(
15734        &mut self,
15735        action: &InsertSnippet,
15736        window: &mut Window,
15737        cx: &mut Context<Self>,
15738    ) {
15739        self.try_insert_snippet_at_selections(action, window, cx)
15740            .log_err();
15741    }
15742
15743    fn try_insert_snippet_at_selections(
15744        &mut self,
15745        action: &InsertSnippet,
15746        window: &mut Window,
15747        cx: &mut Context<Self>,
15748    ) -> Result<()> {
15749        let insertion_ranges = self
15750            .selections
15751            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15752            .into_iter()
15753            .map(|selection| selection.range())
15754            .collect_vec();
15755
15756        let snippet = if let Some(snippet_body) = &action.snippet {
15757            if action.language.is_none() && action.name.is_none() {
15758                Snippet::parse(snippet_body)?
15759            } else {
15760                bail!("`snippet` is mutually exclusive with `language` and `name`")
15761            }
15762        } else if let Some(name) = &action.name {
15763            let project = self.project().context("no project")?;
15764            let snippet_store = project.read(cx).snippets().read(cx);
15765            let snippet = snippet_store
15766                .snippets_for(action.language.clone(), cx)
15767                .into_iter()
15768                .find(|snippet| snippet.name == *name)
15769                .context("snippet not found")?;
15770            Snippet::parse(&snippet.body)?
15771        } else {
15772            // todo(andrew): open modal to select snippet
15773            bail!("`name` or `snippet` is required")
15774        };
15775
15776        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15777    }
15778
15779    fn select_match_ranges(
15780        &mut self,
15781        range: Range<MultiBufferOffset>,
15782        reversed: bool,
15783        replace_newest: bool,
15784        auto_scroll: Option<Autoscroll>,
15785        window: &mut Window,
15786        cx: &mut Context<Editor>,
15787    ) {
15788        self.unfold_ranges(
15789            std::slice::from_ref(&range),
15790            false,
15791            auto_scroll.is_some(),
15792            cx,
15793        );
15794        let effects = if let Some(scroll) = auto_scroll {
15795            SelectionEffects::scroll(scroll)
15796        } else {
15797            SelectionEffects::no_scroll()
15798        };
15799        self.change_selections(effects, window, cx, |s| {
15800            if replace_newest {
15801                s.delete(s.newest_anchor().id);
15802            }
15803            if reversed {
15804                s.insert_range(range.end..range.start);
15805            } else {
15806                s.insert_range(range);
15807            }
15808        });
15809    }
15810
15811    pub fn select_next_match_internal(
15812        &mut self,
15813        display_map: &DisplaySnapshot,
15814        replace_newest: bool,
15815        autoscroll: Option<Autoscroll>,
15816        window: &mut Window,
15817        cx: &mut Context<Self>,
15818    ) -> Result<()> {
15819        let buffer = display_map.buffer_snapshot();
15820        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15821        if let Some(mut select_next_state) = self.select_next_state.take() {
15822            let query = &select_next_state.query;
15823            if !select_next_state.done {
15824                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15825                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15826                let mut next_selected_range = None;
15827
15828                let bytes_after_last_selection =
15829                    buffer.bytes_in_range(last_selection.end..buffer.len());
15830                let bytes_before_first_selection =
15831                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15832                let query_matches = query
15833                    .stream_find_iter(bytes_after_last_selection)
15834                    .map(|result| (last_selection.end, result))
15835                    .chain(
15836                        query
15837                            .stream_find_iter(bytes_before_first_selection)
15838                            .map(|result| (MultiBufferOffset(0), result)),
15839                    );
15840
15841                for (start_offset, query_match) in query_matches {
15842                    let query_match = query_match.unwrap(); // can only fail due to I/O
15843                    let offset_range =
15844                        start_offset + query_match.start()..start_offset + query_match.end();
15845
15846                    if !select_next_state.wordwise
15847                        || (!buffer.is_inside_word(offset_range.start, None)
15848                            && !buffer.is_inside_word(offset_range.end, None))
15849                    {
15850                        let idx = selections
15851                            .partition_point(|selection| selection.end <= offset_range.start);
15852                        let overlaps = selections
15853                            .get(idx)
15854                            .map_or(false, |selection| selection.start < offset_range.end);
15855
15856                        if !overlaps {
15857                            next_selected_range = Some(offset_range);
15858                            break;
15859                        }
15860                    }
15861                }
15862
15863                if let Some(next_selected_range) = next_selected_range {
15864                    self.select_match_ranges(
15865                        next_selected_range,
15866                        last_selection.reversed,
15867                        replace_newest,
15868                        autoscroll,
15869                        window,
15870                        cx,
15871                    );
15872                } else {
15873                    select_next_state.done = true;
15874                }
15875            }
15876
15877            self.select_next_state = Some(select_next_state);
15878        } else {
15879            let mut only_carets = true;
15880            let mut same_text_selected = true;
15881            let mut selected_text = None;
15882
15883            let mut selections_iter = selections.iter().peekable();
15884            while let Some(selection) = selections_iter.next() {
15885                if selection.start != selection.end {
15886                    only_carets = false;
15887                }
15888
15889                if same_text_selected {
15890                    if selected_text.is_none() {
15891                        selected_text =
15892                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15893                    }
15894
15895                    if let Some(next_selection) = selections_iter.peek() {
15896                        if next_selection.len() == selection.len() {
15897                            let next_selected_text = buffer
15898                                .text_for_range(next_selection.range())
15899                                .collect::<String>();
15900                            if Some(next_selected_text) != selected_text {
15901                                same_text_selected = false;
15902                                selected_text = None;
15903                            }
15904                        } else {
15905                            same_text_selected = false;
15906                            selected_text = None;
15907                        }
15908                    }
15909                }
15910            }
15911
15912            if only_carets {
15913                for selection in &mut selections {
15914                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15915                    selection.start = word_range.start;
15916                    selection.end = word_range.end;
15917                    selection.goal = SelectionGoal::None;
15918                    selection.reversed = false;
15919                    self.select_match_ranges(
15920                        selection.start..selection.end,
15921                        selection.reversed,
15922                        replace_newest,
15923                        autoscroll,
15924                        window,
15925                        cx,
15926                    );
15927                }
15928
15929                if selections.len() == 1 {
15930                    let selection = selections
15931                        .last()
15932                        .expect("ensured that there's only one selection");
15933                    let query = buffer
15934                        .text_for_range(selection.start..selection.end)
15935                        .collect::<String>();
15936                    let is_empty = query.is_empty();
15937                    let select_state = SelectNextState {
15938                        query: self.build_query(&[query], cx)?,
15939                        wordwise: true,
15940                        done: is_empty,
15941                    };
15942                    self.select_next_state = Some(select_state);
15943                } else {
15944                    self.select_next_state = None;
15945                }
15946            } else if let Some(selected_text) = selected_text {
15947                self.select_next_state = Some(SelectNextState {
15948                    query: self.build_query(&[selected_text], cx)?,
15949                    wordwise: false,
15950                    done: false,
15951                });
15952                self.select_next_match_internal(
15953                    display_map,
15954                    replace_newest,
15955                    autoscroll,
15956                    window,
15957                    cx,
15958                )?;
15959            }
15960        }
15961        Ok(())
15962    }
15963
15964    pub fn select_all_matches(
15965        &mut self,
15966        _action: &SelectAllMatches,
15967        window: &mut Window,
15968        cx: &mut Context<Self>,
15969    ) -> Result<()> {
15970        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15971
15972        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15973
15974        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15975        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15976        else {
15977            return Ok(());
15978        };
15979
15980        let mut new_selections = Vec::new();
15981
15982        let reversed = self
15983            .selections
15984            .oldest::<MultiBufferOffset>(&display_map)
15985            .reversed;
15986        let buffer = display_map.buffer_snapshot();
15987        let query_matches = select_next_state
15988            .query
15989            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15990
15991        for query_match in query_matches.into_iter() {
15992            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15993            let offset_range = if reversed {
15994                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15995            } else {
15996                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15997            };
15998
15999            if !select_next_state.wordwise
16000                || (!buffer.is_inside_word(offset_range.start, None)
16001                    && !buffer.is_inside_word(offset_range.end, None))
16002            {
16003                new_selections.push(offset_range.start..offset_range.end);
16004            }
16005        }
16006
16007        select_next_state.done = true;
16008
16009        if new_selections.is_empty() {
16010            log::error!("bug: new_selections is empty in select_all_matches");
16011            return Ok(());
16012        }
16013
16014        self.unfold_ranges(&new_selections, false, false, cx);
16015        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16016            selections.select_ranges(new_selections)
16017        });
16018
16019        Ok(())
16020    }
16021
16022    pub fn select_next(
16023        &mut self,
16024        action: &SelectNext,
16025        window: &mut Window,
16026        cx: &mut Context<Self>,
16027    ) -> Result<()> {
16028        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16029        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16030        self.select_next_match_internal(
16031            &display_map,
16032            action.replace_newest,
16033            Some(Autoscroll::newest()),
16034            window,
16035            cx,
16036        )
16037    }
16038
16039    pub fn select_previous(
16040        &mut self,
16041        action: &SelectPrevious,
16042        window: &mut Window,
16043        cx: &mut Context<Self>,
16044    ) -> Result<()> {
16045        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16046        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16047        let buffer = display_map.buffer_snapshot();
16048        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16049        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16050            let query = &select_prev_state.query;
16051            if !select_prev_state.done {
16052                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16053                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16054                let mut next_selected_range = None;
16055                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16056                let bytes_before_last_selection =
16057                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16058                let bytes_after_first_selection =
16059                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16060                let query_matches = query
16061                    .stream_find_iter(bytes_before_last_selection)
16062                    .map(|result| (last_selection.start, result))
16063                    .chain(
16064                        query
16065                            .stream_find_iter(bytes_after_first_selection)
16066                            .map(|result| (buffer.len(), result)),
16067                    );
16068                for (end_offset, query_match) in query_matches {
16069                    let query_match = query_match.unwrap(); // can only fail due to I/O
16070                    let offset_range =
16071                        end_offset - query_match.end()..end_offset - query_match.start();
16072
16073                    if !select_prev_state.wordwise
16074                        || (!buffer.is_inside_word(offset_range.start, None)
16075                            && !buffer.is_inside_word(offset_range.end, None))
16076                    {
16077                        next_selected_range = Some(offset_range);
16078                        break;
16079                    }
16080                }
16081
16082                if let Some(next_selected_range) = next_selected_range {
16083                    self.select_match_ranges(
16084                        next_selected_range,
16085                        last_selection.reversed,
16086                        action.replace_newest,
16087                        Some(Autoscroll::newest()),
16088                        window,
16089                        cx,
16090                    );
16091                } else {
16092                    select_prev_state.done = true;
16093                }
16094            }
16095
16096            self.select_prev_state = Some(select_prev_state);
16097        } else {
16098            let mut only_carets = true;
16099            let mut same_text_selected = true;
16100            let mut selected_text = None;
16101
16102            let mut selections_iter = selections.iter().peekable();
16103            while let Some(selection) = selections_iter.next() {
16104                if selection.start != selection.end {
16105                    only_carets = false;
16106                }
16107
16108                if same_text_selected {
16109                    if selected_text.is_none() {
16110                        selected_text =
16111                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16112                    }
16113
16114                    if let Some(next_selection) = selections_iter.peek() {
16115                        if next_selection.len() == selection.len() {
16116                            let next_selected_text = buffer
16117                                .text_for_range(next_selection.range())
16118                                .collect::<String>();
16119                            if Some(next_selected_text) != selected_text {
16120                                same_text_selected = false;
16121                                selected_text = None;
16122                            }
16123                        } else {
16124                            same_text_selected = false;
16125                            selected_text = None;
16126                        }
16127                    }
16128                }
16129            }
16130
16131            if only_carets {
16132                for selection in &mut selections {
16133                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16134                    selection.start = word_range.start;
16135                    selection.end = word_range.end;
16136                    selection.goal = SelectionGoal::None;
16137                    selection.reversed = false;
16138                    self.select_match_ranges(
16139                        selection.start..selection.end,
16140                        selection.reversed,
16141                        action.replace_newest,
16142                        Some(Autoscroll::newest()),
16143                        window,
16144                        cx,
16145                    );
16146                }
16147                if selections.len() == 1 {
16148                    let selection = selections
16149                        .last()
16150                        .expect("ensured that there's only one selection");
16151                    let query = buffer
16152                        .text_for_range(selection.start..selection.end)
16153                        .collect::<String>();
16154                    let is_empty = query.is_empty();
16155                    let select_state = SelectNextState {
16156                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16157                        wordwise: true,
16158                        done: is_empty,
16159                    };
16160                    self.select_prev_state = Some(select_state);
16161                } else {
16162                    self.select_prev_state = None;
16163                }
16164            } else if let Some(selected_text) = selected_text {
16165                self.select_prev_state = Some(SelectNextState {
16166                    query: self
16167                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16168                    wordwise: false,
16169                    done: false,
16170                });
16171                self.select_previous(action, window, cx)?;
16172            }
16173        }
16174        Ok(())
16175    }
16176
16177    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16178    /// setting the case sensitivity based on the global
16179    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16180    /// editor's settings.
16181    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16182    where
16183        I: IntoIterator<Item = P>,
16184        P: AsRef<[u8]>,
16185    {
16186        let case_sensitive = self
16187            .select_next_is_case_sensitive
16188            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16189
16190        let mut builder = AhoCorasickBuilder::new();
16191        builder.ascii_case_insensitive(!case_sensitive);
16192        builder.build(patterns)
16193    }
16194
16195    pub fn find_next_match(
16196        &mut self,
16197        _: &FindNextMatch,
16198        window: &mut Window,
16199        cx: &mut Context<Self>,
16200    ) -> Result<()> {
16201        let selections = self.selections.disjoint_anchors_arc();
16202        match selections.first() {
16203            Some(first) if selections.len() >= 2 => {
16204                self.change_selections(Default::default(), window, cx, |s| {
16205                    s.select_ranges([first.range()]);
16206                });
16207            }
16208            _ => self.select_next(
16209                &SelectNext {
16210                    replace_newest: true,
16211                },
16212                window,
16213                cx,
16214            )?,
16215        }
16216        Ok(())
16217    }
16218
16219    pub fn find_previous_match(
16220        &mut self,
16221        _: &FindPreviousMatch,
16222        window: &mut Window,
16223        cx: &mut Context<Self>,
16224    ) -> Result<()> {
16225        let selections = self.selections.disjoint_anchors_arc();
16226        match selections.last() {
16227            Some(last) if selections.len() >= 2 => {
16228                self.change_selections(Default::default(), window, cx, |s| {
16229                    s.select_ranges([last.range()]);
16230                });
16231            }
16232            _ => self.select_previous(
16233                &SelectPrevious {
16234                    replace_newest: true,
16235                },
16236                window,
16237                cx,
16238            )?,
16239        }
16240        Ok(())
16241    }
16242
16243    pub fn toggle_comments(
16244        &mut self,
16245        action: &ToggleComments,
16246        window: &mut Window,
16247        cx: &mut Context<Self>,
16248    ) {
16249        if self.read_only(cx) {
16250            return;
16251        }
16252        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16253        let text_layout_details = &self.text_layout_details(window, cx);
16254        self.transact(window, cx, |this, window, cx| {
16255            let mut selections = this
16256                .selections
16257                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16258            let mut edits = Vec::new();
16259            let mut selection_edit_ranges = Vec::new();
16260            let mut last_toggled_row = None;
16261            let snapshot = this.buffer.read(cx).read(cx);
16262            let empty_str: Arc<str> = Arc::default();
16263            let mut suffixes_inserted = Vec::new();
16264            let ignore_indent = action.ignore_indent;
16265
16266            fn comment_prefix_range(
16267                snapshot: &MultiBufferSnapshot,
16268                row: MultiBufferRow,
16269                comment_prefix: &str,
16270                comment_prefix_whitespace: &str,
16271                ignore_indent: bool,
16272            ) -> Range<Point> {
16273                let indent_size = if ignore_indent {
16274                    0
16275                } else {
16276                    snapshot.indent_size_for_line(row).len
16277                };
16278
16279                let start = Point::new(row.0, indent_size);
16280
16281                let mut line_bytes = snapshot
16282                    .bytes_in_range(start..snapshot.max_point())
16283                    .flatten()
16284                    .copied();
16285
16286                // If this line currently begins with the line comment prefix, then record
16287                // the range containing the prefix.
16288                if line_bytes
16289                    .by_ref()
16290                    .take(comment_prefix.len())
16291                    .eq(comment_prefix.bytes())
16292                {
16293                    // Include any whitespace that matches the comment prefix.
16294                    let matching_whitespace_len = line_bytes
16295                        .zip(comment_prefix_whitespace.bytes())
16296                        .take_while(|(a, b)| a == b)
16297                        .count() as u32;
16298                    let end = Point::new(
16299                        start.row,
16300                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16301                    );
16302                    start..end
16303                } else {
16304                    start..start
16305                }
16306            }
16307
16308            fn comment_suffix_range(
16309                snapshot: &MultiBufferSnapshot,
16310                row: MultiBufferRow,
16311                comment_suffix: &str,
16312                comment_suffix_has_leading_space: bool,
16313            ) -> Range<Point> {
16314                let end = Point::new(row.0, snapshot.line_len(row));
16315                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16316
16317                let mut line_end_bytes = snapshot
16318                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16319                    .flatten()
16320                    .copied();
16321
16322                let leading_space_len = if suffix_start_column > 0
16323                    && line_end_bytes.next() == Some(b' ')
16324                    && comment_suffix_has_leading_space
16325                {
16326                    1
16327                } else {
16328                    0
16329                };
16330
16331                // If this line currently begins with the line comment prefix, then record
16332                // the range containing the prefix.
16333                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16334                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16335                    start..end
16336                } else {
16337                    end..end
16338                }
16339            }
16340
16341            // TODO: Handle selections that cross excerpts
16342            for selection in &mut selections {
16343                let start_column = snapshot
16344                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16345                    .len;
16346                let language = if let Some(language) =
16347                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16348                {
16349                    language
16350                } else {
16351                    continue;
16352                };
16353
16354                selection_edit_ranges.clear();
16355
16356                // If multiple selections contain a given row, avoid processing that
16357                // row more than once.
16358                let mut start_row = MultiBufferRow(selection.start.row);
16359                if last_toggled_row == Some(start_row) {
16360                    start_row = start_row.next_row();
16361                }
16362                let end_row =
16363                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16364                        MultiBufferRow(selection.end.row - 1)
16365                    } else {
16366                        MultiBufferRow(selection.end.row)
16367                    };
16368                last_toggled_row = Some(end_row);
16369
16370                if start_row > end_row {
16371                    continue;
16372                }
16373
16374                // If the language has line comments, toggle those.
16375                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16376
16377                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16378                if ignore_indent {
16379                    full_comment_prefixes = full_comment_prefixes
16380                        .into_iter()
16381                        .map(|s| Arc::from(s.trim_end()))
16382                        .collect();
16383                }
16384
16385                if !full_comment_prefixes.is_empty() {
16386                    let first_prefix = full_comment_prefixes
16387                        .first()
16388                        .expect("prefixes is non-empty");
16389                    let prefix_trimmed_lengths = full_comment_prefixes
16390                        .iter()
16391                        .map(|p| p.trim_end_matches(' ').len())
16392                        .collect::<SmallVec<[usize; 4]>>();
16393
16394                    let mut all_selection_lines_are_comments = true;
16395
16396                    for row in start_row.0..=end_row.0 {
16397                        let row = MultiBufferRow(row);
16398                        if start_row < end_row && snapshot.is_line_blank(row) {
16399                            continue;
16400                        }
16401
16402                        let prefix_range = full_comment_prefixes
16403                            .iter()
16404                            .zip(prefix_trimmed_lengths.iter().copied())
16405                            .map(|(prefix, trimmed_prefix_len)| {
16406                                comment_prefix_range(
16407                                    snapshot.deref(),
16408                                    row,
16409                                    &prefix[..trimmed_prefix_len],
16410                                    &prefix[trimmed_prefix_len..],
16411                                    ignore_indent,
16412                                )
16413                            })
16414                            .max_by_key(|range| range.end.column - range.start.column)
16415                            .expect("prefixes is non-empty");
16416
16417                        if prefix_range.is_empty() {
16418                            all_selection_lines_are_comments = false;
16419                        }
16420
16421                        selection_edit_ranges.push(prefix_range);
16422                    }
16423
16424                    if all_selection_lines_are_comments {
16425                        edits.extend(
16426                            selection_edit_ranges
16427                                .iter()
16428                                .cloned()
16429                                .map(|range| (range, empty_str.clone())),
16430                        );
16431                    } else {
16432                        let min_column = selection_edit_ranges
16433                            .iter()
16434                            .map(|range| range.start.column)
16435                            .min()
16436                            .unwrap_or(0);
16437                        edits.extend(selection_edit_ranges.iter().map(|range| {
16438                            let position = Point::new(range.start.row, min_column);
16439                            (position..position, first_prefix.clone())
16440                        }));
16441                    }
16442                } else if let Some(BlockCommentConfig {
16443                    start: full_comment_prefix,
16444                    end: comment_suffix,
16445                    ..
16446                }) = language.block_comment()
16447                {
16448                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16449                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16450                    let prefix_range = comment_prefix_range(
16451                        snapshot.deref(),
16452                        start_row,
16453                        comment_prefix,
16454                        comment_prefix_whitespace,
16455                        ignore_indent,
16456                    );
16457                    let suffix_range = comment_suffix_range(
16458                        snapshot.deref(),
16459                        end_row,
16460                        comment_suffix.trim_start_matches(' '),
16461                        comment_suffix.starts_with(' '),
16462                    );
16463
16464                    if prefix_range.is_empty() || suffix_range.is_empty() {
16465                        edits.push((
16466                            prefix_range.start..prefix_range.start,
16467                            full_comment_prefix.clone(),
16468                        ));
16469                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16470                        suffixes_inserted.push((end_row, comment_suffix.len()));
16471                    } else {
16472                        edits.push((prefix_range, empty_str.clone()));
16473                        edits.push((suffix_range, empty_str.clone()));
16474                    }
16475                } else {
16476                    continue;
16477                }
16478            }
16479
16480            drop(snapshot);
16481            this.buffer.update(cx, |buffer, cx| {
16482                buffer.edit(edits, None, cx);
16483            });
16484
16485            // Adjust selections so that they end before any comment suffixes that
16486            // were inserted.
16487            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16488            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16489            let snapshot = this.buffer.read(cx).read(cx);
16490            for selection in &mut selections {
16491                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16492                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16493                        Ordering::Less => {
16494                            suffixes_inserted.next();
16495                            continue;
16496                        }
16497                        Ordering::Greater => break,
16498                        Ordering::Equal => {
16499                            if selection.end.column == snapshot.line_len(row) {
16500                                if selection.is_empty() {
16501                                    selection.start.column -= suffix_len as u32;
16502                                }
16503                                selection.end.column -= suffix_len as u32;
16504                            }
16505                            break;
16506                        }
16507                    }
16508                }
16509            }
16510
16511            drop(snapshot);
16512            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16513
16514            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16515            let selections_on_single_row = selections.windows(2).all(|selections| {
16516                selections[0].start.row == selections[1].start.row
16517                    && selections[0].end.row == selections[1].end.row
16518                    && selections[0].start.row == selections[0].end.row
16519            });
16520            let selections_selecting = selections
16521                .iter()
16522                .any(|selection| selection.start != selection.end);
16523            let advance_downwards = action.advance_downwards
16524                && selections_on_single_row
16525                && !selections_selecting
16526                && !matches!(this.mode, EditorMode::SingleLine);
16527
16528            if advance_downwards {
16529                let snapshot = this.buffer.read(cx).snapshot(cx);
16530
16531                this.change_selections(Default::default(), window, cx, |s| {
16532                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16533                        let mut point = display_point.to_point(display_snapshot);
16534                        point.row += 1;
16535                        point = snapshot.clip_point(point, Bias::Left);
16536                        let display_point = point.to_display_point(display_snapshot);
16537                        let goal = SelectionGoal::HorizontalPosition(
16538                            display_snapshot
16539                                .x_for_display_point(display_point, text_layout_details)
16540                                .into(),
16541                        );
16542                        (display_point, goal)
16543                    })
16544                });
16545            }
16546        });
16547    }
16548
16549    pub fn select_enclosing_symbol(
16550        &mut self,
16551        _: &SelectEnclosingSymbol,
16552        window: &mut Window,
16553        cx: &mut Context<Self>,
16554    ) {
16555        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16556
16557        let buffer = self.buffer.read(cx).snapshot(cx);
16558        let old_selections = self
16559            .selections
16560            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16561            .into_boxed_slice();
16562
16563        fn update_selection(
16564            selection: &Selection<MultiBufferOffset>,
16565            buffer_snap: &MultiBufferSnapshot,
16566        ) -> Option<Selection<MultiBufferOffset>> {
16567            let cursor = selection.head();
16568            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16569            for symbol in symbols.iter().rev() {
16570                let start = symbol.range.start.to_offset(buffer_snap);
16571                let end = symbol.range.end.to_offset(buffer_snap);
16572                let new_range = start..end;
16573                if start < selection.start || end > selection.end {
16574                    return Some(Selection {
16575                        id: selection.id,
16576                        start: new_range.start,
16577                        end: new_range.end,
16578                        goal: SelectionGoal::None,
16579                        reversed: selection.reversed,
16580                    });
16581                }
16582            }
16583            None
16584        }
16585
16586        let mut selected_larger_symbol = false;
16587        let new_selections = old_selections
16588            .iter()
16589            .map(|selection| match update_selection(selection, &buffer) {
16590                Some(new_selection) => {
16591                    if new_selection.range() != selection.range() {
16592                        selected_larger_symbol = true;
16593                    }
16594                    new_selection
16595                }
16596                None => selection.clone(),
16597            })
16598            .collect::<Vec<_>>();
16599
16600        if selected_larger_symbol {
16601            self.change_selections(Default::default(), window, cx, |s| {
16602                s.select(new_selections);
16603            });
16604        }
16605    }
16606
16607    pub fn select_larger_syntax_node(
16608        &mut self,
16609        _: &SelectLargerSyntaxNode,
16610        window: &mut Window,
16611        cx: &mut Context<Self>,
16612    ) {
16613        let Some(visible_row_count) = self.visible_row_count() else {
16614            return;
16615        };
16616        let old_selections: Box<[_]> = self
16617            .selections
16618            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16619            .into();
16620        if old_selections.is_empty() {
16621            return;
16622        }
16623
16624        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16625
16626        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16627        let buffer = self.buffer.read(cx).snapshot(cx);
16628
16629        let mut selected_larger_node = false;
16630        let mut new_selections = old_selections
16631            .iter()
16632            .map(|selection| {
16633                let old_range = selection.start..selection.end;
16634
16635                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16636                    // manually select word at selection
16637                    if ["string_content", "inline"].contains(&node.kind()) {
16638                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16639                        // ignore if word is already selected
16640                        if !word_range.is_empty() && old_range != word_range {
16641                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16642                            // only select word if start and end point belongs to same word
16643                            if word_range == last_word_range {
16644                                selected_larger_node = true;
16645                                return Selection {
16646                                    id: selection.id,
16647                                    start: word_range.start,
16648                                    end: word_range.end,
16649                                    goal: SelectionGoal::None,
16650                                    reversed: selection.reversed,
16651                                };
16652                            }
16653                        }
16654                    }
16655                }
16656
16657                let mut new_range = old_range.clone();
16658                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16659                    new_range = range;
16660                    if !node.is_named() {
16661                        continue;
16662                    }
16663                    if !display_map.intersects_fold(new_range.start)
16664                        && !display_map.intersects_fold(new_range.end)
16665                    {
16666                        break;
16667                    }
16668                }
16669
16670                selected_larger_node |= new_range != old_range;
16671                Selection {
16672                    id: selection.id,
16673                    start: new_range.start,
16674                    end: new_range.end,
16675                    goal: SelectionGoal::None,
16676                    reversed: selection.reversed,
16677                }
16678            })
16679            .collect::<Vec<_>>();
16680
16681        if !selected_larger_node {
16682            return; // don't put this call in the history
16683        }
16684
16685        // scroll based on transformation done to the last selection created by the user
16686        let (last_old, last_new) = old_selections
16687            .last()
16688            .zip(new_selections.last().cloned())
16689            .expect("old_selections isn't empty");
16690
16691        // revert selection
16692        let is_selection_reversed = {
16693            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16694            new_selections.last_mut().expect("checked above").reversed =
16695                should_newest_selection_be_reversed;
16696            should_newest_selection_be_reversed
16697        };
16698
16699        if selected_larger_node {
16700            self.select_syntax_node_history.disable_clearing = true;
16701            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16702                s.select(new_selections.clone());
16703            });
16704            self.select_syntax_node_history.disable_clearing = false;
16705        }
16706
16707        let start_row = last_new.start.to_display_point(&display_map).row().0;
16708        let end_row = last_new.end.to_display_point(&display_map).row().0;
16709        let selection_height = end_row - start_row + 1;
16710        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16711
16712        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16713        let scroll_behavior = if fits_on_the_screen {
16714            self.request_autoscroll(Autoscroll::fit(), cx);
16715            SelectSyntaxNodeScrollBehavior::FitSelection
16716        } else if is_selection_reversed {
16717            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16718            SelectSyntaxNodeScrollBehavior::CursorTop
16719        } else {
16720            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16721            SelectSyntaxNodeScrollBehavior::CursorBottom
16722        };
16723
16724        let old_selections: Box<[Selection<Anchor>]> = old_selections
16725            .iter()
16726            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16727            .collect();
16728        self.select_syntax_node_history.push((
16729            old_selections,
16730            scroll_behavior,
16731            is_selection_reversed,
16732        ));
16733    }
16734
16735    pub fn select_smaller_syntax_node(
16736        &mut self,
16737        _: &SelectSmallerSyntaxNode,
16738        window: &mut Window,
16739        cx: &mut Context<Self>,
16740    ) {
16741        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16742
16743        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16744            self.select_syntax_node_history.pop()
16745        {
16746            if let Some(selection) = selections.last_mut() {
16747                selection.reversed = is_selection_reversed;
16748            }
16749
16750            let snapshot = self.buffer.read(cx).snapshot(cx);
16751            let selections: Vec<Selection<MultiBufferOffset>> = selections
16752                .iter()
16753                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16754                .collect();
16755
16756            self.select_syntax_node_history.disable_clearing = true;
16757            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16758                s.select(selections);
16759            });
16760            self.select_syntax_node_history.disable_clearing = false;
16761
16762            match scroll_behavior {
16763                SelectSyntaxNodeScrollBehavior::CursorTop => {
16764                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16765                }
16766                SelectSyntaxNodeScrollBehavior::FitSelection => {
16767                    self.request_autoscroll(Autoscroll::fit(), cx);
16768                }
16769                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16770                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16771                }
16772            }
16773        }
16774    }
16775
16776    pub fn unwrap_syntax_node(
16777        &mut self,
16778        _: &UnwrapSyntaxNode,
16779        window: &mut Window,
16780        cx: &mut Context<Self>,
16781    ) {
16782        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16783
16784        let buffer = self.buffer.read(cx).snapshot(cx);
16785        let selections = self
16786            .selections
16787            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16788            .into_iter()
16789            // subtracting the offset requires sorting
16790            .sorted_by_key(|i| i.start);
16791
16792        let full_edits = selections
16793            .into_iter()
16794            .filter_map(|selection| {
16795                let child = if selection.is_empty()
16796                    && let Some((_, ancestor_range)) =
16797                        buffer.syntax_ancestor(selection.start..selection.end)
16798                {
16799                    ancestor_range
16800                } else {
16801                    selection.range()
16802                };
16803
16804                let mut parent = child.clone();
16805                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16806                    parent = ancestor_range;
16807                    if parent.start < child.start || parent.end > child.end {
16808                        break;
16809                    }
16810                }
16811
16812                if parent == child {
16813                    return None;
16814                }
16815                let text = buffer.text_for_range(child).collect::<String>();
16816                Some((selection.id, parent, text))
16817            })
16818            .collect::<Vec<_>>();
16819        if full_edits.is_empty() {
16820            return;
16821        }
16822
16823        self.transact(window, cx, |this, window, cx| {
16824            this.buffer.update(cx, |buffer, cx| {
16825                buffer.edit(
16826                    full_edits
16827                        .iter()
16828                        .map(|(_, p, t)| (p.clone(), t.clone()))
16829                        .collect::<Vec<_>>(),
16830                    None,
16831                    cx,
16832                );
16833            });
16834            this.change_selections(Default::default(), window, cx, |s| {
16835                let mut offset = 0;
16836                let mut selections = vec![];
16837                for (id, parent, text) in full_edits {
16838                    let start = parent.start - offset;
16839                    offset += (parent.end - parent.start) - text.len();
16840                    selections.push(Selection {
16841                        id,
16842                        start,
16843                        end: start + text.len(),
16844                        reversed: false,
16845                        goal: Default::default(),
16846                    });
16847                }
16848                s.select(selections);
16849            });
16850        });
16851    }
16852
16853    pub fn select_next_syntax_node(
16854        &mut self,
16855        _: &SelectNextSyntaxNode,
16856        window: &mut Window,
16857        cx: &mut Context<Self>,
16858    ) {
16859        let old_selections: Box<[_]> = self
16860            .selections
16861            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16862            .into();
16863        if old_selections.is_empty() {
16864            return;
16865        }
16866
16867        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16868
16869        let buffer = self.buffer.read(cx).snapshot(cx);
16870        let mut selected_sibling = false;
16871
16872        let new_selections = old_selections
16873            .iter()
16874            .map(|selection| {
16875                let old_range = selection.start..selection.end;
16876
16877                let old_range =
16878                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16879                let excerpt = buffer.excerpt_containing(old_range.clone());
16880
16881                if let Some(mut excerpt) = excerpt
16882                    && let Some(node) = excerpt
16883                        .buffer()
16884                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16885                {
16886                    let new_range = excerpt.map_range_from_buffer(
16887                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16888                    );
16889                    selected_sibling = true;
16890                    Selection {
16891                        id: selection.id,
16892                        start: new_range.start,
16893                        end: new_range.end,
16894                        goal: SelectionGoal::None,
16895                        reversed: selection.reversed,
16896                    }
16897                } else {
16898                    selection.clone()
16899                }
16900            })
16901            .collect::<Vec<_>>();
16902
16903        if selected_sibling {
16904            self.change_selections(
16905                SelectionEffects::scroll(Autoscroll::fit()),
16906                window,
16907                cx,
16908                |s| {
16909                    s.select(new_selections);
16910                },
16911            );
16912        }
16913    }
16914
16915    pub fn select_prev_syntax_node(
16916        &mut self,
16917        _: &SelectPreviousSyntaxNode,
16918        window: &mut Window,
16919        cx: &mut Context<Self>,
16920    ) {
16921        let old_selections: Box<[_]> = self
16922            .selections
16923            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16924            .into();
16925        if old_selections.is_empty() {
16926            return;
16927        }
16928
16929        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16930
16931        let buffer = self.buffer.read(cx).snapshot(cx);
16932        let mut selected_sibling = false;
16933
16934        let new_selections = old_selections
16935            .iter()
16936            .map(|selection| {
16937                let old_range = selection.start..selection.end;
16938                let old_range =
16939                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16940                let excerpt = buffer.excerpt_containing(old_range.clone());
16941
16942                if let Some(mut excerpt) = excerpt
16943                    && let Some(node) = excerpt
16944                        .buffer()
16945                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16946                {
16947                    let new_range = excerpt.map_range_from_buffer(
16948                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16949                    );
16950                    selected_sibling = true;
16951                    Selection {
16952                        id: selection.id,
16953                        start: new_range.start,
16954                        end: new_range.end,
16955                        goal: SelectionGoal::None,
16956                        reversed: selection.reversed,
16957                    }
16958                } else {
16959                    selection.clone()
16960                }
16961            })
16962            .collect::<Vec<_>>();
16963
16964        if selected_sibling {
16965            self.change_selections(
16966                SelectionEffects::scroll(Autoscroll::fit()),
16967                window,
16968                cx,
16969                |s| {
16970                    s.select(new_selections);
16971                },
16972            );
16973        }
16974    }
16975
16976    pub fn move_to_start_of_larger_syntax_node(
16977        &mut self,
16978        _: &MoveToStartOfLargerSyntaxNode,
16979        window: &mut Window,
16980        cx: &mut Context<Self>,
16981    ) {
16982        self.move_cursors_to_syntax_nodes(window, cx, false);
16983    }
16984
16985    pub fn move_to_end_of_larger_syntax_node(
16986        &mut self,
16987        _: &MoveToEndOfLargerSyntaxNode,
16988        window: &mut Window,
16989        cx: &mut Context<Self>,
16990    ) {
16991        self.move_cursors_to_syntax_nodes(window, cx, true);
16992    }
16993
16994    fn find_syntax_node_boundary(
16995        &self,
16996        selection_pos: MultiBufferOffset,
16997        move_to_end: bool,
16998        display_map: &DisplaySnapshot,
16999        buffer: &MultiBufferSnapshot,
17000    ) -> MultiBufferOffset {
17001        let old_range = selection_pos..selection_pos;
17002        let mut new_pos = selection_pos;
17003        let mut search_range = old_range;
17004        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17005            search_range = range.clone();
17006            if !node.is_named()
17007                || display_map.intersects_fold(range.start)
17008                || display_map.intersects_fold(range.end)
17009                // If cursor is already at the end of the syntax node, continue searching
17010                || (move_to_end && range.end == selection_pos)
17011                // If cursor is already at the start of the syntax node, continue searching
17012                || (!move_to_end && range.start == selection_pos)
17013            {
17014                continue;
17015            }
17016
17017            // If we found a string_content node, find the largest parent that is still string_content
17018            // Enables us to skip to the end of strings without taking multiple steps inside the string
17019            let (_, final_range) = if node.kind() == "string_content" {
17020                let mut current_node = node;
17021                let mut current_range = range;
17022                while let Some((parent, parent_range)) =
17023                    buffer.syntax_ancestor(current_range.clone())
17024                {
17025                    if parent.kind() == "string_content" {
17026                        current_node = parent;
17027                        current_range = parent_range;
17028                    } else {
17029                        break;
17030                    }
17031                }
17032
17033                (current_node, current_range)
17034            } else {
17035                (node, range)
17036            };
17037
17038            new_pos = if move_to_end {
17039                final_range.end
17040            } else {
17041                final_range.start
17042            };
17043
17044            break;
17045        }
17046
17047        new_pos
17048    }
17049
17050    fn move_cursors_to_syntax_nodes(
17051        &mut self,
17052        window: &mut Window,
17053        cx: &mut Context<Self>,
17054        move_to_end: bool,
17055    ) -> bool {
17056        let old_selections: Box<[_]> = self
17057            .selections
17058            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17059            .into();
17060        if old_selections.is_empty() {
17061            return false;
17062        }
17063
17064        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17065
17066        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17067        let buffer = self.buffer.read(cx).snapshot(cx);
17068
17069        let mut any_cursor_moved = false;
17070        let new_selections = old_selections
17071            .iter()
17072            .map(|selection| {
17073                if !selection.is_empty() {
17074                    return selection.clone();
17075                }
17076
17077                let selection_pos = selection.head();
17078                let new_pos = self.find_syntax_node_boundary(
17079                    selection_pos,
17080                    move_to_end,
17081                    &display_map,
17082                    &buffer,
17083                );
17084
17085                any_cursor_moved |= new_pos != selection_pos;
17086
17087                Selection {
17088                    id: selection.id,
17089                    start: new_pos,
17090                    end: new_pos,
17091                    goal: SelectionGoal::None,
17092                    reversed: false,
17093                }
17094            })
17095            .collect::<Vec<_>>();
17096
17097        self.change_selections(Default::default(), window, cx, |s| {
17098            s.select(new_selections);
17099        });
17100        self.request_autoscroll(Autoscroll::newest(), cx);
17101
17102        any_cursor_moved
17103    }
17104
17105    pub fn select_to_start_of_larger_syntax_node(
17106        &mut self,
17107        _: &SelectToStartOfLargerSyntaxNode,
17108        window: &mut Window,
17109        cx: &mut Context<Self>,
17110    ) {
17111        self.select_to_syntax_nodes(window, cx, false);
17112    }
17113
17114    pub fn select_to_end_of_larger_syntax_node(
17115        &mut self,
17116        _: &SelectToEndOfLargerSyntaxNode,
17117        window: &mut Window,
17118        cx: &mut Context<Self>,
17119    ) {
17120        self.select_to_syntax_nodes(window, cx, true);
17121    }
17122
17123    fn select_to_syntax_nodes(
17124        &mut self,
17125        window: &mut Window,
17126        cx: &mut Context<Self>,
17127        move_to_end: bool,
17128    ) {
17129        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17130
17131        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17132        let buffer = self.buffer.read(cx).snapshot(cx);
17133        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17134
17135        let new_selections = old_selections
17136            .iter()
17137            .map(|selection| {
17138                let new_pos = self.find_syntax_node_boundary(
17139                    selection.head(),
17140                    move_to_end,
17141                    &display_map,
17142                    &buffer,
17143                );
17144
17145                let mut new_selection = selection.clone();
17146                new_selection.set_head(new_pos, SelectionGoal::None);
17147                new_selection
17148            })
17149            .collect::<Vec<_>>();
17150
17151        self.change_selections(Default::default(), window, cx, |s| {
17152            s.select(new_selections);
17153        });
17154    }
17155
17156    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17157        if !self.mode().is_full()
17158            || !EditorSettings::get_global(cx).gutter.runnables
17159            || !self.enable_runnables
17160        {
17161            self.clear_tasks();
17162            return Task::ready(());
17163        }
17164        let project = self.project().map(Entity::downgrade);
17165        let task_sources = self.lsp_task_sources(cx);
17166        let multi_buffer = self.buffer.downgrade();
17167        let lsp_data_enabled = self.lsp_data_enabled();
17168        cx.spawn_in(window, async move |editor, cx| {
17169            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17170            let Some(project) = project.and_then(|p| p.upgrade()) else {
17171                return;
17172            };
17173            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17174                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17175            }) else {
17176                return;
17177            };
17178
17179            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17180            if hide_runnables {
17181                return;
17182            }
17183            let new_rows = cx
17184                .background_spawn({
17185                    let snapshot = display_snapshot.clone();
17186                    async move {
17187                        snapshot
17188                            .buffer_snapshot()
17189                            .runnable_ranges(Anchor::min()..Anchor::max())
17190                            .collect()
17191                    }
17192                })
17193                .await;
17194            let lsp_tasks = if lsp_data_enabled {
17195                let Ok(lsp_tasks) =
17196                    cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17197                else {
17198                    return;
17199                };
17200                lsp_tasks.await
17201            } else {
17202                Vec::new()
17203            };
17204
17205            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17206                lsp_tasks
17207                    .into_iter()
17208                    .flat_map(|(kind, tasks)| {
17209                        tasks.into_iter().filter_map(move |(location, task)| {
17210                            Some((kind.clone(), location?, task))
17211                        })
17212                    })
17213                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17214                        let buffer = location.target.buffer;
17215                        let buffer_snapshot = buffer.read(cx).snapshot();
17216                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17217                            |(excerpt_id, snapshot, _)| {
17218                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17219                                    display_snapshot
17220                                        .buffer_snapshot()
17221                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17222                                } else {
17223                                    None
17224                                }
17225                            },
17226                        );
17227                        if let Some(offset) = offset {
17228                            let task_buffer_range =
17229                                location.target.range.to_point(&buffer_snapshot);
17230                            let context_buffer_range =
17231                                task_buffer_range.to_offset(&buffer_snapshot);
17232                            let context_range = BufferOffset(context_buffer_range.start)
17233                                ..BufferOffset(context_buffer_range.end);
17234
17235                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17236                                .or_insert_with(|| RunnableTasks {
17237                                    templates: Vec::new(),
17238                                    offset,
17239                                    column: task_buffer_range.start.column,
17240                                    extra_variables: HashMap::default(),
17241                                    context_range,
17242                                })
17243                                .templates
17244                                .push((kind, task.original_task().clone()));
17245                        }
17246
17247                        acc
17248                    })
17249            }) else {
17250                return;
17251            };
17252
17253            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17254                buffer.language_settings(cx).tasks.prefer_lsp
17255            }) else {
17256                return;
17257            };
17258
17259            let rows = Self::runnable_rows(
17260                project,
17261                display_snapshot,
17262                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17263                new_rows,
17264                cx.clone(),
17265            )
17266            .await;
17267            editor
17268                .update(cx, |editor, _| {
17269                    editor.clear_tasks();
17270                    for (key, mut value) in rows {
17271                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17272                            value.templates.extend(lsp_tasks.templates);
17273                        }
17274
17275                        editor.insert_tasks(key, value);
17276                    }
17277                    for (key, value) in lsp_tasks_by_rows {
17278                        editor.insert_tasks(key, value);
17279                    }
17280                })
17281                .ok();
17282        })
17283    }
17284
17285    fn runnable_rows(
17286        project: Entity<Project>,
17287        snapshot: DisplaySnapshot,
17288        prefer_lsp: bool,
17289        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17290        cx: AsyncWindowContext,
17291    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17292        cx.spawn(async move |cx| {
17293            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17294            for (run_range, mut runnable) in runnable_ranges {
17295                let Some(tasks) = cx
17296                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17297                    .ok()
17298                else {
17299                    continue;
17300                };
17301                let mut tasks = tasks.await;
17302
17303                if prefer_lsp {
17304                    tasks.retain(|(task_kind, _)| {
17305                        !matches!(task_kind, TaskSourceKind::Language { .. })
17306                    });
17307                }
17308                if tasks.is_empty() {
17309                    continue;
17310                }
17311
17312                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17313                let Some(row) = snapshot
17314                    .buffer_snapshot()
17315                    .buffer_line_for_row(MultiBufferRow(point.row))
17316                    .map(|(_, range)| range.start.row)
17317                else {
17318                    continue;
17319                };
17320
17321                let context_range =
17322                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17323                runnable_rows.push((
17324                    (runnable.buffer_id, row),
17325                    RunnableTasks {
17326                        templates: tasks,
17327                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17328                        context_range,
17329                        column: point.column,
17330                        extra_variables: runnable.extra_captures,
17331                    },
17332                ));
17333            }
17334            runnable_rows
17335        })
17336    }
17337
17338    fn templates_with_tags(
17339        project: &Entity<Project>,
17340        runnable: &mut Runnable,
17341        cx: &mut App,
17342    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17343        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17344            let (worktree_id, file) = project
17345                .buffer_for_id(runnable.buffer, cx)
17346                .and_then(|buffer| buffer.read(cx).file())
17347                .map(|file| (file.worktree_id(cx), file.clone()))
17348                .unzip();
17349
17350            (
17351                project.task_store().read(cx).task_inventory().cloned(),
17352                worktree_id,
17353                file,
17354            )
17355        });
17356
17357        let tags = mem::take(&mut runnable.tags);
17358        let language = runnable.language.clone();
17359        cx.spawn(async move |cx| {
17360            let mut templates_with_tags = Vec::new();
17361            if let Some(inventory) = inventory {
17362                for RunnableTag(tag) in tags {
17363                    let new_tasks = inventory.update(cx, |inventory, cx| {
17364                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17365                    });
17366                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17367                        move |(_, template)| {
17368                            template.tags.iter().any(|source_tag| source_tag == &tag)
17369                        },
17370                    ));
17371                }
17372            }
17373            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17374
17375            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17376                // Strongest source wins; if we have worktree tag binding, prefer that to
17377                // global and language bindings;
17378                // if we have a global binding, prefer that to language binding.
17379                let first_mismatch = templates_with_tags
17380                    .iter()
17381                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17382                if let Some(index) = first_mismatch {
17383                    templates_with_tags.truncate(index);
17384                }
17385            }
17386
17387            templates_with_tags
17388        })
17389    }
17390
17391    pub fn move_to_enclosing_bracket(
17392        &mut self,
17393        _: &MoveToEnclosingBracket,
17394        window: &mut Window,
17395        cx: &mut Context<Self>,
17396    ) {
17397        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17398        self.change_selections(Default::default(), window, cx, |s| {
17399            s.move_offsets_with(&mut |snapshot, selection| {
17400                let Some(enclosing_bracket_ranges) =
17401                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17402                else {
17403                    return;
17404                };
17405
17406                let mut best_length = usize::MAX;
17407                let mut best_inside = false;
17408                let mut best_in_bracket_range = false;
17409                let mut best_destination = None;
17410                for (open, close) in enclosing_bracket_ranges {
17411                    let close = close.to_inclusive();
17412                    let length = *close.end() - open.start;
17413                    let inside = selection.start >= open.end && selection.end <= *close.start();
17414                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17415                        || close.contains(&selection.head());
17416
17417                    // If best is next to a bracket and current isn't, skip
17418                    if !in_bracket_range && best_in_bracket_range {
17419                        continue;
17420                    }
17421
17422                    // Prefer smaller lengths unless best is inside and current isn't
17423                    if length > best_length && (best_inside || !inside) {
17424                        continue;
17425                    }
17426
17427                    best_length = length;
17428                    best_inside = inside;
17429                    best_in_bracket_range = in_bracket_range;
17430                    best_destination = Some(
17431                        if close.contains(&selection.start) && close.contains(&selection.end) {
17432                            if inside { open.end } else { open.start }
17433                        } else if inside {
17434                            *close.start()
17435                        } else {
17436                            *close.end()
17437                        },
17438                    );
17439                }
17440
17441                if let Some(destination) = best_destination {
17442                    selection.collapse_to(destination, SelectionGoal::None);
17443                }
17444            })
17445        });
17446    }
17447
17448    pub fn undo_selection(
17449        &mut self,
17450        _: &UndoSelection,
17451        window: &mut Window,
17452        cx: &mut Context<Self>,
17453    ) {
17454        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17455        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17456            self.selection_history.mode = SelectionHistoryMode::Undoing;
17457            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17458                this.end_selection(window, cx);
17459                this.change_selections(
17460                    SelectionEffects::scroll(Autoscroll::newest()),
17461                    window,
17462                    cx,
17463                    |s| s.select_anchors(entry.selections.to_vec()),
17464                );
17465            });
17466            self.selection_history.mode = SelectionHistoryMode::Normal;
17467
17468            self.select_next_state = entry.select_next_state;
17469            self.select_prev_state = entry.select_prev_state;
17470            self.add_selections_state = entry.add_selections_state;
17471        }
17472    }
17473
17474    pub fn redo_selection(
17475        &mut self,
17476        _: &RedoSelection,
17477        window: &mut Window,
17478        cx: &mut Context<Self>,
17479    ) {
17480        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17481        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17482            self.selection_history.mode = SelectionHistoryMode::Redoing;
17483            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17484                this.end_selection(window, cx);
17485                this.change_selections(
17486                    SelectionEffects::scroll(Autoscroll::newest()),
17487                    window,
17488                    cx,
17489                    |s| s.select_anchors(entry.selections.to_vec()),
17490                );
17491            });
17492            self.selection_history.mode = SelectionHistoryMode::Normal;
17493
17494            self.select_next_state = entry.select_next_state;
17495            self.select_prev_state = entry.select_prev_state;
17496            self.add_selections_state = entry.add_selections_state;
17497        }
17498    }
17499
17500    pub fn expand_excerpts(
17501        &mut self,
17502        action: &ExpandExcerpts,
17503        _: &mut Window,
17504        cx: &mut Context<Self>,
17505    ) {
17506        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17507    }
17508
17509    pub fn expand_excerpts_down(
17510        &mut self,
17511        action: &ExpandExcerptsDown,
17512        _: &mut Window,
17513        cx: &mut Context<Self>,
17514    ) {
17515        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17516    }
17517
17518    pub fn expand_excerpts_up(
17519        &mut self,
17520        action: &ExpandExcerptsUp,
17521        _: &mut Window,
17522        cx: &mut Context<Self>,
17523    ) {
17524        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17525    }
17526
17527    pub fn expand_excerpts_for_direction(
17528        &mut self,
17529        lines: u32,
17530        direction: ExpandExcerptDirection,
17531        cx: &mut Context<Self>,
17532    ) {
17533        let selections = self.selections.disjoint_anchors_arc();
17534
17535        let lines = if lines == 0 {
17536            EditorSettings::get_global(cx).expand_excerpt_lines
17537        } else {
17538            lines
17539        };
17540
17541        let snapshot = self.buffer.read(cx).snapshot(cx);
17542        let excerpt_ids = selections
17543            .iter()
17544            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17545            .unique()
17546            .sorted()
17547            .collect::<Vec<_>>();
17548
17549        if self.delegate_expand_excerpts {
17550            cx.emit(EditorEvent::ExpandExcerptsRequested {
17551                excerpt_ids,
17552                lines,
17553                direction,
17554            });
17555            return;
17556        }
17557
17558        self.buffer.update(cx, |buffer, cx| {
17559            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17560        })
17561    }
17562
17563    pub fn expand_excerpt(
17564        &mut self,
17565        excerpt: ExcerptId,
17566        direction: ExpandExcerptDirection,
17567        window: &mut Window,
17568        cx: &mut Context<Self>,
17569    ) {
17570        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17571
17572        if self.delegate_expand_excerpts {
17573            cx.emit(EditorEvent::ExpandExcerptsRequested {
17574                excerpt_ids: vec![excerpt],
17575                lines: lines_to_expand,
17576                direction,
17577            });
17578            return;
17579        }
17580
17581        let current_scroll_position = self.scroll_position(cx);
17582        let mut scroll = None;
17583
17584        if direction == ExpandExcerptDirection::Down {
17585            let multi_buffer = self.buffer.read(cx);
17586            let snapshot = multi_buffer.snapshot(cx);
17587            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17588                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17589                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17590            {
17591                let buffer_snapshot = buffer.read(cx).snapshot();
17592                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17593                let last_row = buffer_snapshot.max_point().row;
17594                let lines_below = last_row.saturating_sub(excerpt_end_row);
17595                if lines_below >= lines_to_expand {
17596                    scroll = Some(
17597                        current_scroll_position
17598                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17599                    );
17600                }
17601            }
17602        }
17603        if direction == ExpandExcerptDirection::Up
17604            && self
17605                .buffer
17606                .read(cx)
17607                .snapshot(cx)
17608                .excerpt_before(excerpt)
17609                .is_none()
17610        {
17611            scroll = Some(current_scroll_position);
17612        }
17613
17614        self.buffer.update(cx, |buffer, cx| {
17615            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17616        });
17617
17618        if let Some(new_scroll_position) = scroll {
17619            self.set_scroll_position(new_scroll_position, window, cx);
17620        }
17621    }
17622
17623    pub fn go_to_singleton_buffer_point(
17624        &mut self,
17625        point: Point,
17626        window: &mut Window,
17627        cx: &mut Context<Self>,
17628    ) {
17629        self.go_to_singleton_buffer_range(point..point, window, cx);
17630    }
17631
17632    pub fn go_to_singleton_buffer_range(
17633        &mut self,
17634        range: Range<Point>,
17635        window: &mut Window,
17636        cx: &mut Context<Self>,
17637    ) {
17638        let multibuffer = self.buffer().read(cx);
17639        let Some(buffer) = multibuffer.as_singleton() else {
17640            return;
17641        };
17642        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17643            return;
17644        };
17645        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17646            return;
17647        };
17648        self.change_selections(
17649            SelectionEffects::default().nav_history(true),
17650            window,
17651            cx,
17652            |s| s.select_anchor_ranges([start..end]),
17653        );
17654    }
17655
17656    pub fn go_to_diagnostic(
17657        &mut self,
17658        action: &GoToDiagnostic,
17659        window: &mut Window,
17660        cx: &mut Context<Self>,
17661    ) {
17662        if !self.diagnostics_enabled() {
17663            return;
17664        }
17665        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17666        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17667    }
17668
17669    pub fn go_to_prev_diagnostic(
17670        &mut self,
17671        action: &GoToPreviousDiagnostic,
17672        window: &mut Window,
17673        cx: &mut Context<Self>,
17674    ) {
17675        if !self.diagnostics_enabled() {
17676            return;
17677        }
17678        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17679        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17680    }
17681
17682    pub fn go_to_diagnostic_impl(
17683        &mut self,
17684        direction: Direction,
17685        severity: GoToDiagnosticSeverityFilter,
17686        window: &mut Window,
17687        cx: &mut Context<Self>,
17688    ) {
17689        let buffer = self.buffer.read(cx).snapshot(cx);
17690        let selection = self
17691            .selections
17692            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17693
17694        let mut active_group_id = None;
17695        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17696            && active_group.active_range.start.to_offset(&buffer) == selection.start
17697        {
17698            active_group_id = Some(active_group.group_id);
17699        }
17700
17701        fn filtered<'a>(
17702            severity: GoToDiagnosticSeverityFilter,
17703            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17704        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17705            diagnostics
17706                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17707                .filter(|entry| entry.range.start != entry.range.end)
17708                .filter(|entry| !entry.diagnostic.is_unnecessary)
17709        }
17710
17711        let before = filtered(
17712            severity,
17713            buffer
17714                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17715                .filter(|entry| entry.range.start <= selection.start),
17716        );
17717        let after = filtered(
17718            severity,
17719            buffer
17720                .diagnostics_in_range(selection.start..buffer.len())
17721                .filter(|entry| entry.range.start >= selection.start),
17722        );
17723
17724        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17725        if direction == Direction::Prev {
17726            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17727            {
17728                for diagnostic in prev_diagnostics.into_iter().rev() {
17729                    if diagnostic.range.start != selection.start
17730                        || active_group_id
17731                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17732                    {
17733                        found = Some(diagnostic);
17734                        break 'outer;
17735                    }
17736                }
17737            }
17738        } else {
17739            for diagnostic in after.chain(before) {
17740                if diagnostic.range.start != selection.start
17741                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17742                {
17743                    found = Some(diagnostic);
17744                    break;
17745                }
17746            }
17747        }
17748        let Some(next_diagnostic) = found else {
17749            return;
17750        };
17751
17752        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17753        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17754            return;
17755        };
17756        let snapshot = self.snapshot(window, cx);
17757        if snapshot.intersects_fold(next_diagnostic.range.start) {
17758            self.unfold_ranges(
17759                std::slice::from_ref(&next_diagnostic.range),
17760                true,
17761                false,
17762                cx,
17763            );
17764        }
17765        self.change_selections(Default::default(), window, cx, |s| {
17766            s.select_ranges(vec![
17767                next_diagnostic.range.start..next_diagnostic.range.start,
17768            ])
17769        });
17770        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17771        self.refresh_edit_prediction(false, true, window, cx);
17772    }
17773
17774    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17775        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17776        let snapshot = self.snapshot(window, cx);
17777        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17778        self.go_to_hunk_before_or_after_position(
17779            &snapshot,
17780            selection.head(),
17781            Direction::Next,
17782            true,
17783            window,
17784            cx,
17785        );
17786    }
17787
17788    pub fn go_to_hunk_before_or_after_position(
17789        &mut self,
17790        snapshot: &EditorSnapshot,
17791        position: Point,
17792        direction: Direction,
17793        wrap_around: bool,
17794        window: &mut Window,
17795        cx: &mut Context<Editor>,
17796    ) {
17797        let row = if direction == Direction::Next {
17798            self.hunk_after_position(snapshot, position, wrap_around)
17799                .map(|hunk| hunk.row_range.start)
17800        } else {
17801            self.hunk_before_position(snapshot, position, wrap_around)
17802        };
17803
17804        if let Some(row) = row {
17805            let destination = Point::new(row.0, 0);
17806            let autoscroll = Autoscroll::center();
17807
17808            self.unfold_ranges(&[destination..destination], false, false, cx);
17809            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17810                s.select_ranges([destination..destination]);
17811            });
17812        }
17813    }
17814
17815    fn hunk_after_position(
17816        &mut self,
17817        snapshot: &EditorSnapshot,
17818        position: Point,
17819        wrap_around: bool,
17820    ) -> Option<MultiBufferDiffHunk> {
17821        let result = snapshot
17822            .buffer_snapshot()
17823            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17824            .find(|hunk| hunk.row_range.start.0 > position.row);
17825
17826        if wrap_around {
17827            result.or_else(|| {
17828                snapshot
17829                    .buffer_snapshot()
17830                    .diff_hunks_in_range(Point::zero()..position)
17831                    .find(|hunk| hunk.row_range.end.0 < position.row)
17832            })
17833        } else {
17834            result
17835        }
17836    }
17837
17838    fn go_to_prev_hunk(
17839        &mut self,
17840        _: &GoToPreviousHunk,
17841        window: &mut Window,
17842        cx: &mut Context<Self>,
17843    ) {
17844        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17845        let snapshot = self.snapshot(window, cx);
17846        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17847        self.go_to_hunk_before_or_after_position(
17848            &snapshot,
17849            selection.head(),
17850            Direction::Prev,
17851            true,
17852            window,
17853            cx,
17854        );
17855    }
17856
17857    fn hunk_before_position(
17858        &mut self,
17859        snapshot: &EditorSnapshot,
17860        position: Point,
17861        wrap_around: bool,
17862    ) -> Option<MultiBufferRow> {
17863        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17864
17865        if wrap_around {
17866            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17867        } else {
17868            result
17869        }
17870    }
17871
17872    fn go_to_next_change(
17873        &mut self,
17874        _: &GoToNextChange,
17875        window: &mut Window,
17876        cx: &mut Context<Self>,
17877    ) {
17878        if let Some(selections) = self
17879            .change_list
17880            .next_change(1, Direction::Next)
17881            .map(|s| s.to_vec())
17882        {
17883            self.change_selections(Default::default(), window, cx, |s| {
17884                let map = s.display_snapshot();
17885                s.select_display_ranges(selections.iter().map(|a| {
17886                    let point = a.to_display_point(&map);
17887                    point..point
17888                }))
17889            })
17890        }
17891    }
17892
17893    fn go_to_previous_change(
17894        &mut self,
17895        _: &GoToPreviousChange,
17896        window: &mut Window,
17897        cx: &mut Context<Self>,
17898    ) {
17899        if let Some(selections) = self
17900            .change_list
17901            .next_change(1, Direction::Prev)
17902            .map(|s| s.to_vec())
17903        {
17904            self.change_selections(Default::default(), window, cx, |s| {
17905                let map = s.display_snapshot();
17906                s.select_display_ranges(selections.iter().map(|a| {
17907                    let point = a.to_display_point(&map);
17908                    point..point
17909                }))
17910            })
17911        }
17912    }
17913
17914    pub fn go_to_next_document_highlight(
17915        &mut self,
17916        _: &GoToNextDocumentHighlight,
17917        window: &mut Window,
17918        cx: &mut Context<Self>,
17919    ) {
17920        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17921    }
17922
17923    pub fn go_to_prev_document_highlight(
17924        &mut self,
17925        _: &GoToPreviousDocumentHighlight,
17926        window: &mut Window,
17927        cx: &mut Context<Self>,
17928    ) {
17929        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17930    }
17931
17932    pub fn go_to_document_highlight_before_or_after_position(
17933        &mut self,
17934        direction: Direction,
17935        window: &mut Window,
17936        cx: &mut Context<Editor>,
17937    ) {
17938        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17939        let snapshot = self.snapshot(window, cx);
17940        let buffer = &snapshot.buffer_snapshot();
17941        let position = self
17942            .selections
17943            .newest::<Point>(&snapshot.display_snapshot)
17944            .head();
17945        let anchor_position = buffer.anchor_after(position);
17946
17947        // Get all document highlights (both read and write)
17948        let mut all_highlights = Vec::new();
17949
17950        if let Some((_, read_highlights)) = self
17951            .background_highlights
17952            .get(&HighlightKey::DocumentHighlightRead)
17953        {
17954            all_highlights.extend(read_highlights.iter());
17955        }
17956
17957        if let Some((_, write_highlights)) = self
17958            .background_highlights
17959            .get(&HighlightKey::DocumentHighlightWrite)
17960        {
17961            all_highlights.extend(write_highlights.iter());
17962        }
17963
17964        if all_highlights.is_empty() {
17965            return;
17966        }
17967
17968        // Sort highlights by position
17969        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17970
17971        let target_highlight = match direction {
17972            Direction::Next => {
17973                // Find the first highlight after the current position
17974                all_highlights
17975                    .iter()
17976                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17977            }
17978            Direction::Prev => {
17979                // Find the last highlight before the current position
17980                all_highlights
17981                    .iter()
17982                    .rev()
17983                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17984            }
17985        };
17986
17987        if let Some(highlight) = target_highlight {
17988            let destination = highlight.start.to_point(buffer);
17989            let autoscroll = Autoscroll::center();
17990
17991            self.unfold_ranges(&[destination..destination], false, false, cx);
17992            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17993                s.select_ranges([destination..destination]);
17994            });
17995        }
17996    }
17997
17998    fn go_to_line<T: 'static>(
17999        &mut self,
18000        position: Anchor,
18001        highlight_color: Option<Hsla>,
18002        window: &mut Window,
18003        cx: &mut Context<Self>,
18004    ) {
18005        let snapshot = self.snapshot(window, cx).display_snapshot;
18006        let position = position.to_point(&snapshot.buffer_snapshot());
18007        let start = snapshot
18008            .buffer_snapshot()
18009            .clip_point(Point::new(position.row, 0), Bias::Left);
18010        let end = start + Point::new(1, 0);
18011        let start = snapshot.buffer_snapshot().anchor_before(start);
18012        let end = snapshot.buffer_snapshot().anchor_before(end);
18013
18014        self.highlight_rows::<T>(
18015            start..end,
18016            highlight_color
18017                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
18018            Default::default(),
18019            cx,
18020        );
18021
18022        if self.buffer.read(cx).is_singleton() {
18023            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
18024        }
18025    }
18026
18027    pub fn go_to_definition(
18028        &mut self,
18029        _: &GoToDefinition,
18030        window: &mut Window,
18031        cx: &mut Context<Self>,
18032    ) -> Task<Result<Navigated>> {
18033        let definition =
18034            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
18035        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
18036        cx.spawn_in(window, async move |editor, cx| {
18037            if definition.await? == Navigated::Yes {
18038                return Ok(Navigated::Yes);
18039            }
18040            match fallback_strategy {
18041                GoToDefinitionFallback::None => Ok(Navigated::No),
18042                GoToDefinitionFallback::FindAllReferences => {
18043                    match editor.update_in(cx, |editor, window, cx| {
18044                        editor.find_all_references(&FindAllReferences::default(), window, cx)
18045                    })? {
18046                        Some(references) => references.await,
18047                        None => Ok(Navigated::No),
18048                    }
18049                }
18050            }
18051        })
18052    }
18053
18054    pub fn go_to_declaration(
18055        &mut self,
18056        _: &GoToDeclaration,
18057        window: &mut Window,
18058        cx: &mut Context<Self>,
18059    ) -> Task<Result<Navigated>> {
18060        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
18061    }
18062
18063    pub fn go_to_declaration_split(
18064        &mut self,
18065        _: &GoToDeclaration,
18066        window: &mut Window,
18067        cx: &mut Context<Self>,
18068    ) -> Task<Result<Navigated>> {
18069        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
18070    }
18071
18072    pub fn go_to_implementation(
18073        &mut self,
18074        _: &GoToImplementation,
18075        window: &mut Window,
18076        cx: &mut Context<Self>,
18077    ) -> Task<Result<Navigated>> {
18078        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
18079    }
18080
18081    pub fn go_to_implementation_split(
18082        &mut self,
18083        _: &GoToImplementationSplit,
18084        window: &mut Window,
18085        cx: &mut Context<Self>,
18086    ) -> Task<Result<Navigated>> {
18087        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
18088    }
18089
18090    pub fn go_to_type_definition(
18091        &mut self,
18092        _: &GoToTypeDefinition,
18093        window: &mut Window,
18094        cx: &mut Context<Self>,
18095    ) -> Task<Result<Navigated>> {
18096        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
18097    }
18098
18099    pub fn go_to_definition_split(
18100        &mut self,
18101        _: &GoToDefinitionSplit,
18102        window: &mut Window,
18103        cx: &mut Context<Self>,
18104    ) -> Task<Result<Navigated>> {
18105        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
18106    }
18107
18108    pub fn go_to_type_definition_split(
18109        &mut self,
18110        _: &GoToTypeDefinitionSplit,
18111        window: &mut Window,
18112        cx: &mut Context<Self>,
18113    ) -> Task<Result<Navigated>> {
18114        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18115    }
18116
18117    fn go_to_definition_of_kind(
18118        &mut self,
18119        kind: GotoDefinitionKind,
18120        split: bool,
18121        window: &mut Window,
18122        cx: &mut Context<Self>,
18123    ) -> Task<Result<Navigated>> {
18124        let Some(provider) = self.semantics_provider.clone() else {
18125            return Task::ready(Ok(Navigated::No));
18126        };
18127        let head = self
18128            .selections
18129            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18130            .head();
18131        let buffer = self.buffer.read(cx);
18132        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18133            return Task::ready(Ok(Navigated::No));
18134        };
18135        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18136            return Task::ready(Ok(Navigated::No));
18137        };
18138
18139        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18140
18141        cx.spawn_in(window, async move |editor, cx| {
18142            let Some(definitions) = definitions.await? else {
18143                return Ok(Navigated::No);
18144            };
18145            let navigated = editor
18146                .update_in(cx, |editor, window, cx| {
18147                    editor.navigate_to_hover_links(
18148                        Some(kind),
18149                        definitions
18150                            .into_iter()
18151                            .filter(|location| {
18152                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18153                            })
18154                            .map(HoverLink::Text)
18155                            .collect::<Vec<_>>(),
18156                        nav_entry,
18157                        split,
18158                        window,
18159                        cx,
18160                    )
18161                })?
18162                .await?;
18163            anyhow::Ok(navigated)
18164        })
18165    }
18166
18167    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18168        let selection = self.selections.newest_anchor();
18169        let head = selection.head();
18170        let tail = selection.tail();
18171
18172        let Some((buffer, start_position)) =
18173            self.buffer.read(cx).text_anchor_for_position(head, cx)
18174        else {
18175            return;
18176        };
18177
18178        let end_position = if head != tail {
18179            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18180                return;
18181            };
18182            Some(pos)
18183        } else {
18184            None
18185        };
18186
18187        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18188            let url = if let Some(end_pos) = end_position {
18189                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18190            } else {
18191                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18192            };
18193
18194            if let Some(url) = url {
18195                cx.update(|window, cx| {
18196                    if parse_zed_link(&url, cx).is_some() {
18197                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18198                    } else {
18199                        cx.open_url(&url);
18200                    }
18201                })?;
18202            }
18203
18204            anyhow::Ok(())
18205        });
18206
18207        url_finder.detach();
18208    }
18209
18210    pub fn open_selected_filename(
18211        &mut self,
18212        _: &OpenSelectedFilename,
18213        window: &mut Window,
18214        cx: &mut Context<Self>,
18215    ) {
18216        let Some(workspace) = self.workspace() else {
18217            return;
18218        };
18219
18220        let position = self.selections.newest_anchor().head();
18221
18222        let Some((buffer, buffer_position)) =
18223            self.buffer.read(cx).text_anchor_for_position(position, cx)
18224        else {
18225            return;
18226        };
18227
18228        let project = self.project.clone();
18229
18230        cx.spawn_in(window, async move |_, cx| {
18231            let result = find_file(&buffer, project, buffer_position, cx).await;
18232
18233            if let Some((_, path)) = result {
18234                workspace
18235                    .update_in(cx, |workspace, window, cx| {
18236                        workspace.open_resolved_path(path, window, cx)
18237                    })?
18238                    .await?;
18239            }
18240            anyhow::Ok(())
18241        })
18242        .detach();
18243    }
18244
18245    pub(crate) fn navigate_to_hover_links(
18246        &mut self,
18247        kind: Option<GotoDefinitionKind>,
18248        definitions: Vec<HoverLink>,
18249        origin: Option<NavigationEntry>,
18250        split: bool,
18251        window: &mut Window,
18252        cx: &mut Context<Editor>,
18253    ) -> Task<Result<Navigated>> {
18254        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18255        let mut first_url_or_file = None;
18256        let definitions: Vec<_> = definitions
18257            .into_iter()
18258            .filter_map(|def| match def {
18259                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18260                HoverLink::InlayHint(lsp_location, server_id) => {
18261                    let computation =
18262                        self.compute_target_location(lsp_location, server_id, window, cx);
18263                    Some(cx.background_spawn(computation))
18264                }
18265                HoverLink::Url(url) => {
18266                    first_url_or_file = Some(Either::Left(url));
18267                    None
18268                }
18269                HoverLink::File(path) => {
18270                    first_url_or_file = Some(Either::Right(path));
18271                    None
18272                }
18273            })
18274            .collect();
18275
18276        let workspace = self.workspace();
18277
18278        cx.spawn_in(window, async move |editor, cx| {
18279            let locations: Vec<Location> = future::join_all(definitions)
18280                .await
18281                .into_iter()
18282                .filter_map(|location| location.transpose())
18283                .collect::<Result<_>>()
18284                .context("location tasks")?;
18285            let mut locations = cx.update(|_, cx| {
18286                locations
18287                    .into_iter()
18288                    .map(|location| {
18289                        let buffer = location.buffer.read(cx);
18290                        (location.buffer, location.range.to_point(buffer))
18291                    })
18292                    .into_group_map()
18293            })?;
18294            let mut num_locations = 0;
18295            for ranges in locations.values_mut() {
18296                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18297                ranges.dedup();
18298                num_locations += ranges.len();
18299            }
18300
18301            if num_locations > 1 {
18302                let tab_kind = match kind {
18303                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18304                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18305                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18306                    Some(GotoDefinitionKind::Type) => "Types",
18307                };
18308                let title = editor
18309                    .update_in(cx, |_, _, cx| {
18310                        let target = locations
18311                            .iter()
18312                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18313                            .map(|(buffer, location)| {
18314                                buffer
18315                                    .read(cx)
18316                                    .text_for_range(location.clone())
18317                                    .collect::<String>()
18318                            })
18319                            .filter(|text| !text.contains('\n'))
18320                            .unique()
18321                            .take(3)
18322                            .join(", ");
18323                        if target.is_empty() {
18324                            tab_kind.to_owned()
18325                        } else {
18326                            format!("{tab_kind} for {target}")
18327                        }
18328                    })
18329                    .context("buffer title")?;
18330
18331                let Some(workspace) = workspace else {
18332                    return Ok(Navigated::No);
18333                };
18334
18335                let opened = workspace
18336                    .update_in(cx, |workspace, window, cx| {
18337                        let allow_preview = PreviewTabsSettings::get_global(cx)
18338                            .enable_preview_multibuffer_from_code_navigation;
18339                        if let Some((target_editor, target_pane)) =
18340                            Self::open_locations_in_multibuffer(
18341                                workspace,
18342                                locations,
18343                                title,
18344                                split,
18345                                allow_preview,
18346                                MultibufferSelectionMode::First,
18347                                window,
18348                                cx,
18349                            )
18350                        {
18351                            // We create our own nav history instead of using
18352                            // `target_editor.nav_history` because `nav_history`
18353                            // seems to be populated asynchronously when an item
18354                            // is added to a pane
18355                            let mut nav_history = target_pane
18356                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18357                            target_editor.update(cx, |editor, cx| {
18358                                let nav_data = editor
18359                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18360                                let target =
18361                                    Some(nav_history.navigation_entry(Some(
18362                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18363                                    )));
18364                                nav_history.push_tag(origin, target);
18365                            })
18366                        }
18367                    })
18368                    .is_ok();
18369
18370                anyhow::Ok(Navigated::from_bool(opened))
18371            } else if num_locations == 0 {
18372                // If there is one url or file, open it directly
18373                match first_url_or_file {
18374                    Some(Either::Left(url)) => {
18375                        cx.update(|window, cx| {
18376                            if parse_zed_link(&url, cx).is_some() {
18377                                window
18378                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18379                            } else {
18380                                cx.open_url(&url);
18381                            }
18382                        })?;
18383                        Ok(Navigated::Yes)
18384                    }
18385                    Some(Either::Right(path)) => {
18386                        // TODO(andrew): respect preview tab settings
18387                        //               `enable_keep_preview_on_code_navigation` and
18388                        //               `enable_preview_file_from_code_navigation`
18389                        let Some(workspace) = workspace else {
18390                            return Ok(Navigated::No);
18391                        };
18392                        workspace
18393                            .update_in(cx, |workspace, window, cx| {
18394                                workspace.open_resolved_path(path, window, cx)
18395                            })?
18396                            .await?;
18397                        Ok(Navigated::Yes)
18398                    }
18399                    None => Ok(Navigated::No),
18400                }
18401            } else {
18402                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18403                let target_range = target_ranges.first().unwrap().clone();
18404
18405                editor.update_in(cx, |editor, window, cx| {
18406                    let range = editor.range_for_match(&target_range);
18407                    let range = collapse_multiline_range(range);
18408
18409                    if !split
18410                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18411                    {
18412                        editor.go_to_singleton_buffer_range(range, window, cx);
18413
18414                        let target =
18415                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18416                        if let Some(mut nav_history) = editor.nav_history.clone() {
18417                            nav_history.push_tag(origin, target);
18418                        }
18419                    } else {
18420                        let Some(workspace) = workspace else {
18421                            return Navigated::No;
18422                        };
18423                        let pane = workspace.read(cx).active_pane().clone();
18424                        window.defer(cx, move |window, cx| {
18425                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18426                                workspace.update(cx, |workspace, cx| {
18427                                    let pane = if split {
18428                                        workspace.adjacent_pane(window, cx)
18429                                    } else {
18430                                        workspace.active_pane().clone()
18431                                    };
18432
18433                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18434                                    let keep_old_preview = preview_tabs_settings
18435                                        .enable_keep_preview_on_code_navigation;
18436                                    let allow_new_preview = preview_tabs_settings
18437                                        .enable_preview_file_from_code_navigation;
18438
18439                                    let editor = workspace.open_project_item(
18440                                        pane.clone(),
18441                                        target_buffer.clone(),
18442                                        true,
18443                                        true,
18444                                        keep_old_preview,
18445                                        allow_new_preview,
18446                                        window,
18447                                        cx,
18448                                    );
18449                                    (editor, pane)
18450                                });
18451                            // We create our own nav history instead of using
18452                            // `target_editor.nav_history` because `nav_history`
18453                            // seems to be populated asynchronously when an item
18454                            // is added to a pane
18455                            let mut nav_history = target_pane
18456                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18457                            target_editor.update(cx, |target_editor, cx| {
18458                                // When selecting a definition in a different buffer, disable the nav history
18459                                // to avoid creating a history entry at the previous cursor location.
18460                                pane.update(cx, |pane, _| pane.disable_history());
18461                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18462
18463                                let nav_data = target_editor.navigation_data(
18464                                    target_editor.selections.newest_anchor().head(),
18465                                    cx,
18466                                );
18467                                let target =
18468                                    Some(nav_history.navigation_entry(Some(
18469                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18470                                    )));
18471                                nav_history.push_tag(origin, target);
18472                                pane.update(cx, |pane, _| pane.enable_history());
18473                            });
18474                        });
18475                    }
18476                    Navigated::Yes
18477                })
18478            }
18479        })
18480    }
18481
18482    fn compute_target_location(
18483        &self,
18484        lsp_location: lsp::Location,
18485        server_id: LanguageServerId,
18486        window: &mut Window,
18487        cx: &mut Context<Self>,
18488    ) -> Task<anyhow::Result<Option<Location>>> {
18489        let Some(project) = self.project.clone() else {
18490            return Task::ready(Ok(None));
18491        };
18492
18493        cx.spawn_in(window, async move |editor, cx| {
18494            let location_task = editor.update(cx, |_, cx| {
18495                project.update(cx, |project, cx| {
18496                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18497                })
18498            })?;
18499            let location = Some({
18500                let target_buffer_handle = location_task.await.context("open local buffer")?;
18501                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18502                    let target_start = target_buffer
18503                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18504                    let target_end = target_buffer
18505                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18506                    target_buffer.anchor_after(target_start)
18507                        ..target_buffer.anchor_before(target_end)
18508                });
18509                Location {
18510                    buffer: target_buffer_handle,
18511                    range,
18512                }
18513            });
18514            Ok(location)
18515        })
18516    }
18517
18518    fn go_to_next_reference(
18519        &mut self,
18520        _: &GoToNextReference,
18521        window: &mut Window,
18522        cx: &mut Context<Self>,
18523    ) {
18524        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18525        if let Some(task) = task {
18526            task.detach();
18527        };
18528    }
18529
18530    fn go_to_prev_reference(
18531        &mut self,
18532        _: &GoToPreviousReference,
18533        window: &mut Window,
18534        cx: &mut Context<Self>,
18535    ) {
18536        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18537        if let Some(task) = task {
18538            task.detach();
18539        };
18540    }
18541
18542    pub fn go_to_reference_before_or_after_position(
18543        &mut self,
18544        direction: Direction,
18545        count: usize,
18546        window: &mut Window,
18547        cx: &mut Context<Self>,
18548    ) -> Option<Task<Result<()>>> {
18549        let selection = self.selections.newest_anchor();
18550        let head = selection.head();
18551
18552        let multi_buffer = self.buffer.read(cx);
18553
18554        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18555        let workspace = self.workspace()?;
18556        let project = workspace.read(cx).project().clone();
18557        let references =
18558            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18559        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18560            let Some(locations) = references.await? else {
18561                return Ok(());
18562            };
18563
18564            if locations.is_empty() {
18565                // totally normal - the cursor may be on something which is not
18566                // a symbol (e.g. a keyword)
18567                log::info!("no references found under cursor");
18568                return Ok(());
18569            }
18570
18571            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18572
18573            let (locations, current_location_index) =
18574                multi_buffer.update(cx, |multi_buffer, cx| {
18575                    let mut locations = locations
18576                        .into_iter()
18577                        .filter_map(|loc| {
18578                            let start = multi_buffer.buffer_anchor_to_anchor(
18579                                &loc.buffer,
18580                                loc.range.start,
18581                                cx,
18582                            )?;
18583                            let end = multi_buffer.buffer_anchor_to_anchor(
18584                                &loc.buffer,
18585                                loc.range.end,
18586                                cx,
18587                            )?;
18588                            Some(start..end)
18589                        })
18590                        .collect::<Vec<_>>();
18591
18592                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18593                    // There is an O(n) implementation, but given this list will be
18594                    // small (usually <100 items), the extra O(log(n)) factor isn't
18595                    // worth the (surprisingly large amount of) extra complexity.
18596                    locations
18597                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18598
18599                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18600
18601                    let current_location_index = locations.iter().position(|loc| {
18602                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18603                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18604                    });
18605
18606                    (locations, current_location_index)
18607                });
18608
18609            let Some(current_location_index) = current_location_index else {
18610                // This indicates something has gone wrong, because we already
18611                // handle the "no references" case above
18612                log::error!(
18613                    "failed to find current reference under cursor. Total references: {}",
18614                    locations.len()
18615                );
18616                return Ok(());
18617            };
18618
18619            let destination_location_index = match direction {
18620                Direction::Next => (current_location_index + count) % locations.len(),
18621                Direction::Prev => {
18622                    (current_location_index + locations.len() - count % locations.len())
18623                        % locations.len()
18624                }
18625            };
18626
18627            // TODO(cameron): is this needed?
18628            // the thinking is to avoid "jumping to the current location" (avoid
18629            // polluting "jumplist" in vim terms)
18630            if current_location_index == destination_location_index {
18631                return Ok(());
18632            }
18633
18634            let Range { start, end } = locations[destination_location_index];
18635
18636            editor.update_in(cx, |editor, window, cx| {
18637                let effects = SelectionEffects::default();
18638
18639                editor.unfold_ranges(&[start..end], false, false, cx);
18640                editor.change_selections(effects, window, cx, |s| {
18641                    s.select_ranges([start..start]);
18642                });
18643            })?;
18644
18645            Ok(())
18646        }))
18647    }
18648
18649    pub fn find_all_references(
18650        &mut self,
18651        action: &FindAllReferences,
18652        window: &mut Window,
18653        cx: &mut Context<Self>,
18654    ) -> Option<Task<Result<Navigated>>> {
18655        let always_open_multibuffer = action.always_open_multibuffer;
18656        let selection = self.selections.newest_anchor();
18657        let multi_buffer = self.buffer.read(cx);
18658        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18659        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18660        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18661        let head = selection_offset.head();
18662
18663        let head_anchor = multi_buffer_snapshot.anchor_at(
18664            head,
18665            if head < selection_offset.tail() {
18666                Bias::Right
18667            } else {
18668                Bias::Left
18669            },
18670        );
18671
18672        match self
18673            .find_all_references_task_sources
18674            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18675        {
18676            Ok(_) => {
18677                log::info!(
18678                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18679                );
18680                return None;
18681            }
18682            Err(i) => {
18683                self.find_all_references_task_sources.insert(i, head_anchor);
18684            }
18685        }
18686
18687        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18688        let workspace = self.workspace()?;
18689        let project = workspace.read(cx).project().clone();
18690        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18691        Some(cx.spawn_in(window, async move |editor, cx| {
18692            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18693                if let Ok(i) = editor
18694                    .find_all_references_task_sources
18695                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18696                {
18697                    editor.find_all_references_task_sources.remove(i);
18698                }
18699            });
18700
18701            let Some(locations) = references.await? else {
18702                return anyhow::Ok(Navigated::No);
18703            };
18704            let mut locations = cx.update(|_, cx| {
18705                locations
18706                    .into_iter()
18707                    .map(|location| {
18708                        let buffer = location.buffer.read(cx);
18709                        (location.buffer, location.range.to_point(buffer))
18710                    })
18711                    // if special-casing the single-match case, remove ranges
18712                    // that intersect current selection
18713                    .filter(|(location_buffer, location)| {
18714                        if always_open_multibuffer || &buffer != location_buffer {
18715                            return true;
18716                        }
18717
18718                        !location.contains_inclusive(&selection_point.range())
18719                    })
18720                    .into_group_map()
18721            })?;
18722            if locations.is_empty() {
18723                return anyhow::Ok(Navigated::No);
18724            }
18725            for ranges in locations.values_mut() {
18726                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18727                ranges.dedup();
18728            }
18729            let mut num_locations = 0;
18730            for ranges in locations.values_mut() {
18731                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18732                ranges.dedup();
18733                num_locations += ranges.len();
18734            }
18735
18736            if num_locations == 1 && !always_open_multibuffer {
18737                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18738                let target_range = target_ranges.first().unwrap().clone();
18739
18740                return editor.update_in(cx, |editor, window, cx| {
18741                    let range = target_range.to_point(target_buffer.read(cx));
18742                    let range = editor.range_for_match(&range);
18743                    let range = range.start..range.start;
18744
18745                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18746                        editor.go_to_singleton_buffer_range(range, window, cx);
18747                    } else {
18748                        let pane = workspace.read(cx).active_pane().clone();
18749                        window.defer(cx, move |window, cx| {
18750                            let target_editor: Entity<Self> =
18751                                workspace.update(cx, |workspace, cx| {
18752                                    let pane = workspace.active_pane().clone();
18753
18754                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18755                                    let keep_old_preview = preview_tabs_settings
18756                                        .enable_keep_preview_on_code_navigation;
18757                                    let allow_new_preview = preview_tabs_settings
18758                                        .enable_preview_file_from_code_navigation;
18759
18760                                    workspace.open_project_item(
18761                                        pane,
18762                                        target_buffer.clone(),
18763                                        true,
18764                                        true,
18765                                        keep_old_preview,
18766                                        allow_new_preview,
18767                                        window,
18768                                        cx,
18769                                    )
18770                                });
18771                            target_editor.update(cx, |target_editor, cx| {
18772                                // When selecting a definition in a different buffer, disable the nav history
18773                                // to avoid creating a history entry at the previous cursor location.
18774                                pane.update(cx, |pane, _| pane.disable_history());
18775                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18776                                pane.update(cx, |pane, _| pane.enable_history());
18777                            });
18778                        });
18779                    }
18780                    Navigated::No
18781                });
18782            }
18783
18784            workspace.update_in(cx, |workspace, window, cx| {
18785                let target = locations
18786                    .iter()
18787                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18788                    .map(|(buffer, location)| {
18789                        buffer
18790                            .read(cx)
18791                            .text_for_range(location.clone())
18792                            .collect::<String>()
18793                    })
18794                    .filter(|text| !text.contains('\n'))
18795                    .unique()
18796                    .take(3)
18797                    .join(", ");
18798                let title = if target.is_empty() {
18799                    "References".to_owned()
18800                } else {
18801                    format!("References to {target}")
18802                };
18803                let allow_preview = PreviewTabsSettings::get_global(cx)
18804                    .enable_preview_multibuffer_from_code_navigation;
18805                Self::open_locations_in_multibuffer(
18806                    workspace,
18807                    locations,
18808                    title,
18809                    false,
18810                    allow_preview,
18811                    MultibufferSelectionMode::First,
18812                    window,
18813                    cx,
18814                );
18815                Navigated::Yes
18816            })
18817        }))
18818    }
18819
18820    /// Opens a multibuffer with the given project locations in it.
18821    pub fn open_locations_in_multibuffer(
18822        workspace: &mut Workspace,
18823        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18824        title: String,
18825        split: bool,
18826        allow_preview: bool,
18827        multibuffer_selection_mode: MultibufferSelectionMode,
18828        window: &mut Window,
18829        cx: &mut Context<Workspace>,
18830    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18831        if locations.is_empty() {
18832            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18833            return None;
18834        }
18835
18836        let capability = workspace.project().read(cx).capability();
18837        let mut ranges = <Vec<Range<Anchor>>>::new();
18838
18839        // a key to find existing multibuffer editors with the same set of locations
18840        // to prevent us from opening more and more multibuffer tabs for searches and the like
18841        let mut key = (title.clone(), vec![]);
18842        let excerpt_buffer = cx.new(|cx| {
18843            let key = &mut key.1;
18844            let mut multibuffer = MultiBuffer::new(capability);
18845            for (buffer, mut ranges_for_buffer) in locations {
18846                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18847                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18848                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18849                    PathKey::for_buffer(&buffer, cx),
18850                    buffer.clone(),
18851                    ranges_for_buffer,
18852                    multibuffer_context_lines(cx),
18853                    cx,
18854                );
18855                ranges.extend(new_ranges)
18856            }
18857
18858            multibuffer.with_title(title)
18859        });
18860        let existing = workspace.active_pane().update(cx, |pane, cx| {
18861            pane.items()
18862                .filter_map(|item| item.downcast::<Editor>())
18863                .find(|editor| {
18864                    editor
18865                        .read(cx)
18866                        .lookup_key
18867                        .as_ref()
18868                        .and_then(|it| {
18869                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18870                        })
18871                        .is_some_and(|it| *it == key)
18872                })
18873        });
18874        let was_existing = existing.is_some();
18875        let editor = existing.unwrap_or_else(|| {
18876            cx.new(|cx| {
18877                let mut editor = Editor::for_multibuffer(
18878                    excerpt_buffer,
18879                    Some(workspace.project().clone()),
18880                    window,
18881                    cx,
18882                );
18883                editor.lookup_key = Some(Box::new(key));
18884                editor
18885            })
18886        });
18887        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18888            MultibufferSelectionMode::First => {
18889                if let Some(first_range) = ranges.first() {
18890                    editor.change_selections(
18891                        SelectionEffects::no_scroll(),
18892                        window,
18893                        cx,
18894                        |selections| {
18895                            selections.clear_disjoint();
18896                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18897                        },
18898                    );
18899                }
18900                editor.highlight_background(
18901                    HighlightKey::Editor,
18902                    &ranges,
18903                    |_, theme| theme.colors().editor_highlighted_line_background,
18904                    cx,
18905                );
18906            }
18907            MultibufferSelectionMode::All => {
18908                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18909                    selections.clear_disjoint();
18910                    selections.select_anchor_ranges(ranges);
18911                });
18912            }
18913        });
18914
18915        let item = Box::new(editor.clone());
18916
18917        let pane = if split {
18918            workspace.adjacent_pane(window, cx)
18919        } else {
18920            workspace.active_pane().clone()
18921        };
18922        let activate_pane = split;
18923
18924        let mut destination_index = None;
18925        pane.update(cx, |pane, cx| {
18926            if allow_preview && !was_existing {
18927                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18928            }
18929            if was_existing && !allow_preview {
18930                pane.unpreview_item_if_preview(item.item_id());
18931            }
18932            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18933        });
18934
18935        Some((editor, pane))
18936    }
18937
18938    pub fn rename(
18939        &mut self,
18940        _: &Rename,
18941        window: &mut Window,
18942        cx: &mut Context<Self>,
18943    ) -> Option<Task<Result<()>>> {
18944        use language::ToOffset as _;
18945
18946        let provider = self.semantics_provider.clone()?;
18947        let selection = self.selections.newest_anchor().clone();
18948        let (cursor_buffer, cursor_buffer_position) = self
18949            .buffer
18950            .read(cx)
18951            .text_anchor_for_position(selection.head(), cx)?;
18952        let (tail_buffer, cursor_buffer_position_end) = self
18953            .buffer
18954            .read(cx)
18955            .text_anchor_for_position(selection.tail(), cx)?;
18956        if tail_buffer != cursor_buffer {
18957            return None;
18958        }
18959
18960        let snapshot = cursor_buffer.read(cx).snapshot();
18961        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18962        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18963        let prepare_rename = provider
18964            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18965            .unwrap_or_else(|| Task::ready(Ok(None)));
18966        drop(snapshot);
18967
18968        Some(cx.spawn_in(window, async move |this, cx| {
18969            let rename_range = if let Some(range) = prepare_rename.await? {
18970                Some(range)
18971            } else {
18972                this.update(cx, |this, cx| {
18973                    let buffer = this.buffer.read(cx).snapshot(cx);
18974                    let mut buffer_highlights = this
18975                        .document_highlights_for_position(selection.head(), &buffer)
18976                        .filter(|highlight| {
18977                            highlight.start.excerpt_id == selection.head().excerpt_id
18978                                && highlight.end.excerpt_id == selection.head().excerpt_id
18979                        });
18980                    buffer_highlights
18981                        .next()
18982                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18983                })?
18984            };
18985            if let Some(rename_range) = rename_range {
18986                this.update_in(cx, |this, window, cx| {
18987                    let snapshot = cursor_buffer.read(cx).snapshot();
18988                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18989                    let cursor_offset_in_rename_range =
18990                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18991                    let cursor_offset_in_rename_range_end =
18992                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18993
18994                    this.take_rename(false, window, cx);
18995                    let buffer = this.buffer.read(cx).read(cx);
18996                    let cursor_offset = selection.head().to_offset(&buffer);
18997                    let rename_start =
18998                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18999                    let rename_end = rename_start + rename_buffer_range.len();
19000                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
19001                    let mut old_highlight_id = None;
19002                    let old_name: Arc<str> = buffer
19003                        .chunks(rename_start..rename_end, true)
19004                        .map(|chunk| {
19005                            if old_highlight_id.is_none() {
19006                                old_highlight_id = chunk.syntax_highlight_id;
19007                            }
19008                            chunk.text
19009                        })
19010                        .collect::<String>()
19011                        .into();
19012
19013                    drop(buffer);
19014
19015                    // Position the selection in the rename editor so that it matches the current selection.
19016                    this.show_local_selections = false;
19017                    let rename_editor = cx.new(|cx| {
19018                        let mut editor = Editor::single_line(window, cx);
19019                        editor.buffer.update(cx, |buffer, cx| {
19020                            buffer.edit(
19021                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
19022                                None,
19023                                cx,
19024                            )
19025                        });
19026                        let cursor_offset_in_rename_range =
19027                            MultiBufferOffset(cursor_offset_in_rename_range);
19028                        let cursor_offset_in_rename_range_end =
19029                            MultiBufferOffset(cursor_offset_in_rename_range_end);
19030                        let rename_selection_range = match cursor_offset_in_rename_range
19031                            .cmp(&cursor_offset_in_rename_range_end)
19032                        {
19033                            Ordering::Equal => {
19034                                editor.select_all(&SelectAll, window, cx);
19035                                return editor;
19036                            }
19037                            Ordering::Less => {
19038                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
19039                            }
19040                            Ordering::Greater => {
19041                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
19042                            }
19043                        };
19044                        if rename_selection_range.end.0 > old_name.len() {
19045                            editor.select_all(&SelectAll, window, cx);
19046                        } else {
19047                            editor.change_selections(Default::default(), window, cx, |s| {
19048                                s.select_ranges([rename_selection_range]);
19049                            });
19050                        }
19051                        editor
19052                    });
19053                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19054                        if e == &EditorEvent::Focused {
19055                            cx.emit(EditorEvent::FocusedIn)
19056                        }
19057                    })
19058                    .detach();
19059
19060                    let write_highlights =
19061                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19062                    let read_highlights =
19063                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19064                    let ranges = write_highlights
19065                        .iter()
19066                        .flat_map(|(_, ranges)| ranges.iter())
19067                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19068                        .cloned()
19069                        .collect();
19070
19071                    this.highlight_text(
19072                        HighlightKey::Rename,
19073                        ranges,
19074                        HighlightStyle {
19075                            fade_out: Some(0.6),
19076                            ..Default::default()
19077                        },
19078                        cx,
19079                    );
19080                    let rename_focus_handle = rename_editor.focus_handle(cx);
19081                    window.focus(&rename_focus_handle, cx);
19082                    let block_id = this.insert_blocks(
19083                        [BlockProperties {
19084                            style: BlockStyle::Flex,
19085                            placement: BlockPlacement::Below(range.start),
19086                            height: Some(1),
19087                            render: Arc::new({
19088                                let rename_editor = rename_editor.clone();
19089                                move |cx: &mut BlockContext| {
19090                                    let mut text_style = cx.editor_style.text.clone();
19091                                    if let Some(highlight_style) = old_highlight_id
19092                                        .and_then(|h| h.style(&cx.editor_style.syntax))
19093                                    {
19094                                        text_style = text_style.highlight(highlight_style);
19095                                    }
19096                                    div()
19097                                        .block_mouse_except_scroll()
19098                                        .pl(cx.anchor_x)
19099                                        .child(EditorElement::new(
19100                                            &rename_editor,
19101                                            EditorStyle {
19102                                                background: cx.theme().system().transparent,
19103                                                local_player: cx.editor_style.local_player,
19104                                                text: text_style,
19105                                                scrollbar_width: cx.editor_style.scrollbar_width,
19106                                                syntax: cx.editor_style.syntax.clone(),
19107                                                status: cx.editor_style.status.clone(),
19108                                                inlay_hints_style: HighlightStyle {
19109                                                    font_weight: Some(FontWeight::BOLD),
19110                                                    ..make_inlay_hints_style(cx.app)
19111                                                },
19112                                                edit_prediction_styles: make_suggestion_styles(
19113                                                    cx.app,
19114                                                ),
19115                                                ..EditorStyle::default()
19116                                            },
19117                                        ))
19118                                        .into_any_element()
19119                                }
19120                            }),
19121                            priority: 0,
19122                        }],
19123                        Some(Autoscroll::fit()),
19124                        cx,
19125                    )[0];
19126                    this.pending_rename = Some(RenameState {
19127                        range,
19128                        old_name,
19129                        editor: rename_editor,
19130                        block_id,
19131                    });
19132                })?;
19133            }
19134
19135            Ok(())
19136        }))
19137    }
19138
19139    pub fn confirm_rename(
19140        &mut self,
19141        _: &ConfirmRename,
19142        window: &mut Window,
19143        cx: &mut Context<Self>,
19144    ) -> Option<Task<Result<()>>> {
19145        let rename = self.take_rename(false, window, cx)?;
19146        let workspace = self.workspace()?.downgrade();
19147        let (buffer, start) = self
19148            .buffer
19149            .read(cx)
19150            .text_anchor_for_position(rename.range.start, cx)?;
19151        let (end_buffer, _) = self
19152            .buffer
19153            .read(cx)
19154            .text_anchor_for_position(rename.range.end, cx)?;
19155        if buffer != end_buffer {
19156            return None;
19157        }
19158
19159        let old_name = rename.old_name;
19160        let new_name = rename.editor.read(cx).text(cx);
19161
19162        let rename = self.semantics_provider.as_ref()?.perform_rename(
19163            &buffer,
19164            start,
19165            new_name.clone(),
19166            cx,
19167        )?;
19168
19169        Some(cx.spawn_in(window, async move |editor, cx| {
19170            let project_transaction = rename.await?;
19171            Self::open_project_transaction(
19172                &editor,
19173                workspace,
19174                project_transaction,
19175                format!("Rename: {}{}", old_name, new_name),
19176                cx,
19177            )
19178            .await?;
19179
19180            editor.update(cx, |editor, cx| {
19181                editor.refresh_document_highlights(cx);
19182            })?;
19183            Ok(())
19184        }))
19185    }
19186
19187    fn take_rename(
19188        &mut self,
19189        moving_cursor: bool,
19190        window: &mut Window,
19191        cx: &mut Context<Self>,
19192    ) -> Option<RenameState> {
19193        let rename = self.pending_rename.take()?;
19194        if rename.editor.focus_handle(cx).is_focused(window) {
19195            window.focus(&self.focus_handle, cx);
19196        }
19197
19198        self.remove_blocks(
19199            [rename.block_id].into_iter().collect(),
19200            Some(Autoscroll::fit()),
19201            cx,
19202        );
19203        self.clear_highlights(HighlightKey::Rename, cx);
19204        self.show_local_selections = true;
19205
19206        if moving_cursor {
19207            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19208                editor
19209                    .selections
19210                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19211                    .head()
19212            });
19213
19214            // Update the selection to match the position of the selection inside
19215            // the rename editor.
19216            let snapshot = self.buffer.read(cx).read(cx);
19217            let rename_range = rename.range.to_offset(&snapshot);
19218            let cursor_in_editor = snapshot
19219                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19220                .min(rename_range.end);
19221            drop(snapshot);
19222
19223            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19224                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19225            });
19226        } else {
19227            self.refresh_document_highlights(cx);
19228        }
19229
19230        Some(rename)
19231    }
19232
19233    pub fn pending_rename(&self) -> Option<&RenameState> {
19234        self.pending_rename.as_ref()
19235    }
19236
19237    fn format(
19238        &mut self,
19239        _: &Format,
19240        window: &mut Window,
19241        cx: &mut Context<Self>,
19242    ) -> Option<Task<Result<()>>> {
19243        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19244
19245        let project = match &self.project {
19246            Some(project) => project.clone(),
19247            None => return None,
19248        };
19249
19250        Some(self.perform_format(
19251            project,
19252            FormatTrigger::Manual,
19253            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19254            window,
19255            cx,
19256        ))
19257    }
19258
19259    fn format_selections(
19260        &mut self,
19261        _: &FormatSelections,
19262        window: &mut Window,
19263        cx: &mut Context<Self>,
19264    ) -> Option<Task<Result<()>>> {
19265        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19266
19267        let project = match &self.project {
19268            Some(project) => project.clone(),
19269            None => return None,
19270        };
19271
19272        let ranges = self
19273            .selections
19274            .all_adjusted(&self.display_snapshot(cx))
19275            .into_iter()
19276            .map(|selection| selection.range())
19277            .collect_vec();
19278
19279        Some(self.perform_format(
19280            project,
19281            FormatTrigger::Manual,
19282            FormatTarget::Ranges(ranges),
19283            window,
19284            cx,
19285        ))
19286    }
19287
19288    fn perform_format(
19289        &mut self,
19290        project: Entity<Project>,
19291        trigger: FormatTrigger,
19292        target: FormatTarget,
19293        window: &mut Window,
19294        cx: &mut Context<Self>,
19295    ) -> Task<Result<()>> {
19296        let buffer = self.buffer.clone();
19297        let (buffers, target) = match target {
19298            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19299            FormatTarget::Ranges(selection_ranges) => {
19300                let multi_buffer = buffer.read(cx);
19301                let snapshot = multi_buffer.read(cx);
19302                let mut buffers = HashSet::default();
19303                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19304                    BTreeMap::new();
19305                for selection_range in selection_ranges {
19306                    for (buffer, buffer_range, _) in
19307                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19308                    {
19309                        let buffer_id = buffer.remote_id();
19310                        let start = buffer.anchor_before(buffer_range.start);
19311                        let end = buffer.anchor_after(buffer_range.end);
19312                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19313                        buffer_id_to_ranges
19314                            .entry(buffer_id)
19315                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19316                            .or_insert_with(|| vec![start..end]);
19317                    }
19318                }
19319                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19320            }
19321        };
19322
19323        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19324        let selections_prev = transaction_id_prev
19325            .and_then(|transaction_id_prev| {
19326                // default to selections as they were after the last edit, if we have them,
19327                // instead of how they are now.
19328                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19329                // will take you back to where you made the last edit, instead of staying where you scrolled
19330                self.selection_history
19331                    .transaction(transaction_id_prev)
19332                    .map(|t| t.0.clone())
19333            })
19334            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19335
19336        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19337        let format = project.update(cx, |project, cx| {
19338            project.format(buffers, target, true, trigger, cx)
19339        });
19340
19341        cx.spawn_in(window, async move |editor, cx| {
19342            let transaction = futures::select_biased! {
19343                transaction = format.log_err().fuse() => transaction,
19344                () = timeout => {
19345                    log::warn!("timed out waiting for formatting");
19346                    None
19347                }
19348            };
19349
19350            buffer.update(cx, |buffer, cx| {
19351                if let Some(transaction) = transaction
19352                    && !buffer.is_singleton()
19353                {
19354                    buffer.push_transaction(&transaction.0, cx);
19355                }
19356                cx.notify();
19357            });
19358
19359            if let Some(transaction_id_now) =
19360                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19361            {
19362                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19363                if has_new_transaction {
19364                    editor
19365                        .update(cx, |editor, _| {
19366                            editor
19367                                .selection_history
19368                                .insert_transaction(transaction_id_now, selections_prev);
19369                        })
19370                        .ok();
19371                }
19372            }
19373
19374            Ok(())
19375        })
19376    }
19377
19378    fn organize_imports(
19379        &mut self,
19380        _: &OrganizeImports,
19381        window: &mut Window,
19382        cx: &mut Context<Self>,
19383    ) -> Option<Task<Result<()>>> {
19384        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19385        let project = match &self.project {
19386            Some(project) => project.clone(),
19387            None => return None,
19388        };
19389        Some(self.perform_code_action_kind(
19390            project,
19391            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19392            window,
19393            cx,
19394        ))
19395    }
19396
19397    fn perform_code_action_kind(
19398        &mut self,
19399        project: Entity<Project>,
19400        kind: CodeActionKind,
19401        window: &mut Window,
19402        cx: &mut Context<Self>,
19403    ) -> Task<Result<()>> {
19404        let buffer = self.buffer.clone();
19405        let buffers = buffer.read(cx).all_buffers();
19406        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19407        let apply_action = project.update(cx, |project, cx| {
19408            project.apply_code_action_kind(buffers, kind, true, cx)
19409        });
19410        cx.spawn_in(window, async move |_, cx| {
19411            let transaction = futures::select_biased! {
19412                () = timeout => {
19413                    log::warn!("timed out waiting for executing code action");
19414                    None
19415                }
19416                transaction = apply_action.log_err().fuse() => transaction,
19417            };
19418            buffer.update(cx, |buffer, cx| {
19419                // check if we need this
19420                if let Some(transaction) = transaction
19421                    && !buffer.is_singleton()
19422                {
19423                    buffer.push_transaction(&transaction.0, cx);
19424                }
19425                cx.notify();
19426            });
19427            Ok(())
19428        })
19429    }
19430
19431    pub fn restart_language_server(
19432        &mut self,
19433        _: &RestartLanguageServer,
19434        _: &mut Window,
19435        cx: &mut Context<Self>,
19436    ) {
19437        if let Some(project) = self.project.clone() {
19438            self.buffer.update(cx, |multi_buffer, cx| {
19439                project.update(cx, |project, cx| {
19440                    project.restart_language_servers_for_buffers(
19441                        multi_buffer.all_buffers().into_iter().collect(),
19442                        HashSet::default(),
19443                        cx,
19444                    );
19445                });
19446            })
19447        }
19448    }
19449
19450    pub fn stop_language_server(
19451        &mut self,
19452        _: &StopLanguageServer,
19453        _: &mut Window,
19454        cx: &mut Context<Self>,
19455    ) {
19456        if let Some(project) = self.project.clone() {
19457            self.buffer.update(cx, |multi_buffer, cx| {
19458                project.update(cx, |project, cx| {
19459                    project.stop_language_servers_for_buffers(
19460                        multi_buffer.all_buffers().into_iter().collect(),
19461                        HashSet::default(),
19462                        cx,
19463                    );
19464                });
19465            });
19466        }
19467    }
19468
19469    fn cancel_language_server_work(
19470        workspace: &mut Workspace,
19471        _: &actions::CancelLanguageServerWork,
19472        _: &mut Window,
19473        cx: &mut Context<Workspace>,
19474    ) {
19475        let project = workspace.project();
19476        let buffers = workspace
19477            .active_item(cx)
19478            .and_then(|item| item.act_as::<Editor>(cx))
19479            .map_or(HashSet::default(), |editor| {
19480                editor.read(cx).buffer.read(cx).all_buffers()
19481            });
19482        project.update(cx, |project, cx| {
19483            project.cancel_language_server_work_for_buffers(buffers, cx);
19484        });
19485    }
19486
19487    fn show_character_palette(
19488        &mut self,
19489        _: &ShowCharacterPalette,
19490        window: &mut Window,
19491        _: &mut Context<Self>,
19492    ) {
19493        window.show_character_palette();
19494    }
19495
19496    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19497        if !self.diagnostics_enabled() {
19498            return;
19499        }
19500
19501        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19502            let buffer = self.buffer.read(cx).snapshot(cx);
19503            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19504            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19505            let is_valid = buffer
19506                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19507                .any(|entry| {
19508                    entry.diagnostic.is_primary
19509                        && !entry.range.is_empty()
19510                        && entry.range.start == primary_range_start
19511                        && entry.diagnostic.message == active_diagnostics.active_message
19512                });
19513
19514            if !is_valid {
19515                self.dismiss_diagnostics(cx);
19516            }
19517        }
19518    }
19519
19520    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19521        match &self.active_diagnostics {
19522            ActiveDiagnostic::Group(group) => Some(group),
19523            _ => None,
19524        }
19525    }
19526
19527    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19528        if !self.diagnostics_enabled() {
19529            return;
19530        }
19531        self.dismiss_diagnostics(cx);
19532        self.active_diagnostics = ActiveDiagnostic::All;
19533    }
19534
19535    fn activate_diagnostics(
19536        &mut self,
19537        buffer_id: BufferId,
19538        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19539        window: &mut Window,
19540        cx: &mut Context<Self>,
19541    ) {
19542        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19543            return;
19544        }
19545        self.dismiss_diagnostics(cx);
19546        let snapshot = self.snapshot(window, cx);
19547        let buffer = self.buffer.read(cx).snapshot(cx);
19548        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19549            return;
19550        };
19551
19552        let diagnostic_group = buffer
19553            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19554            .collect::<Vec<_>>();
19555
19556        let language_registry = self
19557            .project()
19558            .map(|project| project.read(cx).languages().clone());
19559
19560        let blocks = renderer.render_group(
19561            diagnostic_group,
19562            buffer_id,
19563            snapshot,
19564            cx.weak_entity(),
19565            language_registry,
19566            cx,
19567        );
19568
19569        let blocks = self.display_map.update(cx, |display_map, cx| {
19570            display_map.insert_blocks(blocks, cx).into_iter().collect()
19571        });
19572        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19573            active_range: buffer.anchor_before(diagnostic.range.start)
19574                ..buffer.anchor_after(diagnostic.range.end),
19575            active_message: diagnostic.diagnostic.message.clone(),
19576            group_id: diagnostic.diagnostic.group_id,
19577            blocks,
19578        });
19579        cx.notify();
19580    }
19581
19582    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19583        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19584            return;
19585        };
19586
19587        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19588        if let ActiveDiagnostic::Group(group) = prev {
19589            self.display_map.update(cx, |display_map, cx| {
19590                display_map.remove_blocks(group.blocks, cx);
19591            });
19592            cx.notify();
19593        }
19594    }
19595
19596    /// Disable inline diagnostics rendering for this editor.
19597    pub fn disable_inline_diagnostics(&mut self) {
19598        self.inline_diagnostics_enabled = false;
19599        self.inline_diagnostics_update = Task::ready(());
19600        self.inline_diagnostics.clear();
19601    }
19602
19603    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19604        self.diagnostics_enabled = false;
19605        self.dismiss_diagnostics(cx);
19606        self.inline_diagnostics_update = Task::ready(());
19607        self.inline_diagnostics.clear();
19608    }
19609
19610    pub fn disable_word_completions(&mut self) {
19611        self.word_completions_enabled = false;
19612    }
19613
19614    pub fn diagnostics_enabled(&self) -> bool {
19615        self.diagnostics_enabled && self.lsp_data_enabled()
19616    }
19617
19618    pub fn inline_diagnostics_enabled(&self) -> bool {
19619        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19620    }
19621
19622    pub fn show_inline_diagnostics(&self) -> bool {
19623        self.show_inline_diagnostics
19624    }
19625
19626    pub fn toggle_inline_diagnostics(
19627        &mut self,
19628        _: &ToggleInlineDiagnostics,
19629        window: &mut Window,
19630        cx: &mut Context<Editor>,
19631    ) {
19632        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19633        self.refresh_inline_diagnostics(false, window, cx);
19634    }
19635
19636    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19637        self.diagnostics_max_severity = severity;
19638        self.display_map.update(cx, |display_map, _| {
19639            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19640        });
19641    }
19642
19643    pub fn toggle_diagnostics(
19644        &mut self,
19645        _: &ToggleDiagnostics,
19646        window: &mut Window,
19647        cx: &mut Context<Editor>,
19648    ) {
19649        if !self.diagnostics_enabled() {
19650            return;
19651        }
19652
19653        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19654            EditorSettings::get_global(cx)
19655                .diagnostics_max_severity
19656                .filter(|severity| severity != &DiagnosticSeverity::Off)
19657                .unwrap_or(DiagnosticSeverity::Hint)
19658        } else {
19659            DiagnosticSeverity::Off
19660        };
19661        self.set_max_diagnostics_severity(new_severity, cx);
19662        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19663            self.active_diagnostics = ActiveDiagnostic::None;
19664            self.inline_diagnostics_update = Task::ready(());
19665            self.inline_diagnostics.clear();
19666        } else {
19667            self.refresh_inline_diagnostics(false, window, cx);
19668        }
19669
19670        cx.notify();
19671    }
19672
19673    pub fn toggle_minimap(
19674        &mut self,
19675        _: &ToggleMinimap,
19676        window: &mut Window,
19677        cx: &mut Context<Editor>,
19678    ) {
19679        if self.supports_minimap(cx) {
19680            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19681        }
19682    }
19683
19684    fn refresh_inline_diagnostics(
19685        &mut self,
19686        debounce: bool,
19687        window: &mut Window,
19688        cx: &mut Context<Self>,
19689    ) {
19690        let max_severity = ProjectSettings::get_global(cx)
19691            .diagnostics
19692            .inline
19693            .max_severity
19694            .unwrap_or(self.diagnostics_max_severity);
19695
19696        if !self.inline_diagnostics_enabled()
19697            || !self.diagnostics_enabled()
19698            || !self.show_inline_diagnostics
19699            || max_severity == DiagnosticSeverity::Off
19700        {
19701            self.inline_diagnostics_update = Task::ready(());
19702            self.inline_diagnostics.clear();
19703            return;
19704        }
19705
19706        let debounce_ms = ProjectSettings::get_global(cx)
19707            .diagnostics
19708            .inline
19709            .update_debounce_ms;
19710        let debounce = if debounce && debounce_ms > 0 {
19711            Some(Duration::from_millis(debounce_ms))
19712        } else {
19713            None
19714        };
19715        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19716            if let Some(debounce) = debounce {
19717                cx.background_executor().timer(debounce).await;
19718            }
19719            let Some(snapshot) = editor.upgrade().map(|editor| {
19720                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19721            }) else {
19722                return;
19723            };
19724
19725            let new_inline_diagnostics = cx
19726                .background_spawn(async move {
19727                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19728                    for diagnostic_entry in
19729                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19730                    {
19731                        let message = diagnostic_entry
19732                            .diagnostic
19733                            .message
19734                            .split_once('\n')
19735                            .map(|(line, _)| line)
19736                            .map(SharedString::new)
19737                            .unwrap_or_else(|| {
19738                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19739                            });
19740                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19741                        let (Ok(i) | Err(i)) = inline_diagnostics
19742                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19743                        inline_diagnostics.insert(
19744                            i,
19745                            (
19746                                start_anchor,
19747                                InlineDiagnostic {
19748                                    message,
19749                                    group_id: diagnostic_entry.diagnostic.group_id,
19750                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19751                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19752                                    severity: diagnostic_entry.diagnostic.severity,
19753                                },
19754                            ),
19755                        );
19756                    }
19757                    inline_diagnostics
19758                })
19759                .await;
19760
19761            editor
19762                .update(cx, |editor, cx| {
19763                    editor.inline_diagnostics = new_inline_diagnostics;
19764                    cx.notify();
19765                })
19766                .ok();
19767        });
19768    }
19769
19770    fn pull_diagnostics(
19771        &mut self,
19772        buffer_id: BufferId,
19773        _window: &Window,
19774        cx: &mut Context<Self>,
19775    ) -> Option<()> {
19776        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19777        // skip any LSP updates for it.
19778
19779        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19780            return None;
19781        }
19782        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19783            .diagnostics
19784            .lsp_pull_diagnostics;
19785        if !pull_diagnostics_settings.enabled {
19786            return None;
19787        }
19788        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19789        let project = self.project()?.downgrade();
19790        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19791
19792        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19793            cx.background_executor().timer(debounce).await;
19794            if let Ok(task) = project.update(cx, |project, cx| {
19795                project.lsp_store().update(cx, |lsp_store, cx| {
19796                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19797                })
19798            }) {
19799                task.await.log_err();
19800            }
19801            project
19802                .update(cx, |project, cx| {
19803                    project.lsp_store().update(cx, |lsp_store, cx| {
19804                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19805                    })
19806                })
19807                .log_err();
19808        });
19809
19810        Some(())
19811    }
19812
19813    pub fn set_selections_from_remote(
19814        &mut self,
19815        selections: Vec<Selection<Anchor>>,
19816        pending_selection: Option<Selection<Anchor>>,
19817        window: &mut Window,
19818        cx: &mut Context<Self>,
19819    ) {
19820        let old_cursor_position = self.selections.newest_anchor().head();
19821        self.selections
19822            .change_with(&self.display_snapshot(cx), |s| {
19823                s.select_anchors(selections);
19824                if let Some(pending_selection) = pending_selection {
19825                    s.set_pending(pending_selection, SelectMode::Character);
19826                } else {
19827                    s.clear_pending();
19828                }
19829            });
19830        self.selections_did_change(
19831            false,
19832            &old_cursor_position,
19833            SelectionEffects::default(),
19834            window,
19835            cx,
19836        );
19837    }
19838
19839    pub fn transact(
19840        &mut self,
19841        window: &mut Window,
19842        cx: &mut Context<Self>,
19843        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19844    ) -> Option<TransactionId> {
19845        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19846            this.start_transaction_at(Instant::now(), window, cx);
19847            update(this, window, cx);
19848            this.end_transaction_at(Instant::now(), cx)
19849        })
19850    }
19851
19852    pub fn start_transaction_at(
19853        &mut self,
19854        now: Instant,
19855        window: &mut Window,
19856        cx: &mut Context<Self>,
19857    ) -> Option<TransactionId> {
19858        self.end_selection(window, cx);
19859        if let Some(tx_id) = self
19860            .buffer
19861            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19862        {
19863            self.selection_history
19864                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19865            cx.emit(EditorEvent::TransactionBegun {
19866                transaction_id: tx_id,
19867            });
19868            Some(tx_id)
19869        } else {
19870            None
19871        }
19872    }
19873
19874    pub fn end_transaction_at(
19875        &mut self,
19876        now: Instant,
19877        cx: &mut Context<Self>,
19878    ) -> Option<TransactionId> {
19879        if let Some(transaction_id) = self
19880            .buffer
19881            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19882        {
19883            if let Some((_, end_selections)) =
19884                self.selection_history.transaction_mut(transaction_id)
19885            {
19886                *end_selections = Some(self.selections.disjoint_anchors_arc());
19887            } else {
19888                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19889            }
19890
19891            cx.emit(EditorEvent::Edited { transaction_id });
19892            Some(transaction_id)
19893        } else {
19894            None
19895        }
19896    }
19897
19898    pub fn modify_transaction_selection_history(
19899        &mut self,
19900        transaction_id: TransactionId,
19901        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19902    ) -> bool {
19903        self.selection_history
19904            .transaction_mut(transaction_id)
19905            .map(modify)
19906            .is_some()
19907    }
19908
19909    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19910        if self.selection_mark_mode {
19911            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19912                s.move_with(&mut |_, sel| {
19913                    sel.collapse_to(sel.head(), SelectionGoal::None);
19914                });
19915            })
19916        }
19917        self.selection_mark_mode = true;
19918        cx.notify();
19919    }
19920
19921    pub fn swap_selection_ends(
19922        &mut self,
19923        _: &actions::SwapSelectionEnds,
19924        window: &mut Window,
19925        cx: &mut Context<Self>,
19926    ) {
19927        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19928            s.move_with(&mut |_, sel| {
19929                if sel.start != sel.end {
19930                    sel.reversed = !sel.reversed
19931                }
19932            });
19933        });
19934        self.request_autoscroll(Autoscroll::newest(), cx);
19935        cx.notify();
19936    }
19937
19938    pub fn toggle_focus(
19939        workspace: &mut Workspace,
19940        _: &actions::ToggleFocus,
19941        window: &mut Window,
19942        cx: &mut Context<Workspace>,
19943    ) {
19944        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19945            return;
19946        };
19947        workspace.activate_item(&item, true, true, window, cx);
19948    }
19949
19950    pub fn toggle_fold(
19951        &mut self,
19952        _: &actions::ToggleFold,
19953        window: &mut Window,
19954        cx: &mut Context<Self>,
19955    ) {
19956        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19957            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19958            let selection = self.selections.newest::<Point>(&display_map);
19959
19960            let range = if selection.is_empty() {
19961                let point = selection.head().to_display_point(&display_map);
19962                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19963                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19964                    .to_point(&display_map);
19965                start..end
19966            } else {
19967                selection.range()
19968            };
19969            if display_map.folds_in_range(range).next().is_some() {
19970                self.unfold_lines(&Default::default(), window, cx)
19971            } else {
19972                self.fold(&Default::default(), window, cx)
19973            }
19974        } else {
19975            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19976            let buffer_ids: HashSet<_> = self
19977                .selections
19978                .disjoint_anchor_ranges()
19979                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19980                .collect();
19981
19982            let should_unfold = buffer_ids
19983                .iter()
19984                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19985
19986            for buffer_id in buffer_ids {
19987                if should_unfold {
19988                    self.unfold_buffer(buffer_id, cx);
19989                } else {
19990                    self.fold_buffer(buffer_id, cx);
19991                }
19992            }
19993        }
19994    }
19995
19996    pub fn toggle_fold_recursive(
19997        &mut self,
19998        _: &actions::ToggleFoldRecursive,
19999        window: &mut Window,
20000        cx: &mut Context<Self>,
20001    ) {
20002        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
20003
20004        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20005        let range = if selection.is_empty() {
20006            let point = selection.head().to_display_point(&display_map);
20007            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20008            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20009                .to_point(&display_map);
20010            start..end
20011        } else {
20012            selection.range()
20013        };
20014        if display_map.folds_in_range(range).next().is_some() {
20015            self.unfold_recursive(&Default::default(), window, cx)
20016        } else {
20017            self.fold_recursive(&Default::default(), window, cx)
20018        }
20019    }
20020
20021    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
20022        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20023            let mut to_fold = Vec::new();
20024            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20025            let selections = self.selections.all_adjusted(&display_map);
20026
20027            for selection in selections {
20028                let range = selection.range().sorted();
20029                let buffer_start_row = range.start.row;
20030
20031                if range.start.row != range.end.row {
20032                    let mut found = false;
20033                    let mut row = range.start.row;
20034                    while row <= range.end.row {
20035                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20036                        {
20037                            found = true;
20038                            row = crease.range().end.row + 1;
20039                            to_fold.push(crease);
20040                        } else {
20041                            row += 1
20042                        }
20043                    }
20044                    if found {
20045                        continue;
20046                    }
20047                }
20048
20049                for row in (0..=range.start.row).rev() {
20050                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20051                        && crease.range().end.row >= buffer_start_row
20052                    {
20053                        to_fold.push(crease);
20054                        if row <= range.start.row {
20055                            break;
20056                        }
20057                    }
20058                }
20059            }
20060
20061            self.fold_creases(to_fold, true, window, cx);
20062        } else {
20063            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20064            let buffer_ids = self
20065                .selections
20066                .disjoint_anchor_ranges()
20067                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20068                .collect::<HashSet<_>>();
20069            for buffer_id in buffer_ids {
20070                self.fold_buffer(buffer_id, cx);
20071            }
20072        }
20073    }
20074
20075    pub fn toggle_fold_all(
20076        &mut self,
20077        _: &actions::ToggleFoldAll,
20078        window: &mut Window,
20079        cx: &mut Context<Self>,
20080    ) {
20081        let has_folds = if self.buffer.read(cx).is_singleton() {
20082            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20083            let has_folds = display_map
20084                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20085                .next()
20086                .is_some();
20087            has_folds
20088        } else {
20089            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20090            let has_folds = buffer_ids
20091                .iter()
20092                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20093            has_folds
20094        };
20095
20096        if has_folds {
20097            self.unfold_all(&actions::UnfoldAll, window, cx);
20098        } else {
20099            self.fold_all(&actions::FoldAll, window, cx);
20100        }
20101    }
20102
20103    fn fold_at_level(
20104        &mut self,
20105        fold_at: &FoldAtLevel,
20106        window: &mut Window,
20107        cx: &mut Context<Self>,
20108    ) {
20109        if !self.buffer.read(cx).is_singleton() {
20110            return;
20111        }
20112
20113        let fold_at_level = fold_at.0;
20114        let snapshot = self.buffer.read(cx).snapshot(cx);
20115        let mut to_fold = Vec::new();
20116        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20117
20118        let row_ranges_to_keep: Vec<Range<u32>> = self
20119            .selections
20120            .all::<Point>(&self.display_snapshot(cx))
20121            .into_iter()
20122            .map(|sel| sel.start.row..sel.end.row)
20123            .collect();
20124
20125        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20126            while start_row < end_row {
20127                match self
20128                    .snapshot(window, cx)
20129                    .crease_for_buffer_row(MultiBufferRow(start_row))
20130                {
20131                    Some(crease) => {
20132                        let nested_start_row = crease.range().start.row + 1;
20133                        let nested_end_row = crease.range().end.row;
20134
20135                        if current_level < fold_at_level {
20136                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20137                        } else if current_level == fold_at_level {
20138                            // Fold iff there is no selection completely contained within the fold region
20139                            if !row_ranges_to_keep.iter().any(|selection| {
20140                                selection.end >= nested_start_row
20141                                    && selection.start <= nested_end_row
20142                            }) {
20143                                to_fold.push(crease);
20144                            }
20145                        }
20146
20147                        start_row = nested_end_row + 1;
20148                    }
20149                    None => start_row += 1,
20150                }
20151            }
20152        }
20153
20154        self.fold_creases(to_fold, true, window, cx);
20155    }
20156
20157    pub fn fold_at_level_1(
20158        &mut self,
20159        _: &actions::FoldAtLevel1,
20160        window: &mut Window,
20161        cx: &mut Context<Self>,
20162    ) {
20163        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20164    }
20165
20166    pub fn fold_at_level_2(
20167        &mut self,
20168        _: &actions::FoldAtLevel2,
20169        window: &mut Window,
20170        cx: &mut Context<Self>,
20171    ) {
20172        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20173    }
20174
20175    pub fn fold_at_level_3(
20176        &mut self,
20177        _: &actions::FoldAtLevel3,
20178        window: &mut Window,
20179        cx: &mut Context<Self>,
20180    ) {
20181        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20182    }
20183
20184    pub fn fold_at_level_4(
20185        &mut self,
20186        _: &actions::FoldAtLevel4,
20187        window: &mut Window,
20188        cx: &mut Context<Self>,
20189    ) {
20190        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20191    }
20192
20193    pub fn fold_at_level_5(
20194        &mut self,
20195        _: &actions::FoldAtLevel5,
20196        window: &mut Window,
20197        cx: &mut Context<Self>,
20198    ) {
20199        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20200    }
20201
20202    pub fn fold_at_level_6(
20203        &mut self,
20204        _: &actions::FoldAtLevel6,
20205        window: &mut Window,
20206        cx: &mut Context<Self>,
20207    ) {
20208        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20209    }
20210
20211    pub fn fold_at_level_7(
20212        &mut self,
20213        _: &actions::FoldAtLevel7,
20214        window: &mut Window,
20215        cx: &mut Context<Self>,
20216    ) {
20217        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20218    }
20219
20220    pub fn fold_at_level_8(
20221        &mut self,
20222        _: &actions::FoldAtLevel8,
20223        window: &mut Window,
20224        cx: &mut Context<Self>,
20225    ) {
20226        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20227    }
20228
20229    pub fn fold_at_level_9(
20230        &mut self,
20231        _: &actions::FoldAtLevel9,
20232        window: &mut Window,
20233        cx: &mut Context<Self>,
20234    ) {
20235        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20236    }
20237
20238    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20239        if self.buffer.read(cx).is_singleton() {
20240            let mut fold_ranges = Vec::new();
20241            let snapshot = self.buffer.read(cx).snapshot(cx);
20242
20243            for row in 0..snapshot.max_row().0 {
20244                if let Some(foldable_range) = self
20245                    .snapshot(window, cx)
20246                    .crease_for_buffer_row(MultiBufferRow(row))
20247                {
20248                    fold_ranges.push(foldable_range);
20249                }
20250            }
20251
20252            self.fold_creases(fold_ranges, true, window, cx);
20253        } else {
20254            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20255                editor
20256                    .update_in(cx, |editor, _, cx| {
20257                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20258                            editor.fold_buffer(buffer_id, cx);
20259                        }
20260                    })
20261                    .ok();
20262            });
20263        }
20264    }
20265
20266    pub fn fold_function_bodies(
20267        &mut self,
20268        _: &actions::FoldFunctionBodies,
20269        window: &mut Window,
20270        cx: &mut Context<Self>,
20271    ) {
20272        let snapshot = self.buffer.read(cx).snapshot(cx);
20273
20274        let ranges = snapshot
20275            .text_object_ranges(
20276                MultiBufferOffset(0)..snapshot.len(),
20277                TreeSitterOptions::default(),
20278            )
20279            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20280            .collect::<Vec<_>>();
20281
20282        let creases = ranges
20283            .into_iter()
20284            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20285            .collect();
20286
20287        self.fold_creases(creases, true, window, cx);
20288    }
20289
20290    pub fn fold_recursive(
20291        &mut self,
20292        _: &actions::FoldRecursive,
20293        window: &mut Window,
20294        cx: &mut Context<Self>,
20295    ) {
20296        let mut to_fold = Vec::new();
20297        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20298        let selections = self.selections.all_adjusted(&display_map);
20299
20300        for selection in selections {
20301            let range = selection.range().sorted();
20302            let buffer_start_row = range.start.row;
20303
20304            if range.start.row != range.end.row {
20305                let mut found = false;
20306                for row in range.start.row..=range.end.row {
20307                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20308                        found = true;
20309                        to_fold.push(crease);
20310                    }
20311                }
20312                if found {
20313                    continue;
20314                }
20315            }
20316
20317            for row in (0..=range.start.row).rev() {
20318                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20319                    if crease.range().end.row >= buffer_start_row {
20320                        to_fold.push(crease);
20321                    } else {
20322                        break;
20323                    }
20324                }
20325            }
20326        }
20327
20328        self.fold_creases(to_fold, true, window, cx);
20329    }
20330
20331    pub fn fold_at(
20332        &mut self,
20333        buffer_row: MultiBufferRow,
20334        window: &mut Window,
20335        cx: &mut Context<Self>,
20336    ) {
20337        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20338
20339        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20340            let autoscroll = self
20341                .selections
20342                .all::<Point>(&display_map)
20343                .iter()
20344                .any(|selection| crease.range().overlaps(&selection.range()));
20345
20346            self.fold_creases(vec![crease], autoscroll, window, cx);
20347        }
20348    }
20349
20350    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20351        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20352            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20353            let buffer = display_map.buffer_snapshot();
20354            let selections = self.selections.all::<Point>(&display_map);
20355            let ranges = selections
20356                .iter()
20357                .map(|s| {
20358                    let range = s.display_range(&display_map).sorted();
20359                    let mut start = range.start.to_point(&display_map);
20360                    let mut end = range.end.to_point(&display_map);
20361                    start.column = 0;
20362                    end.column = buffer.line_len(MultiBufferRow(end.row));
20363                    start..end
20364                })
20365                .collect::<Vec<_>>();
20366
20367            self.unfold_ranges(&ranges, true, true, cx);
20368        } else {
20369            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20370            let buffer_ids = self
20371                .selections
20372                .disjoint_anchor_ranges()
20373                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20374                .collect::<HashSet<_>>();
20375            for buffer_id in buffer_ids {
20376                self.unfold_buffer(buffer_id, cx);
20377            }
20378        }
20379    }
20380
20381    pub fn unfold_recursive(
20382        &mut self,
20383        _: &UnfoldRecursive,
20384        _window: &mut Window,
20385        cx: &mut Context<Self>,
20386    ) {
20387        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20388        let selections = self.selections.all::<Point>(&display_map);
20389        let ranges = selections
20390            .iter()
20391            .map(|s| {
20392                let mut range = s.display_range(&display_map).sorted();
20393                *range.start.column_mut() = 0;
20394                *range.end.column_mut() = display_map.line_len(range.end.row());
20395                let start = range.start.to_point(&display_map);
20396                let end = range.end.to_point(&display_map);
20397                start..end
20398            })
20399            .collect::<Vec<_>>();
20400
20401        self.unfold_ranges(&ranges, true, true, cx);
20402    }
20403
20404    pub fn unfold_at(
20405        &mut self,
20406        buffer_row: MultiBufferRow,
20407        _window: &mut Window,
20408        cx: &mut Context<Self>,
20409    ) {
20410        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20411
20412        let intersection_range = Point::new(buffer_row.0, 0)
20413            ..Point::new(
20414                buffer_row.0,
20415                display_map.buffer_snapshot().line_len(buffer_row),
20416            );
20417
20418        let autoscroll = self
20419            .selections
20420            .all::<Point>(&display_map)
20421            .iter()
20422            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20423
20424        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20425    }
20426
20427    pub fn unfold_all(
20428        &mut self,
20429        _: &actions::UnfoldAll,
20430        _window: &mut Window,
20431        cx: &mut Context<Self>,
20432    ) {
20433        if self.buffer.read(cx).is_singleton() {
20434            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20435            self.unfold_ranges(
20436                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20437                true,
20438                true,
20439                cx,
20440            );
20441        } else {
20442            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20443                editor
20444                    .update(cx, |editor, cx| {
20445                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20446                            editor.unfold_buffer(buffer_id, cx);
20447                        }
20448                    })
20449                    .ok();
20450            });
20451        }
20452    }
20453
20454    pub fn fold_selected_ranges(
20455        &mut self,
20456        _: &FoldSelectedRanges,
20457        window: &mut Window,
20458        cx: &mut Context<Self>,
20459    ) {
20460        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20461        let selections = self.selections.all_adjusted(&display_map);
20462        let ranges = selections
20463            .into_iter()
20464            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20465            .collect::<Vec<_>>();
20466        self.fold_creases(ranges, true, window, cx);
20467    }
20468
20469    pub fn fold_ranges<T: ToOffset + Clone>(
20470        &mut self,
20471        ranges: Vec<Range<T>>,
20472        auto_scroll: bool,
20473        window: &mut Window,
20474        cx: &mut Context<Self>,
20475    ) {
20476        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20477        let ranges = ranges
20478            .into_iter()
20479            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20480            .collect::<Vec<_>>();
20481        self.fold_creases(ranges, auto_scroll, window, cx);
20482    }
20483
20484    pub fn fold_creases<T: ToOffset + Clone>(
20485        &mut self,
20486        creases: Vec<Crease<T>>,
20487        auto_scroll: bool,
20488        _window: &mut Window,
20489        cx: &mut Context<Self>,
20490    ) {
20491        if creases.is_empty() {
20492            return;
20493        }
20494
20495        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20496
20497        if auto_scroll {
20498            self.request_autoscroll(Autoscroll::fit(), cx);
20499        }
20500
20501        cx.notify();
20502
20503        self.scrollbar_marker_state.dirty = true;
20504        self.folds_did_change(cx);
20505    }
20506
20507    /// Removes any folds whose ranges intersect any of the given ranges.
20508    pub fn unfold_ranges<T: ToOffset + Clone>(
20509        &mut self,
20510        ranges: &[Range<T>],
20511        inclusive: bool,
20512        auto_scroll: bool,
20513        cx: &mut Context<Self>,
20514    ) {
20515        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20516            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20517        });
20518        self.folds_did_change(cx);
20519    }
20520
20521    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20522        self.fold_buffers([buffer_id], cx);
20523    }
20524
20525    pub fn fold_buffers(
20526        &mut self,
20527        buffer_ids: impl IntoIterator<Item = BufferId>,
20528        cx: &mut Context<Self>,
20529    ) {
20530        if self.buffer().read(cx).is_singleton() {
20531            return;
20532        }
20533
20534        let ids_to_fold: Vec<BufferId> = buffer_ids
20535            .into_iter()
20536            .filter(|id| !self.is_buffer_folded(*id, cx))
20537            .collect();
20538
20539        if ids_to_fold.is_empty() {
20540            return;
20541        }
20542
20543        let mut all_folded_excerpt_ids = Vec::new();
20544        for buffer_id in &ids_to_fold {
20545            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20546            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20547        }
20548
20549        self.display_map.update(cx, |display_map, cx| {
20550            display_map.fold_buffers(ids_to_fold.clone(), cx)
20551        });
20552
20553        let snapshot = self.display_snapshot(cx);
20554        self.selections.change_with(&snapshot, |selections| {
20555            for buffer_id in ids_to_fold {
20556                selections.remove_selections_from_buffer(buffer_id);
20557            }
20558        });
20559
20560        cx.emit(EditorEvent::BufferFoldToggled {
20561            ids: all_folded_excerpt_ids,
20562            folded: true,
20563        });
20564        cx.notify();
20565    }
20566
20567    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20568        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20569            return;
20570        }
20571        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20572        self.display_map.update(cx, |display_map, cx| {
20573            display_map.unfold_buffers([buffer_id], cx);
20574        });
20575        cx.emit(EditorEvent::BufferFoldToggled {
20576            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20577            folded: false,
20578        });
20579        cx.notify();
20580    }
20581
20582    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20583        self.display_map.read(cx).is_buffer_folded(buffer)
20584    }
20585
20586    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20587        if self.buffer().read(cx).is_singleton() {
20588            return false;
20589        }
20590        !self.folded_buffers(cx).is_empty()
20591    }
20592
20593    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20594        self.display_map.read(cx).folded_buffers()
20595    }
20596
20597    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20598        self.display_map.update(cx, |display_map, cx| {
20599            display_map.disable_header_for_buffer(buffer_id, cx);
20600        });
20601        cx.notify();
20602    }
20603
20604    /// Removes any folds with the given ranges.
20605    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20606        &mut self,
20607        ranges: &[Range<T>],
20608        type_id: TypeId,
20609        auto_scroll: bool,
20610        cx: &mut Context<Self>,
20611    ) {
20612        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20613            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20614        });
20615        self.folds_did_change(cx);
20616    }
20617
20618    fn remove_folds_with<T: ToOffset + Clone>(
20619        &mut self,
20620        ranges: &[Range<T>],
20621        auto_scroll: bool,
20622        cx: &mut Context<Self>,
20623        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20624    ) {
20625        if ranges.is_empty() {
20626            return;
20627        }
20628
20629        let mut buffers_affected = HashSet::default();
20630        let multi_buffer = self.buffer().read(cx);
20631        for range in ranges {
20632            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20633                buffers_affected.insert(buffer.read(cx).remote_id());
20634            };
20635        }
20636
20637        self.display_map.update(cx, update);
20638
20639        if auto_scroll {
20640            self.request_autoscroll(Autoscroll::fit(), cx);
20641        }
20642
20643        cx.notify();
20644        self.scrollbar_marker_state.dirty = true;
20645        self.active_indent_guides_state.dirty = true;
20646    }
20647
20648    pub fn update_renderer_widths(
20649        &mut self,
20650        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20651        cx: &mut Context<Self>,
20652    ) -> bool {
20653        self.display_map
20654            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20655    }
20656
20657    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20658        self.display_map.read(cx).fold_placeholder.clone()
20659    }
20660
20661    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20662        self.buffer.update(cx, |buffer, cx| {
20663            buffer.set_all_diff_hunks_expanded(cx);
20664        });
20665    }
20666
20667    pub fn expand_all_diff_hunks(
20668        &mut self,
20669        _: &ExpandAllDiffHunks,
20670        _window: &mut Window,
20671        cx: &mut Context<Self>,
20672    ) {
20673        self.buffer.update(cx, |buffer, cx| {
20674            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20675        });
20676    }
20677
20678    pub fn collapse_all_diff_hunks(
20679        &mut self,
20680        _: &CollapseAllDiffHunks,
20681        _window: &mut Window,
20682        cx: &mut Context<Self>,
20683    ) {
20684        self.buffer.update(cx, |buffer, cx| {
20685            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20686        });
20687    }
20688
20689    pub fn toggle_selected_diff_hunks(
20690        &mut self,
20691        _: &ToggleSelectedDiffHunks,
20692        _window: &mut Window,
20693        cx: &mut Context<Self>,
20694    ) {
20695        let ranges: Vec<_> = self
20696            .selections
20697            .disjoint_anchors()
20698            .iter()
20699            .map(|s| s.range())
20700            .collect();
20701        self.toggle_diff_hunks_in_ranges(ranges, cx);
20702    }
20703
20704    pub fn diff_hunks_in_ranges<'a>(
20705        &'a self,
20706        ranges: &'a [Range<Anchor>],
20707        buffer: &'a MultiBufferSnapshot,
20708    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20709        ranges.iter().flat_map(move |range| {
20710            let end_excerpt_id = range.end.excerpt_id;
20711            let range = range.to_point(buffer);
20712            let mut peek_end = range.end;
20713            if range.end.row < buffer.max_row().0 {
20714                peek_end = Point::new(range.end.row + 1, 0);
20715            }
20716            buffer
20717                .diff_hunks_in_range(range.start..peek_end)
20718                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20719        })
20720    }
20721
20722    pub fn has_stageable_diff_hunks_in_ranges(
20723        &self,
20724        ranges: &[Range<Anchor>],
20725        snapshot: &MultiBufferSnapshot,
20726    ) -> bool {
20727        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20728        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20729    }
20730
20731    pub fn toggle_staged_selected_diff_hunks(
20732        &mut self,
20733        _: &::git::ToggleStaged,
20734        _: &mut Window,
20735        cx: &mut Context<Self>,
20736    ) {
20737        let snapshot = self.buffer.read(cx).snapshot(cx);
20738        let ranges: Vec<_> = self
20739            .selections
20740            .disjoint_anchors()
20741            .iter()
20742            .map(|s| s.range())
20743            .collect();
20744        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20745        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20746    }
20747
20748    pub fn set_render_diff_hunk_controls(
20749        &mut self,
20750        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20751        cx: &mut Context<Self>,
20752    ) {
20753        self.render_diff_hunk_controls = render_diff_hunk_controls;
20754        cx.notify();
20755    }
20756
20757    pub fn stage_and_next(
20758        &mut self,
20759        _: &::git::StageAndNext,
20760        window: &mut Window,
20761        cx: &mut Context<Self>,
20762    ) {
20763        self.do_stage_or_unstage_and_next(true, window, cx);
20764    }
20765
20766    pub fn unstage_and_next(
20767        &mut self,
20768        _: &::git::UnstageAndNext,
20769        window: &mut Window,
20770        cx: &mut Context<Self>,
20771    ) {
20772        self.do_stage_or_unstage_and_next(false, window, cx);
20773    }
20774
20775    pub fn stage_or_unstage_diff_hunks(
20776        &mut self,
20777        stage: bool,
20778        ranges: Vec<Range<Anchor>>,
20779        cx: &mut Context<Self>,
20780    ) {
20781        if self.delegate_stage_and_restore {
20782            let snapshot = self.buffer.read(cx).snapshot(cx);
20783            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20784            if !hunks.is_empty() {
20785                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20786            }
20787            return;
20788        }
20789        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20790        cx.spawn(async move |this, cx| {
20791            task.await?;
20792            this.update(cx, |this, cx| {
20793                let snapshot = this.buffer.read(cx).snapshot(cx);
20794                let chunk_by = this
20795                    .diff_hunks_in_ranges(&ranges, &snapshot)
20796                    .chunk_by(|hunk| hunk.buffer_id);
20797                for (buffer_id, hunks) in &chunk_by {
20798                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20799                }
20800            })
20801        })
20802        .detach_and_log_err(cx);
20803    }
20804
20805    fn save_buffers_for_ranges_if_needed(
20806        &mut self,
20807        ranges: &[Range<Anchor>],
20808        cx: &mut Context<Editor>,
20809    ) -> Task<Result<()>> {
20810        let multibuffer = self.buffer.read(cx);
20811        let snapshot = multibuffer.read(cx);
20812        let buffer_ids: HashSet<_> = ranges
20813            .iter()
20814            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20815            .collect();
20816        drop(snapshot);
20817
20818        let mut buffers = HashSet::default();
20819        for buffer_id in buffer_ids {
20820            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20821                let buffer = buffer_entity.read(cx);
20822                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20823                {
20824                    buffers.insert(buffer_entity);
20825                }
20826            }
20827        }
20828
20829        if let Some(project) = &self.project {
20830            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20831        } else {
20832            Task::ready(Ok(()))
20833        }
20834    }
20835
20836    fn do_stage_or_unstage_and_next(
20837        &mut self,
20838        stage: bool,
20839        window: &mut Window,
20840        cx: &mut Context<Self>,
20841    ) {
20842        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20843
20844        if ranges.iter().any(|range| range.start != range.end) {
20845            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20846            return;
20847        }
20848
20849        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20850
20851        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20852        let wrap_around = !all_diff_hunks_expanded;
20853        let snapshot = self.snapshot(window, cx);
20854        let position = self
20855            .selections
20856            .newest::<Point>(&snapshot.display_snapshot)
20857            .head();
20858
20859        self.go_to_hunk_before_or_after_position(
20860            &snapshot,
20861            position,
20862            Direction::Next,
20863            wrap_around,
20864            window,
20865            cx,
20866        );
20867    }
20868
20869    pub(crate) fn do_stage_or_unstage(
20870        &self,
20871        stage: bool,
20872        buffer_id: BufferId,
20873        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20874        cx: &mut App,
20875    ) -> Option<()> {
20876        let project = self.project()?;
20877        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20878        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20879        let buffer_snapshot = buffer.read(cx).snapshot();
20880        let file_exists = buffer_snapshot
20881            .file()
20882            .is_some_and(|file| file.disk_state().exists());
20883        diff.update(cx, |diff, cx| {
20884            diff.stage_or_unstage_hunks(
20885                stage,
20886                &hunks
20887                    .map(|hunk| buffer_diff::DiffHunk {
20888                        buffer_range: hunk.buffer_range,
20889                        // We don't need to pass in word diffs here because they're only used for rendering and
20890                        // this function changes internal state
20891                        base_word_diffs: Vec::default(),
20892                        buffer_word_diffs: Vec::default(),
20893                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20894                            ..hunk.diff_base_byte_range.end.0,
20895                        secondary_status: hunk.status.secondary,
20896                        range: Point::zero()..Point::zero(), // unused
20897                    })
20898                    .collect::<Vec<_>>(),
20899                &buffer_snapshot,
20900                file_exists,
20901                cx,
20902            )
20903        });
20904        None
20905    }
20906
20907    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20908        let ranges: Vec<_> = self
20909            .selections
20910            .disjoint_anchors()
20911            .iter()
20912            .map(|s| s.range())
20913            .collect();
20914        self.buffer
20915            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20916    }
20917
20918    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20919        self.buffer.update(cx, |buffer, cx| {
20920            let ranges = vec![Anchor::min()..Anchor::max()];
20921            if !buffer.all_diff_hunks_expanded()
20922                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20923            {
20924                buffer.collapse_diff_hunks(ranges, cx);
20925                true
20926            } else {
20927                false
20928            }
20929        })
20930    }
20931
20932    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20933        if self.buffer.read(cx).all_diff_hunks_expanded() {
20934            return true;
20935        }
20936        let ranges = vec![Anchor::min()..Anchor::max()];
20937        self.buffer
20938            .read(cx)
20939            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20940    }
20941
20942    fn toggle_diff_hunks_in_ranges(
20943        &mut self,
20944        ranges: Vec<Range<Anchor>>,
20945        cx: &mut Context<Editor>,
20946    ) {
20947        self.buffer.update(cx, |buffer, cx| {
20948            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20949            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20950        })
20951    }
20952
20953    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20954        self.buffer.update(cx, |buffer, cx| {
20955            buffer.toggle_single_diff_hunk(range, cx);
20956        })
20957    }
20958
20959    pub(crate) fn apply_all_diff_hunks(
20960        &mut self,
20961        _: &ApplyAllDiffHunks,
20962        window: &mut Window,
20963        cx: &mut Context<Self>,
20964    ) {
20965        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20966
20967        let buffers = self.buffer.read(cx).all_buffers();
20968        for branch_buffer in buffers {
20969            branch_buffer.update(cx, |branch_buffer, cx| {
20970                branch_buffer.merge_into_base(Vec::new(), cx);
20971            });
20972        }
20973
20974        if let Some(project) = self.project.clone() {
20975            self.save(
20976                SaveOptions {
20977                    format: true,
20978                    autosave: false,
20979                },
20980                project,
20981                window,
20982                cx,
20983            )
20984            .detach_and_log_err(cx);
20985        }
20986    }
20987
20988    pub(crate) fn apply_selected_diff_hunks(
20989        &mut self,
20990        _: &ApplyDiffHunk,
20991        window: &mut Window,
20992        cx: &mut Context<Self>,
20993    ) {
20994        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20995        let snapshot = self.snapshot(window, cx);
20996        let hunks = snapshot.hunks_for_ranges(
20997            self.selections
20998                .all(&snapshot.display_snapshot)
20999                .into_iter()
21000                .map(|selection| selection.range()),
21001        );
21002        let mut ranges_by_buffer = HashMap::default();
21003        self.transact(window, cx, |editor, _window, cx| {
21004            for hunk in hunks {
21005                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
21006                    ranges_by_buffer
21007                        .entry(buffer.clone())
21008                        .or_insert_with(Vec::new)
21009                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
21010                }
21011            }
21012
21013            for (buffer, ranges) in ranges_by_buffer {
21014                buffer.update(cx, |buffer, cx| {
21015                    buffer.merge_into_base(ranges, cx);
21016                });
21017            }
21018        });
21019
21020        if let Some(project) = self.project.clone() {
21021            self.save(
21022                SaveOptions {
21023                    format: true,
21024                    autosave: false,
21025                },
21026                project,
21027                window,
21028                cx,
21029            )
21030            .detach_and_log_err(cx);
21031        }
21032    }
21033
21034    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
21035        if hovered != self.gutter_hovered {
21036            self.gutter_hovered = hovered;
21037            cx.notify();
21038        }
21039    }
21040
21041    pub fn insert_blocks(
21042        &mut self,
21043        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
21044        autoscroll: Option<Autoscroll>,
21045        cx: &mut Context<Self>,
21046    ) -> Vec<CustomBlockId> {
21047        let blocks = self
21048            .display_map
21049            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
21050        if let Some(autoscroll) = autoscroll {
21051            self.request_autoscroll(autoscroll, cx);
21052        }
21053        cx.notify();
21054        blocks
21055    }
21056
21057    pub fn resize_blocks(
21058        &mut self,
21059        heights: HashMap<CustomBlockId, u32>,
21060        autoscroll: Option<Autoscroll>,
21061        cx: &mut Context<Self>,
21062    ) {
21063        self.display_map
21064            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21065        if let Some(autoscroll) = autoscroll {
21066            self.request_autoscroll(autoscroll, cx);
21067        }
21068        cx.notify();
21069    }
21070
21071    pub fn replace_blocks(
21072        &mut self,
21073        renderers: HashMap<CustomBlockId, RenderBlock>,
21074        autoscroll: Option<Autoscroll>,
21075        cx: &mut Context<Self>,
21076    ) {
21077        self.display_map
21078            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21079        if let Some(autoscroll) = autoscroll {
21080            self.request_autoscroll(autoscroll, cx);
21081        }
21082        cx.notify();
21083    }
21084
21085    pub fn remove_blocks(
21086        &mut self,
21087        block_ids: HashSet<CustomBlockId>,
21088        autoscroll: Option<Autoscroll>,
21089        cx: &mut Context<Self>,
21090    ) {
21091        self.display_map.update(cx, |display_map, cx| {
21092            display_map.remove_blocks(block_ids, cx)
21093        });
21094        if let Some(autoscroll) = autoscroll {
21095            self.request_autoscroll(autoscroll, cx);
21096        }
21097        cx.notify();
21098    }
21099
21100    pub fn row_for_block(
21101        &self,
21102        block_id: CustomBlockId,
21103        cx: &mut Context<Self>,
21104    ) -> Option<DisplayRow> {
21105        self.display_map
21106            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21107    }
21108
21109    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21110        self.focused_block = Some(focused_block);
21111    }
21112
21113    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21114        self.focused_block.take()
21115    }
21116
21117    pub fn insert_creases(
21118        &mut self,
21119        creases: impl IntoIterator<Item = Crease<Anchor>>,
21120        cx: &mut Context<Self>,
21121    ) -> Vec<CreaseId> {
21122        self.display_map
21123            .update(cx, |map, cx| map.insert_creases(creases, cx))
21124    }
21125
21126    pub fn remove_creases(
21127        &mut self,
21128        ids: impl IntoIterator<Item = CreaseId>,
21129        cx: &mut Context<Self>,
21130    ) -> Vec<(CreaseId, Range<Anchor>)> {
21131        self.display_map
21132            .update(cx, |map, cx| map.remove_creases(ids, cx))
21133    }
21134
21135    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21136        self.display_map
21137            .update(cx, |map, cx| map.snapshot(cx))
21138            .longest_row()
21139    }
21140
21141    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21142        self.display_map
21143            .update(cx, |map, cx| map.snapshot(cx))
21144            .max_point()
21145    }
21146
21147    pub fn text(&self, cx: &App) -> String {
21148        self.buffer.read(cx).read(cx).text()
21149    }
21150
21151    pub fn is_empty(&self, cx: &App) -> bool {
21152        self.buffer.read(cx).read(cx).is_empty()
21153    }
21154
21155    pub fn text_option(&self, cx: &App) -> Option<String> {
21156        let text = self.text(cx);
21157        let text = text.trim();
21158
21159        if text.is_empty() {
21160            return None;
21161        }
21162
21163        Some(text.to_string())
21164    }
21165
21166    pub fn set_text(
21167        &mut self,
21168        text: impl Into<Arc<str>>,
21169        window: &mut Window,
21170        cx: &mut Context<Self>,
21171    ) {
21172        self.transact(window, cx, |this, _, cx| {
21173            this.buffer
21174                .read(cx)
21175                .as_singleton()
21176                .expect("you can only call set_text on editors for singleton buffers")
21177                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21178        });
21179    }
21180
21181    pub fn display_text(&self, cx: &mut App) -> String {
21182        self.display_map
21183            .update(cx, |map, cx| map.snapshot(cx))
21184            .text()
21185    }
21186
21187    fn create_minimap(
21188        &self,
21189        minimap_settings: MinimapSettings,
21190        window: &mut Window,
21191        cx: &mut Context<Self>,
21192    ) -> Option<Entity<Self>> {
21193        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21194            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21195    }
21196
21197    fn initialize_new_minimap(
21198        &self,
21199        minimap_settings: MinimapSettings,
21200        window: &mut Window,
21201        cx: &mut Context<Self>,
21202    ) -> Entity<Self> {
21203        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21204        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21205
21206        let mut minimap = Editor::new_internal(
21207            EditorMode::Minimap {
21208                parent: cx.weak_entity(),
21209            },
21210            self.buffer.clone(),
21211            None,
21212            Some(self.display_map.clone()),
21213            window,
21214            cx,
21215        );
21216        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21217        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21218        minimap.scroll_manager.clone_state(
21219            &self.scroll_manager,
21220            &my_snapshot,
21221            &minimap_snapshot,
21222            cx,
21223        );
21224        minimap.set_text_style_refinement(TextStyleRefinement {
21225            font_size: Some(MINIMAP_FONT_SIZE),
21226            font_weight: Some(MINIMAP_FONT_WEIGHT),
21227            font_family: Some(MINIMAP_FONT_FAMILY),
21228            ..Default::default()
21229        });
21230        minimap.update_minimap_configuration(minimap_settings, cx);
21231        cx.new(|_| minimap)
21232    }
21233
21234    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21235        let current_line_highlight = minimap_settings
21236            .current_line_highlight
21237            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21238        self.set_current_line_highlight(Some(current_line_highlight));
21239    }
21240
21241    pub fn minimap(&self) -> Option<&Entity<Self>> {
21242        self.minimap
21243            .as_ref()
21244            .filter(|_| self.minimap_visibility.visible())
21245    }
21246
21247    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21248        let mut wrap_guides = smallvec![];
21249
21250        if self.show_wrap_guides == Some(false) {
21251            return wrap_guides;
21252        }
21253
21254        let settings = self.buffer.read(cx).language_settings(cx);
21255        if settings.show_wrap_guides {
21256            match self.soft_wrap_mode(cx) {
21257                SoftWrap::Column(soft_wrap) => {
21258                    wrap_guides.push((soft_wrap as usize, true));
21259                }
21260                SoftWrap::Bounded(soft_wrap) => {
21261                    wrap_guides.push((soft_wrap as usize, true));
21262                }
21263                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21264            }
21265            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21266        }
21267
21268        wrap_guides
21269    }
21270
21271    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21272        let settings = self.buffer.read(cx).language_settings(cx);
21273        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21274        match mode {
21275            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21276                SoftWrap::None
21277            }
21278            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21279            language_settings::SoftWrap::PreferredLineLength => {
21280                SoftWrap::Column(settings.preferred_line_length)
21281            }
21282            language_settings::SoftWrap::Bounded => {
21283                SoftWrap::Bounded(settings.preferred_line_length)
21284            }
21285        }
21286    }
21287
21288    pub fn set_soft_wrap_mode(
21289        &mut self,
21290        mode: language_settings::SoftWrap,
21291        cx: &mut Context<Self>,
21292    ) {
21293        self.soft_wrap_mode_override = Some(mode);
21294        cx.notify();
21295    }
21296
21297    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21298        self.hard_wrap = hard_wrap;
21299        cx.notify();
21300    }
21301
21302    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21303        self.text_style_refinement = Some(style);
21304    }
21305
21306    /// called by the Element so we know what style we were most recently rendered with.
21307    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21308        // We intentionally do not inform the display map about the minimap style
21309        // so that wrapping is not recalculated and stays consistent for the editor
21310        // and its linked minimap.
21311        if !self.mode.is_minimap() {
21312            let font = style.text.font();
21313            let font_size = style.text.font_size.to_pixels(window.rem_size());
21314            let display_map = self
21315                .placeholder_display_map
21316                .as_ref()
21317                .filter(|_| self.is_empty(cx))
21318                .unwrap_or(&self.display_map);
21319
21320            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21321        }
21322        self.style = Some(style);
21323    }
21324
21325    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21326        if self.style.is_none() {
21327            self.style = Some(self.create_style(cx));
21328        }
21329        self.style.as_ref().unwrap()
21330    }
21331
21332    // Called by the element. This method is not designed to be called outside of the editor
21333    // element's layout code because it does not notify when rewrapping is computed synchronously.
21334    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21335        if self.is_empty(cx) {
21336            self.placeholder_display_map
21337                .as_ref()
21338                .map_or(false, |display_map| {
21339                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21340                })
21341        } else {
21342            self.display_map
21343                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21344        }
21345    }
21346
21347    pub fn set_soft_wrap(&mut self) {
21348        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21349    }
21350
21351    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21352        if self.soft_wrap_mode_override.is_some() {
21353            self.soft_wrap_mode_override.take();
21354        } else {
21355            let soft_wrap = match self.soft_wrap_mode(cx) {
21356                SoftWrap::GitDiff => return,
21357                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21358                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21359                    language_settings::SoftWrap::None
21360                }
21361            };
21362            self.soft_wrap_mode_override = Some(soft_wrap);
21363        }
21364        cx.notify();
21365    }
21366
21367    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21368        let Some(workspace) = self.workspace() else {
21369            return;
21370        };
21371        let fs = workspace.read(cx).app_state().fs.clone();
21372        let current_show = TabBarSettings::get_global(cx).show;
21373        update_settings_file(fs, cx, move |setting, _| {
21374            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21375        });
21376    }
21377
21378    pub fn toggle_indent_guides(
21379        &mut self,
21380        _: &ToggleIndentGuides,
21381        _: &mut Window,
21382        cx: &mut Context<Self>,
21383    ) {
21384        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21385            self.buffer
21386                .read(cx)
21387                .language_settings(cx)
21388                .indent_guides
21389                .enabled
21390        });
21391        self.show_indent_guides = Some(!currently_enabled);
21392        cx.notify();
21393    }
21394
21395    fn should_show_indent_guides(&self) -> Option<bool> {
21396        self.show_indent_guides
21397    }
21398
21399    pub fn disable_indent_guides_for_buffer(
21400        &mut self,
21401        buffer_id: BufferId,
21402        cx: &mut Context<Self>,
21403    ) {
21404        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21405        cx.notify();
21406    }
21407
21408    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21409        self.buffers_with_disabled_indent_guides
21410            .contains(&buffer_id)
21411    }
21412
21413    pub fn toggle_line_numbers(
21414        &mut self,
21415        _: &ToggleLineNumbers,
21416        _: &mut Window,
21417        cx: &mut Context<Self>,
21418    ) {
21419        let mut editor_settings = EditorSettings::get_global(cx).clone();
21420        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21421        EditorSettings::override_global(editor_settings, cx);
21422    }
21423
21424    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21425        if let Some(show_line_numbers) = self.show_line_numbers {
21426            return show_line_numbers;
21427        }
21428        EditorSettings::get_global(cx).gutter.line_numbers
21429    }
21430
21431    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21432        match (
21433            self.use_relative_line_numbers,
21434            EditorSettings::get_global(cx).relative_line_numbers,
21435        ) {
21436            (None, setting) => setting,
21437            (Some(false), _) => RelativeLineNumbers::Disabled,
21438            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21439            (Some(true), _) => RelativeLineNumbers::Enabled,
21440        }
21441    }
21442
21443    pub fn toggle_relative_line_numbers(
21444        &mut self,
21445        _: &ToggleRelativeLineNumbers,
21446        _: &mut Window,
21447        cx: &mut Context<Self>,
21448    ) {
21449        let is_relative = self.relative_line_numbers(cx);
21450        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21451    }
21452
21453    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21454        self.use_relative_line_numbers = is_relative;
21455        cx.notify();
21456    }
21457
21458    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21459        self.show_gutter = show_gutter;
21460        cx.notify();
21461    }
21462
21463    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21464        self.show_scrollbars = ScrollbarAxes {
21465            horizontal: show,
21466            vertical: show,
21467        };
21468        cx.notify();
21469    }
21470
21471    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21472        self.show_scrollbars.vertical = show;
21473        cx.notify();
21474    }
21475
21476    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21477        self.show_scrollbars.horizontal = show;
21478        cx.notify();
21479    }
21480
21481    pub fn set_minimap_visibility(
21482        &mut self,
21483        minimap_visibility: MinimapVisibility,
21484        window: &mut Window,
21485        cx: &mut Context<Self>,
21486    ) {
21487        if self.minimap_visibility != minimap_visibility {
21488            if minimap_visibility.visible() && self.minimap.is_none() {
21489                let minimap_settings = EditorSettings::get_global(cx).minimap;
21490                self.minimap =
21491                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21492            }
21493            self.minimap_visibility = minimap_visibility;
21494            cx.notify();
21495        }
21496    }
21497
21498    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21499        self.set_show_scrollbars(false, cx);
21500        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21501    }
21502
21503    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21504        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21505    }
21506
21507    /// Normally the text in full mode and auto height editors is padded on the
21508    /// left side by roughly half a character width for improved hit testing.
21509    ///
21510    /// Use this method to disable this for cases where this is not wanted (e.g.
21511    /// if you want to align the editor text with some other text above or below)
21512    /// or if you want to add this padding to single-line editors.
21513    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21514        self.offset_content = offset_content;
21515        cx.notify();
21516    }
21517
21518    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21519        self.show_line_numbers = Some(show_line_numbers);
21520        cx.notify();
21521    }
21522
21523    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21524        self.disable_expand_excerpt_buttons = true;
21525        cx.notify();
21526    }
21527
21528    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21529        self.number_deleted_lines = number;
21530        cx.notify();
21531    }
21532
21533    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21534        self.delegate_expand_excerpts = delegate;
21535    }
21536
21537    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21538        self.delegate_stage_and_restore = delegate;
21539    }
21540
21541    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21542        self.delegate_open_excerpts = delegate;
21543    }
21544
21545    pub fn set_on_local_selections_changed(
21546        &mut self,
21547        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21548    ) {
21549        self.on_local_selections_changed = callback;
21550    }
21551
21552    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21553        self.suppress_selection_callback = suppress;
21554    }
21555
21556    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21557        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21558        cx.notify();
21559    }
21560
21561    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21562        self.show_code_actions = Some(show_code_actions);
21563        cx.notify();
21564    }
21565
21566    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21567        self.show_runnables = Some(show_runnables);
21568        cx.notify();
21569    }
21570
21571    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21572        self.show_breakpoints = Some(show_breakpoints);
21573        cx.notify();
21574    }
21575
21576    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21577        self.show_diff_review_button = show;
21578        cx.notify();
21579    }
21580
21581    pub fn show_diff_review_button(&self) -> bool {
21582        self.show_diff_review_button
21583    }
21584
21585    pub fn render_diff_review_button(
21586        &self,
21587        display_row: DisplayRow,
21588        width: Pixels,
21589        cx: &mut Context<Self>,
21590    ) -> impl IntoElement {
21591        let text_color = cx.theme().colors().text;
21592        let icon_color = cx.theme().colors().icon_accent;
21593
21594        h_flex()
21595            .id("diff_review_button")
21596            .cursor_pointer()
21597            .w(width - px(1.))
21598            .h(relative(0.9))
21599            .justify_center()
21600            .rounded_sm()
21601            .border_1()
21602            .border_color(text_color.opacity(0.1))
21603            .bg(text_color.opacity(0.15))
21604            .hover(|s| {
21605                s.bg(icon_color.opacity(0.4))
21606                    .border_color(icon_color.opacity(0.5))
21607            })
21608            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21609            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21610            .on_mouse_down(
21611                gpui::MouseButton::Left,
21612                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21613                    editor.start_diff_review_drag(display_row, window, cx);
21614                }),
21615            )
21616    }
21617
21618    pub fn start_diff_review_drag(
21619        &mut self,
21620        display_row: DisplayRow,
21621        window: &mut Window,
21622        cx: &mut Context<Self>,
21623    ) {
21624        let snapshot = self.snapshot(window, cx);
21625        let point = snapshot
21626            .display_snapshot
21627            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21628        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21629        self.diff_review_drag_state = Some(DiffReviewDragState {
21630            start_anchor: anchor,
21631            current_anchor: anchor,
21632        });
21633        cx.notify();
21634    }
21635
21636    pub fn update_diff_review_drag(
21637        &mut self,
21638        display_row: DisplayRow,
21639        window: &mut Window,
21640        cx: &mut Context<Self>,
21641    ) {
21642        if self.diff_review_drag_state.is_none() {
21643            return;
21644        }
21645        let snapshot = self.snapshot(window, cx);
21646        let point = snapshot
21647            .display_snapshot
21648            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21649        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21650        if let Some(drag_state) = &mut self.diff_review_drag_state {
21651            drag_state.current_anchor = anchor;
21652            cx.notify();
21653        }
21654    }
21655
21656    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21657        if let Some(drag_state) = self.diff_review_drag_state.take() {
21658            let snapshot = self.snapshot(window, cx);
21659            let range = drag_state.row_range(&snapshot.display_snapshot);
21660            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21661        }
21662        cx.notify();
21663    }
21664
21665    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21666        self.diff_review_drag_state = None;
21667        cx.notify();
21668    }
21669
21670    /// Calculates the appropriate block height for the diff review overlay.
21671    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21672    /// and 2 lines per comment when expanded.
21673    fn calculate_overlay_height(
21674        &self,
21675        hunk_key: &DiffHunkKey,
21676        comments_expanded: bool,
21677        snapshot: &MultiBufferSnapshot,
21678    ) -> u32 {
21679        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21680        let base_height: u32 = 2; // Input row with avatar and buttons
21681
21682        if comment_count == 0 {
21683            base_height
21684        } else if comments_expanded {
21685            // Header (1 line) + 2 lines per comment
21686            base_height + 1 + (comment_count as u32 * 2)
21687        } else {
21688            // Just header when collapsed
21689            base_height + 1
21690        }
21691    }
21692
21693    pub fn show_diff_review_overlay(
21694        &mut self,
21695        display_range: Range<DisplayRow>,
21696        window: &mut Window,
21697        cx: &mut Context<Self>,
21698    ) {
21699        let Range { start, end } = display_range.sorted();
21700
21701        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21702        let editor_snapshot = self.snapshot(window, cx);
21703
21704        // Convert display rows to multibuffer points
21705        let start_point = editor_snapshot
21706            .display_snapshot
21707            .display_point_to_point(start.as_display_point(), Bias::Left);
21708        let end_point = editor_snapshot
21709            .display_snapshot
21710            .display_point_to_point(end.as_display_point(), Bias::Left);
21711        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21712
21713        // Create anchor range for the selected lines (start of first line to end of last line)
21714        let line_end = Point::new(
21715            end_point.row,
21716            buffer_snapshot.line_len(end_multi_buffer_row),
21717        );
21718        let anchor_range =
21719            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21720
21721        // Compute the hunk key for this display row
21722        let file_path = buffer_snapshot
21723            .file_at(start_point)
21724            .map(|file: &Arc<dyn language::File>| file.path().clone())
21725            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21726        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21727        let new_hunk_key = DiffHunkKey {
21728            file_path,
21729            hunk_start_anchor,
21730        };
21731
21732        // Check if we already have an overlay for this hunk
21733        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21734            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21735        }) {
21736            // Just focus the existing overlay's prompt editor
21737            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21738            window.focus(&focus_handle, cx);
21739            return;
21740        }
21741
21742        // Dismiss overlays that have no comments for their hunks
21743        self.dismiss_overlays_without_comments(cx);
21744
21745        // Get the current user's avatar URI from the project's user_store
21746        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21747            let user_store = project.read(cx).user_store();
21748            user_store
21749                .read(cx)
21750                .current_user()
21751                .map(|user| user.avatar_uri.clone())
21752        });
21753
21754        // Create anchor at the end of the last row so the block appears immediately below it
21755        // Use multibuffer coordinates for anchor creation
21756        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21757        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21758
21759        // Use the hunk key we already computed
21760        let hunk_key = new_hunk_key;
21761
21762        // Create the prompt editor for the review input
21763        let prompt_editor = cx.new(|cx| {
21764            let mut editor = Editor::single_line(window, cx);
21765            editor.set_placeholder_text("Add a review comment...", window, cx);
21766            editor
21767        });
21768
21769        // Register the Newline action on the prompt editor to submit the review
21770        let parent_editor = cx.entity().downgrade();
21771        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21772            prompt_editor.register_action({
21773                let parent_editor = parent_editor.clone();
21774                move |_: &crate::actions::Newline, window, cx| {
21775                    if let Some(editor) = parent_editor.upgrade() {
21776                        editor.update(cx, |editor, cx| {
21777                            editor.submit_diff_review_comment(window, cx);
21778                        });
21779                    }
21780                }
21781            })
21782        });
21783
21784        // Calculate initial height based on existing comments for this hunk
21785        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21786
21787        // Create the overlay block
21788        let prompt_editor_for_render = prompt_editor.clone();
21789        let hunk_key_for_render = hunk_key.clone();
21790        let editor_handle = cx.entity().downgrade();
21791        let block = BlockProperties {
21792            style: BlockStyle::Sticky,
21793            placement: BlockPlacement::Below(anchor),
21794            height: Some(initial_height),
21795            render: Arc::new(move |cx| {
21796                Self::render_diff_review_overlay(
21797                    &prompt_editor_for_render,
21798                    &hunk_key_for_render,
21799                    &editor_handle,
21800                    cx,
21801                )
21802            }),
21803            priority: 0,
21804        };
21805
21806        let block_ids = self.insert_blocks([block], None, cx);
21807        let Some(block_id) = block_ids.into_iter().next() else {
21808            log::error!("Failed to insert diff review overlay block");
21809            return;
21810        };
21811
21812        self.diff_review_overlays.push(DiffReviewOverlay {
21813            anchor_range,
21814            block_id,
21815            prompt_editor: prompt_editor.clone(),
21816            hunk_key,
21817            comments_expanded: true,
21818            inline_edit_editors: HashMap::default(),
21819            inline_edit_subscriptions: HashMap::default(),
21820            user_avatar_uri,
21821            _subscription: subscription,
21822        });
21823
21824        // Focus the prompt editor
21825        let focus_handle = prompt_editor.focus_handle(cx);
21826        window.focus(&focus_handle, cx);
21827
21828        cx.notify();
21829    }
21830
21831    /// Dismisses all diff review overlays.
21832    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21833        if self.diff_review_overlays.is_empty() {
21834            return;
21835        }
21836        let block_ids: HashSet<_> = self
21837            .diff_review_overlays
21838            .drain(..)
21839            .map(|overlay| overlay.block_id)
21840            .collect();
21841        self.remove_blocks(block_ids, None, cx);
21842        cx.notify();
21843    }
21844
21845    /// Dismisses overlays that have no comments stored for their hunks.
21846    /// Keeps overlays that have at least one comment.
21847    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21848        let snapshot = self.buffer.read(cx).snapshot(cx);
21849
21850        // First, compute which overlays have comments (to avoid borrow issues with retain)
21851        let overlays_with_comments: Vec<bool> = self
21852            .diff_review_overlays
21853            .iter()
21854            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21855            .collect();
21856
21857        // Now collect block IDs to remove and retain overlays
21858        let mut block_ids_to_remove = HashSet::default();
21859        let mut index = 0;
21860        self.diff_review_overlays.retain(|overlay| {
21861            let has_comments = overlays_with_comments[index];
21862            index += 1;
21863            if !has_comments {
21864                block_ids_to_remove.insert(overlay.block_id);
21865            }
21866            has_comments
21867        });
21868
21869        if !block_ids_to_remove.is_empty() {
21870            self.remove_blocks(block_ids_to_remove, None, cx);
21871            cx.notify();
21872        }
21873    }
21874
21875    /// Refreshes the diff review overlay block to update its height and render function.
21876    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21877    fn refresh_diff_review_overlay_height(
21878        &mut self,
21879        hunk_key: &DiffHunkKey,
21880        _window: &mut Window,
21881        cx: &mut Context<Self>,
21882    ) {
21883        // Extract all needed data from overlay first to avoid borrow conflicts
21884        let snapshot = self.buffer.read(cx).snapshot(cx);
21885        let (comments_expanded, block_id, prompt_editor) = {
21886            let Some(overlay) = self
21887                .diff_review_overlays
21888                .iter()
21889                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21890            else {
21891                return;
21892            };
21893
21894            (
21895                overlay.comments_expanded,
21896                overlay.block_id,
21897                overlay.prompt_editor.clone(),
21898            )
21899        };
21900
21901        // Calculate new height
21902        let snapshot = self.buffer.read(cx).snapshot(cx);
21903        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21904
21905        // Update the block height using resize_blocks (avoids flicker)
21906        let mut heights = HashMap::default();
21907        heights.insert(block_id, new_height);
21908        self.resize_blocks(heights, None, cx);
21909
21910        // Update the render function using replace_blocks (avoids flicker)
21911        let hunk_key_for_render = hunk_key.clone();
21912        let editor_handle = cx.entity().downgrade();
21913        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21914            Arc::new(move |cx| {
21915                Self::render_diff_review_overlay(
21916                    &prompt_editor,
21917                    &hunk_key_for_render,
21918                    &editor_handle,
21919                    cx,
21920                )
21921            });
21922
21923        let mut renderers = HashMap::default();
21924        renderers.insert(block_id, render);
21925        self.replace_blocks(renderers, None, cx);
21926    }
21927
21928    /// Action handler for SubmitDiffReviewComment.
21929    pub fn submit_diff_review_comment_action(
21930        &mut self,
21931        _: &SubmitDiffReviewComment,
21932        window: &mut Window,
21933        cx: &mut Context<Self>,
21934    ) {
21935        self.submit_diff_review_comment(window, cx);
21936    }
21937
21938    /// Stores the diff review comment locally.
21939    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21940    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21941        // Find the overlay that currently has focus
21942        let overlay_index = self
21943            .diff_review_overlays
21944            .iter()
21945            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21946        let Some(overlay_index) = overlay_index else {
21947            return;
21948        };
21949        let overlay = &self.diff_review_overlays[overlay_index];
21950
21951        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21952        if comment_text.is_empty() {
21953            return;
21954        }
21955
21956        let anchor_range = overlay.anchor_range.clone();
21957        let hunk_key = overlay.hunk_key.clone();
21958
21959        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21960
21961        // Clear the prompt editor but keep the overlay open
21962        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21963            overlay.prompt_editor.update(cx, |editor, cx| {
21964                editor.clear(window, cx);
21965            });
21966        }
21967
21968        // Refresh the overlay to update the block height for the new comment
21969        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21970
21971        cx.notify();
21972    }
21973
21974    /// Returns the prompt editor for the diff review overlay, if one is active.
21975    /// This is primarily used for testing.
21976    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21977        self.diff_review_overlays
21978            .first()
21979            .map(|overlay| &overlay.prompt_editor)
21980    }
21981
21982    /// Returns the line range for the first diff review overlay, if one is active.
21983    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21984    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21985        let overlay = self.diff_review_overlays.first()?;
21986        let snapshot = self.buffer.read(cx).snapshot(cx);
21987        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21988        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21989        let start_row = snapshot
21990            .point_to_buffer_point(start_point)
21991            .map(|(_, p, _)| p.row)
21992            .unwrap_or(start_point.row);
21993        let end_row = snapshot
21994            .point_to_buffer_point(end_point)
21995            .map(|(_, p, _)| p.row)
21996            .unwrap_or(end_point.row);
21997        Some((start_row, end_row))
21998    }
21999
22000    /// Sets whether the comments section is expanded in the diff review overlay.
22001    /// This is primarily used for testing.
22002    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
22003        for overlay in &mut self.diff_review_overlays {
22004            overlay.comments_expanded = expanded;
22005        }
22006        cx.notify();
22007    }
22008
22009    /// Compares two DiffHunkKeys for equality by resolving their anchors.
22010    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
22011        a.file_path == b.file_path
22012            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
22013    }
22014
22015    /// Returns comments for a specific hunk, ordered by creation time.
22016    pub fn comments_for_hunk<'a>(
22017        &'a self,
22018        key: &DiffHunkKey,
22019        snapshot: &MultiBufferSnapshot,
22020    ) -> &'a [StoredReviewComment] {
22021        let key_point = key.hunk_start_anchor.to_point(snapshot);
22022        self.stored_review_comments
22023            .iter()
22024            .find(|(k, _)| {
22025                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22026            })
22027            .map(|(_, comments)| comments.as_slice())
22028            .unwrap_or(&[])
22029    }
22030
22031    /// Returns the total count of stored review comments across all hunks.
22032    pub fn total_review_comment_count(&self) -> usize {
22033        self.stored_review_comments
22034            .iter()
22035            .map(|(_, v)| v.len())
22036            .sum()
22037    }
22038
22039    /// Returns the count of comments for a specific hunk.
22040    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
22041        let key_point = key.hunk_start_anchor.to_point(snapshot);
22042        self.stored_review_comments
22043            .iter()
22044            .find(|(k, _)| {
22045                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22046            })
22047            .map(|(_, v)| v.len())
22048            .unwrap_or(0)
22049    }
22050
22051    /// Adds a new review comment to a specific hunk.
22052    pub fn add_review_comment(
22053        &mut self,
22054        hunk_key: DiffHunkKey,
22055        comment: String,
22056        anchor_range: Range<Anchor>,
22057        cx: &mut Context<Self>,
22058    ) -> usize {
22059        let id = self.next_review_comment_id;
22060        self.next_review_comment_id += 1;
22061
22062        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22063
22064        let snapshot = self.buffer.read(cx).snapshot(cx);
22065        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22066
22067        // Find existing entry for this hunk or add a new one
22068        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22069            k.file_path == hunk_key.file_path
22070                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22071        }) {
22072            comments.push(stored_comment);
22073        } else {
22074            self.stored_review_comments
22075                .push((hunk_key, vec![stored_comment]));
22076        }
22077
22078        cx.emit(EditorEvent::ReviewCommentsChanged {
22079            total_count: self.total_review_comment_count(),
22080        });
22081        cx.notify();
22082        id
22083    }
22084
22085    /// Removes a review comment by ID from any hunk.
22086    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22087        for (_, comments) in self.stored_review_comments.iter_mut() {
22088            if let Some(index) = comments.iter().position(|c| c.id == id) {
22089                comments.remove(index);
22090                cx.emit(EditorEvent::ReviewCommentsChanged {
22091                    total_count: self.total_review_comment_count(),
22092                });
22093                cx.notify();
22094                return true;
22095            }
22096        }
22097        false
22098    }
22099
22100    /// Updates a review comment's text by ID.
22101    pub fn update_review_comment(
22102        &mut self,
22103        id: usize,
22104        new_comment: String,
22105        cx: &mut Context<Self>,
22106    ) -> bool {
22107        for (_, comments) in self.stored_review_comments.iter_mut() {
22108            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22109                comment.comment = new_comment;
22110                comment.is_editing = false;
22111                cx.emit(EditorEvent::ReviewCommentsChanged {
22112                    total_count: self.total_review_comment_count(),
22113                });
22114                cx.notify();
22115                return true;
22116            }
22117        }
22118        false
22119    }
22120
22121    /// Sets a comment's editing state.
22122    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22123        for (_, comments) in self.stored_review_comments.iter_mut() {
22124            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22125                comment.is_editing = is_editing;
22126                cx.notify();
22127                return;
22128            }
22129        }
22130    }
22131
22132    /// Takes all stored comments from all hunks, clearing the storage.
22133    /// Returns a Vec of (hunk_key, comments) pairs.
22134    pub fn take_all_review_comments(
22135        &mut self,
22136        cx: &mut Context<Self>,
22137    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22138        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22139        self.dismiss_all_diff_review_overlays(cx);
22140        let comments = std::mem::take(&mut self.stored_review_comments);
22141        // Reset the ID counter since all comments have been taken
22142        self.next_review_comment_id = 0;
22143        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22144        cx.notify();
22145        comments
22146    }
22147
22148    /// Removes review comments whose anchors are no longer valid or whose
22149    /// associated diff hunks no longer exist.
22150    ///
22151    /// This should be called when the buffer changes to prevent orphaned comments
22152    /// from accumulating.
22153    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22154        let snapshot = self.buffer.read(cx).snapshot(cx);
22155        let original_count = self.total_review_comment_count();
22156
22157        // Remove comments with invalid hunk anchors
22158        self.stored_review_comments
22159            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22160
22161        // Also clean up individual comments with invalid anchor ranges
22162        for (_, comments) in &mut self.stored_review_comments {
22163            comments.retain(|comment| {
22164                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22165            });
22166        }
22167
22168        // Remove empty hunk entries
22169        self.stored_review_comments
22170            .retain(|(_, comments)| !comments.is_empty());
22171
22172        let new_count = self.total_review_comment_count();
22173        if new_count != original_count {
22174            cx.emit(EditorEvent::ReviewCommentsChanged {
22175                total_count: new_count,
22176            });
22177            cx.notify();
22178        }
22179    }
22180
22181    /// Toggles the expanded state of the comments section in the overlay.
22182    pub fn toggle_review_comments_expanded(
22183        &mut self,
22184        _: &ToggleReviewCommentsExpanded,
22185        window: &mut Window,
22186        cx: &mut Context<Self>,
22187    ) {
22188        // Find the overlay that currently has focus, or use the first one
22189        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22190            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22191                overlay.comments_expanded = !overlay.comments_expanded;
22192                Some(overlay.hunk_key.clone())
22193            } else {
22194                None
22195            }
22196        });
22197
22198        // If no focused overlay found, toggle the first one
22199        let hunk_key = overlay_info.or_else(|| {
22200            self.diff_review_overlays.first_mut().map(|overlay| {
22201                overlay.comments_expanded = !overlay.comments_expanded;
22202                overlay.hunk_key.clone()
22203            })
22204        });
22205
22206        if let Some(hunk_key) = hunk_key {
22207            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22208            cx.notify();
22209        }
22210    }
22211
22212    /// Handles the EditReviewComment action - sets a comment into editing mode.
22213    pub fn edit_review_comment(
22214        &mut self,
22215        action: &EditReviewComment,
22216        window: &mut Window,
22217        cx: &mut Context<Self>,
22218    ) {
22219        let comment_id = action.id;
22220
22221        // Set the comment to editing mode
22222        self.set_comment_editing(comment_id, true, cx);
22223
22224        // Find the overlay that contains this comment and create an inline editor if needed
22225        // First, find which hunk this comment belongs to
22226        let hunk_key = self
22227            .stored_review_comments
22228            .iter()
22229            .find_map(|(key, comments)| {
22230                if comments.iter().any(|c| c.id == comment_id) {
22231                    Some(key.clone())
22232                } else {
22233                    None
22234                }
22235            });
22236
22237        let snapshot = self.buffer.read(cx).snapshot(cx);
22238        if let Some(hunk_key) = hunk_key {
22239            if let Some(overlay) = self
22240                .diff_review_overlays
22241                .iter_mut()
22242                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22243            {
22244                if let std::collections::hash_map::Entry::Vacant(entry) =
22245                    overlay.inline_edit_editors.entry(comment_id)
22246                {
22247                    // Find the comment text
22248                    let comment_text = self
22249                        .stored_review_comments
22250                        .iter()
22251                        .flat_map(|(_, comments)| comments)
22252                        .find(|c| c.id == comment_id)
22253                        .map(|c| c.comment.clone())
22254                        .unwrap_or_default();
22255
22256                    // Create inline editor
22257                    let parent_editor = cx.entity().downgrade();
22258                    let inline_editor = cx.new(|cx| {
22259                        let mut editor = Editor::single_line(window, cx);
22260                        editor.set_text(&*comment_text, window, cx);
22261                        // Select all text for easy replacement
22262                        editor.select_all(&crate::actions::SelectAll, window, cx);
22263                        editor
22264                    });
22265
22266                    // Register the Newline action to confirm the edit
22267                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22268                        inline_editor.register_action({
22269                            let parent_editor = parent_editor.clone();
22270                            move |_: &crate::actions::Newline, window, cx| {
22271                                if let Some(editor) = parent_editor.upgrade() {
22272                                    editor.update(cx, |editor, cx| {
22273                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22274                                    });
22275                                }
22276                            }
22277                        })
22278                    });
22279
22280                    // Store the subscription to keep the action handler alive
22281                    overlay
22282                        .inline_edit_subscriptions
22283                        .insert(comment_id, subscription);
22284
22285                    // Focus the inline editor
22286                    let focus_handle = inline_editor.focus_handle(cx);
22287                    window.focus(&focus_handle, cx);
22288
22289                    entry.insert(inline_editor);
22290                }
22291            }
22292        }
22293
22294        cx.notify();
22295    }
22296
22297    /// Confirms an inline edit of a review comment.
22298    pub fn confirm_edit_review_comment(
22299        &mut self,
22300        comment_id: usize,
22301        _window: &mut Window,
22302        cx: &mut Context<Self>,
22303    ) {
22304        // Get the new text from the inline editor
22305        // Find the overlay containing this comment's inline editor
22306        let snapshot = self.buffer.read(cx).snapshot(cx);
22307        let hunk_key = self
22308            .stored_review_comments
22309            .iter()
22310            .find_map(|(key, comments)| {
22311                if comments.iter().any(|c| c.id == comment_id) {
22312                    Some(key.clone())
22313                } else {
22314                    None
22315                }
22316            });
22317
22318        let new_text = hunk_key
22319            .as_ref()
22320            .and_then(|hunk_key| {
22321                self.diff_review_overlays
22322                    .iter()
22323                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22324            })
22325            .as_ref()
22326            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22327            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22328
22329        if let Some(new_text) = new_text {
22330            if !new_text.is_empty() {
22331                self.update_review_comment(comment_id, new_text, cx);
22332            }
22333        }
22334
22335        // Remove the inline editor and its subscription
22336        if let Some(hunk_key) = hunk_key {
22337            if let Some(overlay) = self
22338                .diff_review_overlays
22339                .iter_mut()
22340                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22341            {
22342                overlay.inline_edit_editors.remove(&comment_id);
22343                overlay.inline_edit_subscriptions.remove(&comment_id);
22344            }
22345        }
22346
22347        // Clear editing state
22348        self.set_comment_editing(comment_id, false, cx);
22349    }
22350
22351    /// Cancels an inline edit of a review comment.
22352    pub fn cancel_edit_review_comment(
22353        &mut self,
22354        comment_id: usize,
22355        _window: &mut Window,
22356        cx: &mut Context<Self>,
22357    ) {
22358        // Find which hunk this comment belongs to
22359        let hunk_key = self
22360            .stored_review_comments
22361            .iter()
22362            .find_map(|(key, comments)| {
22363                if comments.iter().any(|c| c.id == comment_id) {
22364                    Some(key.clone())
22365                } else {
22366                    None
22367                }
22368            });
22369
22370        // Remove the inline editor and its subscription
22371        if let Some(hunk_key) = hunk_key {
22372            let snapshot = self.buffer.read(cx).snapshot(cx);
22373            if let Some(overlay) = self
22374                .diff_review_overlays
22375                .iter_mut()
22376                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22377            {
22378                overlay.inline_edit_editors.remove(&comment_id);
22379                overlay.inline_edit_subscriptions.remove(&comment_id);
22380            }
22381        }
22382
22383        // Clear editing state
22384        self.set_comment_editing(comment_id, false, cx);
22385    }
22386
22387    /// Action handler for ConfirmEditReviewComment.
22388    pub fn confirm_edit_review_comment_action(
22389        &mut self,
22390        action: &ConfirmEditReviewComment,
22391        window: &mut Window,
22392        cx: &mut Context<Self>,
22393    ) {
22394        self.confirm_edit_review_comment(action.id, window, cx);
22395    }
22396
22397    /// Action handler for CancelEditReviewComment.
22398    pub fn cancel_edit_review_comment_action(
22399        &mut self,
22400        action: &CancelEditReviewComment,
22401        window: &mut Window,
22402        cx: &mut Context<Self>,
22403    ) {
22404        self.cancel_edit_review_comment(action.id, window, cx);
22405    }
22406
22407    /// Handles the DeleteReviewComment action - removes a comment.
22408    pub fn delete_review_comment(
22409        &mut self,
22410        action: &DeleteReviewComment,
22411        window: &mut Window,
22412        cx: &mut Context<Self>,
22413    ) {
22414        // Get the hunk key before removing the comment
22415        // Find the hunk key from the comment itself
22416        let comment_id = action.id;
22417        let hunk_key = self
22418            .stored_review_comments
22419            .iter()
22420            .find_map(|(key, comments)| {
22421                if comments.iter().any(|c| c.id == comment_id) {
22422                    Some(key.clone())
22423                } else {
22424                    None
22425                }
22426            });
22427
22428        // Also get it from the overlay for refresh purposes
22429        let overlay_hunk_key = self
22430            .diff_review_overlays
22431            .first()
22432            .map(|o| o.hunk_key.clone());
22433
22434        self.remove_review_comment(action.id, cx);
22435
22436        // Refresh the overlay height after removing a comment
22437        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22438            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22439        }
22440    }
22441
22442    fn render_diff_review_overlay(
22443        prompt_editor: &Entity<Editor>,
22444        hunk_key: &DiffHunkKey,
22445        editor_handle: &WeakEntity<Editor>,
22446        cx: &mut BlockContext,
22447    ) -> AnyElement {
22448        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22449            if ranges.is_empty() {
22450                return None;
22451            }
22452            let formatted: Vec<String> = ranges
22453                .iter()
22454                .map(|(start, end)| {
22455                    let start_line = start + 1;
22456                    let end_line = end + 1;
22457                    if start_line == end_line {
22458                        format!("Line {start_line}")
22459                    } else {
22460                        format!("Lines {start_line}-{end_line}")
22461                    }
22462                })
22463                .collect();
22464            // Don't show label for single line in single excerpt
22465            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22466                return None;
22467            }
22468            Some(formatted.join(""))
22469        }
22470
22471        let theme = cx.theme();
22472        let colors = theme.colors();
22473
22474        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22475            editor_handle
22476                .upgrade()
22477                .map(|editor| {
22478                    let editor = editor.read(cx);
22479                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22480                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22481                    let (expanded, editors, avatar_uri, line_ranges) = editor
22482                        .diff_review_overlays
22483                        .iter()
22484                        .find(|overlay| {
22485                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22486                        })
22487                        .map(|o| {
22488                            let start_point = o.anchor_range.start.to_point(&snapshot);
22489                            let end_point = o.anchor_range.end.to_point(&snapshot);
22490                            // Get line ranges per excerpt to detect discontinuities
22491                            let buffer_ranges =
22492                                snapshot.range_to_buffer_ranges(start_point..end_point);
22493                            let ranges: Vec<(u32, u32)> = buffer_ranges
22494                                .iter()
22495                                .map(|(buffer, range, _)| {
22496                                    let start = buffer.offset_to_point(range.start.0).row;
22497                                    let end = buffer.offset_to_point(range.end.0).row;
22498                                    (start, end)
22499                                })
22500                                .collect();
22501                            (
22502                                o.comments_expanded,
22503                                o.inline_edit_editors.clone(),
22504                                o.user_avatar_uri.clone(),
22505                                if ranges.is_empty() {
22506                                    None
22507                                } else {
22508                                    Some(ranges)
22509                                },
22510                            )
22511                        })
22512                        .unwrap_or((true, HashMap::default(), None, None));
22513                    (comments, expanded, editors, avatar_uri, line_ranges)
22514                })
22515                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22516
22517        let comment_count = comments.len();
22518        let avatar_size = px(20.);
22519        let action_icon_size = IconSize::XSmall;
22520
22521        v_flex()
22522            .w_full()
22523            .bg(colors.editor_background)
22524            .border_b_1()
22525            .border_color(colors.border)
22526            .px_2()
22527            .pb_2()
22528            .gap_2()
22529            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22530            .when_some(line_ranges, |el, ranges| {
22531                let label = format_line_ranges(&ranges);
22532                if let Some(label) = label {
22533                    el.child(
22534                        h_flex()
22535                            .w_full()
22536                            .px_2()
22537                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22538                    )
22539                } else {
22540                    el
22541                }
22542            })
22543            // Top row: editable input with user's avatar
22544            .child(
22545                h_flex()
22546                    .w_full()
22547                    .items_center()
22548                    .gap_2()
22549                    .px_2()
22550                    .py_1p5()
22551                    .rounded_md()
22552                    .bg(colors.surface_background)
22553                    .child(
22554                        div()
22555                            .size(avatar_size)
22556                            .flex_shrink_0()
22557                            .rounded_full()
22558                            .overflow_hidden()
22559                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22560                                Avatar::new(avatar_uri.clone())
22561                                    .size(avatar_size)
22562                                    .into_any_element()
22563                            } else {
22564                                Icon::new(IconName::Person)
22565                                    .size(IconSize::Small)
22566                                    .color(ui::Color::Muted)
22567                                    .into_any_element()
22568                            }),
22569                    )
22570                    .child(
22571                        div()
22572                            .flex_1()
22573                            .border_1()
22574                            .border_color(colors.border)
22575                            .rounded_md()
22576                            .bg(colors.editor_background)
22577                            .px_2()
22578                            .py_1()
22579                            .child(prompt_editor.clone()),
22580                    )
22581                    .child(
22582                        h_flex()
22583                            .flex_shrink_0()
22584                            .gap_1()
22585                            .child(
22586                                IconButton::new("diff-review-close", IconName::Close)
22587                                    .icon_color(ui::Color::Muted)
22588                                    .icon_size(action_icon_size)
22589                                    .tooltip(Tooltip::text("Close"))
22590                                    .on_click(|_, window, cx| {
22591                                        window
22592                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22593                                    }),
22594                            )
22595                            .child(
22596                                IconButton::new("diff-review-add", IconName::Return)
22597                                    .icon_color(ui::Color::Muted)
22598                                    .icon_size(action_icon_size)
22599                                    .tooltip(Tooltip::text("Add comment"))
22600                                    .on_click(|_, window, cx| {
22601                                        window.dispatch_action(
22602                                            Box::new(crate::actions::SubmitDiffReviewComment),
22603                                            cx,
22604                                        );
22605                                    }),
22606                            ),
22607                    ),
22608            )
22609            // Expandable comments section (only shown when there are comments)
22610            .when(comment_count > 0, |el| {
22611                el.child(Self::render_comments_section(
22612                    comments,
22613                    comments_expanded,
22614                    inline_editors,
22615                    user_avatar_uri,
22616                    avatar_size,
22617                    action_icon_size,
22618                    colors,
22619                ))
22620            })
22621            .into_any_element()
22622    }
22623
22624    fn render_comments_section(
22625        comments: Vec<StoredReviewComment>,
22626        expanded: bool,
22627        inline_editors: HashMap<usize, Entity<Editor>>,
22628        user_avatar_uri: Option<SharedUri>,
22629        avatar_size: Pixels,
22630        action_icon_size: IconSize,
22631        colors: &theme::ThemeColors,
22632    ) -> impl IntoElement {
22633        let comment_count = comments.len();
22634
22635        v_flex()
22636            .w_full()
22637            .gap_1()
22638            // Header with expand/collapse toggle
22639            .child(
22640                h_flex()
22641                    .id("review-comments-header")
22642                    .w_full()
22643                    .items_center()
22644                    .gap_1()
22645                    .px_2()
22646                    .py_1()
22647                    .cursor_pointer()
22648                    .rounded_md()
22649                    .hover(|style| style.bg(colors.ghost_element_hover))
22650                    .on_click(|_, window: &mut Window, cx| {
22651                        window.dispatch_action(
22652                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22653                            cx,
22654                        );
22655                    })
22656                    .child(
22657                        Icon::new(if expanded {
22658                            IconName::ChevronDown
22659                        } else {
22660                            IconName::ChevronRight
22661                        })
22662                        .size(IconSize::Small)
22663                        .color(ui::Color::Muted),
22664                    )
22665                    .child(
22666                        Label::new(format!(
22667                            "{} Comment{}",
22668                            comment_count,
22669                            if comment_count == 1 { "" } else { "s" }
22670                        ))
22671                        .size(LabelSize::Small)
22672                        .color(Color::Muted),
22673                    ),
22674            )
22675            // Comments list (when expanded)
22676            .when(expanded, |el| {
22677                el.children(comments.into_iter().map(|comment| {
22678                    let inline_editor = inline_editors.get(&comment.id).cloned();
22679                    Self::render_comment_row(
22680                        comment,
22681                        inline_editor,
22682                        user_avatar_uri.clone(),
22683                        avatar_size,
22684                        action_icon_size,
22685                        colors,
22686                    )
22687                }))
22688            })
22689    }
22690
22691    fn render_comment_row(
22692        comment: StoredReviewComment,
22693        inline_editor: Option<Entity<Editor>>,
22694        user_avatar_uri: Option<SharedUri>,
22695        avatar_size: Pixels,
22696        action_icon_size: IconSize,
22697        colors: &theme::ThemeColors,
22698    ) -> impl IntoElement {
22699        let comment_id = comment.id;
22700        let is_editing = inline_editor.is_some();
22701
22702        h_flex()
22703            .w_full()
22704            .items_center()
22705            .gap_2()
22706            .px_2()
22707            .py_1p5()
22708            .rounded_md()
22709            .bg(colors.surface_background)
22710            .child(
22711                div()
22712                    .size(avatar_size)
22713                    .flex_shrink_0()
22714                    .rounded_full()
22715                    .overflow_hidden()
22716                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22717                        Avatar::new(avatar_uri.clone())
22718                            .size(avatar_size)
22719                            .into_any_element()
22720                    } else {
22721                        Icon::new(IconName::Person)
22722                            .size(IconSize::Small)
22723                            .color(ui::Color::Muted)
22724                            .into_any_element()
22725                    }),
22726            )
22727            .child(if let Some(editor) = inline_editor {
22728                // Inline edit mode: show an editable text field
22729                div()
22730                    .flex_1()
22731                    .border_1()
22732                    .border_color(colors.border)
22733                    .rounded_md()
22734                    .bg(colors.editor_background)
22735                    .px_2()
22736                    .py_1()
22737                    .child(editor)
22738                    .into_any_element()
22739            } else {
22740                // Display mode: show the comment text
22741                div()
22742                    .flex_1()
22743                    .text_sm()
22744                    .text_color(colors.text)
22745                    .child(comment.comment)
22746                    .into_any_element()
22747            })
22748            .child(if is_editing {
22749                // Editing mode: show close and confirm buttons
22750                h_flex()
22751                    .gap_1()
22752                    .child(
22753                        IconButton::new(
22754                            format!("diff-review-cancel-edit-{comment_id}"),
22755                            IconName::Close,
22756                        )
22757                        .icon_color(ui::Color::Muted)
22758                        .icon_size(action_icon_size)
22759                        .tooltip(Tooltip::text("Cancel"))
22760                        .on_click(move |_, window, cx| {
22761                            window.dispatch_action(
22762                                Box::new(crate::actions::CancelEditReviewComment {
22763                                    id: comment_id,
22764                                }),
22765                                cx,
22766                            );
22767                        }),
22768                    )
22769                    .child(
22770                        IconButton::new(
22771                            format!("diff-review-confirm-edit-{comment_id}"),
22772                            IconName::Return,
22773                        )
22774                        .icon_color(ui::Color::Muted)
22775                        .icon_size(action_icon_size)
22776                        .tooltip(Tooltip::text("Confirm"))
22777                        .on_click(move |_, window, cx| {
22778                            window.dispatch_action(
22779                                Box::new(crate::actions::ConfirmEditReviewComment {
22780                                    id: comment_id,
22781                                }),
22782                                cx,
22783                            );
22784                        }),
22785                    )
22786                    .into_any_element()
22787            } else {
22788                // Display mode: no action buttons for now (edit/delete not yet implemented)
22789                gpui::Empty.into_any_element()
22790            })
22791    }
22792
22793    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22794        if self.display_map.read(cx).masked != masked {
22795            self.display_map.update(cx, |map, _| map.masked = masked);
22796        }
22797        cx.notify()
22798    }
22799
22800    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22801        self.show_wrap_guides = Some(show_wrap_guides);
22802        cx.notify();
22803    }
22804
22805    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22806        self.show_indent_guides = Some(show_indent_guides);
22807        cx.notify();
22808    }
22809
22810    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22811        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22812            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22813                && let Some(dir) = file.abs_path(cx).parent()
22814            {
22815                return Some(dir.to_owned());
22816            }
22817        }
22818
22819        None
22820    }
22821
22822    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22823        self.active_excerpt(cx)?
22824            .1
22825            .read(cx)
22826            .file()
22827            .and_then(|f| f.as_local())
22828    }
22829
22830    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22831        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22832            let buffer = buffer.read(cx);
22833            if let Some(project_path) = buffer.project_path(cx) {
22834                let project = self.project()?.read(cx);
22835                project.absolute_path(&project_path, cx)
22836            } else {
22837                buffer
22838                    .file()
22839                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22840            }
22841        })
22842    }
22843
22844    pub fn reveal_in_finder(
22845        &mut self,
22846        _: &RevealInFileManager,
22847        _window: &mut Window,
22848        cx: &mut Context<Self>,
22849    ) {
22850        if let Some(path) = self.target_file_abs_path(cx) {
22851            if let Some(project) = self.project() {
22852                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22853            } else {
22854                cx.reveal_path(&path);
22855            }
22856        }
22857    }
22858
22859    pub fn copy_path(
22860        &mut self,
22861        _: &zed_actions::workspace::CopyPath,
22862        _window: &mut Window,
22863        cx: &mut Context<Self>,
22864    ) {
22865        if let Some(path) = self.target_file_abs_path(cx)
22866            && let Some(path) = path.to_str()
22867        {
22868            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22869        } else {
22870            cx.propagate();
22871        }
22872    }
22873
22874    pub fn copy_relative_path(
22875        &mut self,
22876        _: &zed_actions::workspace::CopyRelativePath,
22877        _window: &mut Window,
22878        cx: &mut Context<Self>,
22879    ) {
22880        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22881            let project = self.project()?.read(cx);
22882            let path = buffer.read(cx).file()?.path();
22883            let path = path.display(project.path_style(cx));
22884            Some(path)
22885        }) {
22886            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22887        } else {
22888            cx.propagate();
22889        }
22890    }
22891
22892    /// Returns the project path for the editor's buffer, if any buffer is
22893    /// opened in the editor.
22894    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22895        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22896            buffer.read(cx).project_path(cx)
22897        } else {
22898            None
22899        }
22900    }
22901
22902    // Returns true if the editor handled a go-to-line request
22903    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22904        maybe!({
22905            let breakpoint_store = self.breakpoint_store.as_ref()?;
22906
22907            let (active_stack_frame, debug_line_pane_id) = {
22908                let store = breakpoint_store.read(cx);
22909                let active_stack_frame = store.active_position().cloned();
22910                let debug_line_pane_id = store.active_debug_line_pane_id();
22911                (active_stack_frame, debug_line_pane_id)
22912            };
22913
22914            let Some(active_stack_frame) = active_stack_frame else {
22915                self.clear_row_highlights::<ActiveDebugLine>();
22916                return None;
22917            };
22918
22919            if let Some(debug_line_pane_id) = debug_line_pane_id {
22920                if let Some(workspace) = self
22921                    .workspace
22922                    .as_ref()
22923                    .and_then(|(workspace, _)| workspace.upgrade())
22924                {
22925                    let editor_pane_id = workspace
22926                        .read(cx)
22927                        .pane_for_item_id(cx.entity_id())
22928                        .map(|pane| pane.entity_id());
22929
22930                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22931                        self.clear_row_highlights::<ActiveDebugLine>();
22932                        return None;
22933                    }
22934                }
22935            }
22936
22937            let position = active_stack_frame.position;
22938            let buffer_id = position.buffer_id?;
22939            let snapshot = self
22940                .project
22941                .as_ref()?
22942                .read(cx)
22943                .buffer_for_id(buffer_id, cx)?
22944                .read(cx)
22945                .snapshot();
22946
22947            let mut handled = false;
22948            for (id, _, ExcerptRange { context, .. }) in
22949                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22950            {
22951                if context.start.cmp(&position, &snapshot).is_ge()
22952                    || context.end.cmp(&position, &snapshot).is_lt()
22953                {
22954                    continue;
22955                }
22956                let snapshot = self.buffer.read(cx).snapshot(cx);
22957                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22958
22959                handled = true;
22960                self.clear_row_highlights::<ActiveDebugLine>();
22961
22962                self.go_to_line::<ActiveDebugLine>(
22963                    multibuffer_anchor,
22964                    Some(cx.theme().colors().editor_debugger_active_line_background),
22965                    window,
22966                    cx,
22967                );
22968
22969                cx.notify();
22970            }
22971
22972            handled.then_some(())
22973        })
22974        .is_some()
22975    }
22976
22977    pub fn copy_file_name_without_extension(
22978        &mut self,
22979        _: &CopyFileNameWithoutExtension,
22980        _: &mut Window,
22981        cx: &mut Context<Self>,
22982    ) {
22983        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22984            let file = buffer.read(cx).file()?;
22985            file.path().file_stem()
22986        }) {
22987            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22988        }
22989    }
22990
22991    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22992        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22993            let file = buffer.read(cx).file()?;
22994            Some(file.file_name(cx))
22995        }) {
22996            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22997        }
22998    }
22999
23000    pub fn toggle_git_blame(
23001        &mut self,
23002        _: &::git::Blame,
23003        window: &mut Window,
23004        cx: &mut Context<Self>,
23005    ) {
23006        self.show_git_blame_gutter = !self.show_git_blame_gutter;
23007
23008        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
23009            self.start_git_blame(true, window, cx);
23010        }
23011
23012        cx.notify();
23013    }
23014
23015    pub fn toggle_git_blame_inline(
23016        &mut self,
23017        _: &ToggleGitBlameInline,
23018        window: &mut Window,
23019        cx: &mut Context<Self>,
23020    ) {
23021        self.toggle_git_blame_inline_internal(true, window, cx);
23022        cx.notify();
23023    }
23024
23025    pub fn open_git_blame_commit(
23026        &mut self,
23027        _: &OpenGitBlameCommit,
23028        window: &mut Window,
23029        cx: &mut Context<Self>,
23030    ) {
23031        self.open_git_blame_commit_internal(window, cx);
23032    }
23033
23034    fn open_git_blame_commit_internal(
23035        &mut self,
23036        window: &mut Window,
23037        cx: &mut Context<Self>,
23038    ) -> Option<()> {
23039        let blame = self.blame.as_ref()?;
23040        let snapshot = self.snapshot(window, cx);
23041        let cursor = self
23042            .selections
23043            .newest::<Point>(&snapshot.display_snapshot)
23044            .head();
23045        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
23046        let (_, blame_entry) = blame
23047            .update(cx, |blame, cx| {
23048                blame
23049                    .blame_for_rows(
23050                        &[RowInfo {
23051                            buffer_id: Some(buffer.remote_id()),
23052                            buffer_row: Some(point.row),
23053                            ..Default::default()
23054                        }],
23055                        cx,
23056                    )
23057                    .next()
23058            })
23059            .flatten()?;
23060        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23061        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23062        let workspace = self.workspace()?.downgrade();
23063        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23064        None
23065    }
23066
23067    pub fn git_blame_inline_enabled(&self) -> bool {
23068        self.git_blame_inline_enabled
23069    }
23070
23071    pub fn toggle_selection_menu(
23072        &mut self,
23073        _: &ToggleSelectionMenu,
23074        _: &mut Window,
23075        cx: &mut Context<Self>,
23076    ) {
23077        self.show_selection_menu = self
23078            .show_selection_menu
23079            .map(|show_selections_menu| !show_selections_menu)
23080            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23081
23082        cx.notify();
23083    }
23084
23085    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23086        self.show_selection_menu
23087            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23088    }
23089
23090    fn start_git_blame(
23091        &mut self,
23092        user_triggered: bool,
23093        window: &mut Window,
23094        cx: &mut Context<Self>,
23095    ) {
23096        if let Some(project) = self.project() {
23097            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23098                && buffer.read(cx).file().is_none()
23099            {
23100                return;
23101            }
23102
23103            let focused = self.focus_handle(cx).contains_focused(window, cx);
23104
23105            let project = project.clone();
23106            let blame = cx
23107                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23108            self.blame_subscription =
23109                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23110            self.blame = Some(blame);
23111        }
23112    }
23113
23114    fn toggle_git_blame_inline_internal(
23115        &mut self,
23116        user_triggered: bool,
23117        window: &mut Window,
23118        cx: &mut Context<Self>,
23119    ) {
23120        if self.git_blame_inline_enabled {
23121            self.git_blame_inline_enabled = false;
23122            self.show_git_blame_inline = false;
23123            self.show_git_blame_inline_delay_task.take();
23124        } else {
23125            self.git_blame_inline_enabled = true;
23126            self.start_git_blame_inline(user_triggered, window, cx);
23127        }
23128
23129        cx.notify();
23130    }
23131
23132    fn start_git_blame_inline(
23133        &mut self,
23134        user_triggered: bool,
23135        window: &mut Window,
23136        cx: &mut Context<Self>,
23137    ) {
23138        self.start_git_blame(user_triggered, window, cx);
23139
23140        if ProjectSettings::get_global(cx)
23141            .git
23142            .inline_blame_delay()
23143            .is_some()
23144        {
23145            self.start_inline_blame_timer(window, cx);
23146        } else {
23147            self.show_git_blame_inline = true
23148        }
23149    }
23150
23151    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23152        self.blame.as_ref()
23153    }
23154
23155    pub fn show_git_blame_gutter(&self) -> bool {
23156        self.show_git_blame_gutter
23157    }
23158
23159    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23160        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23161    }
23162
23163    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23164        self.show_git_blame_inline
23165            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23166            && !self.newest_selection_head_on_empty_line(cx)
23167            && self.has_blame_entries(cx)
23168    }
23169
23170    fn has_blame_entries(&self, cx: &App) -> bool {
23171        self.blame()
23172            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23173    }
23174
23175    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23176        let cursor_anchor = self.selections.newest_anchor().head();
23177
23178        let snapshot = self.buffer.read(cx).snapshot(cx);
23179        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23180
23181        snapshot.line_len(buffer_row) == 0
23182    }
23183
23184    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23185        let buffer_and_selection = maybe!({
23186            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23187            let selection_range = selection.range();
23188
23189            let multi_buffer = self.buffer().read(cx);
23190            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23191            let buffer_ranges = multi_buffer_snapshot
23192                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23193
23194            let (buffer, range, _) = if selection.reversed {
23195                buffer_ranges.first()
23196            } else {
23197                buffer_ranges.last()
23198            }?;
23199
23200            let buffer_range = range.to_point(buffer);
23201
23202            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23203                return Some((
23204                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23205                    buffer_range.start.row..buffer_range.end.row,
23206                ));
23207            };
23208
23209            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23210            let start =
23211                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23212            let end =
23213                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23214
23215            Some((
23216                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23217                start.row..end.row,
23218            ))
23219        });
23220
23221        let Some((buffer, selection)) = buffer_and_selection else {
23222            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23223        };
23224
23225        let Some(project) = self.project() else {
23226            return Task::ready(Err(anyhow!("editor does not have project")));
23227        };
23228
23229        project.update(cx, |project, cx| {
23230            project.get_permalink_to_line(&buffer, selection, cx)
23231        })
23232    }
23233
23234    pub fn copy_permalink_to_line(
23235        &mut self,
23236        _: &CopyPermalinkToLine,
23237        window: &mut Window,
23238        cx: &mut Context<Self>,
23239    ) {
23240        let permalink_task = self.get_permalink_to_line(cx);
23241        let workspace = self.workspace();
23242
23243        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23244            Ok(permalink) => {
23245                cx.update(|_, cx| {
23246                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23247                })
23248                .ok();
23249            }
23250            Err(err) => {
23251                let message = format!("Failed to copy permalink: {err}");
23252
23253                anyhow::Result::<()>::Err(err).log_err();
23254
23255                if let Some(workspace) = workspace {
23256                    workspace
23257                        .update_in(cx, |workspace, _, cx| {
23258                            struct CopyPermalinkToLine;
23259
23260                            workspace.show_toast(
23261                                Toast::new(
23262                                    NotificationId::unique::<CopyPermalinkToLine>(),
23263                                    message,
23264                                ),
23265                                cx,
23266                            )
23267                        })
23268                        .ok();
23269                }
23270            }
23271        })
23272        .detach();
23273    }
23274
23275    pub fn copy_file_location(
23276        &mut self,
23277        _: &CopyFileLocation,
23278        _: &mut Window,
23279        cx: &mut Context<Self>,
23280    ) {
23281        let selection = self
23282            .selections
23283            .newest::<Point>(&self.display_snapshot(cx))
23284            .start
23285            .row
23286            + 1;
23287        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23288            let project = self.project()?.read(cx);
23289            let file = buffer.read(cx).file()?;
23290            let path = file.path().display(project.path_style(cx));
23291
23292            Some(format!("{path}:{selection}"))
23293        }) {
23294            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23295        }
23296    }
23297
23298    pub fn open_permalink_to_line(
23299        &mut self,
23300        _: &OpenPermalinkToLine,
23301        window: &mut Window,
23302        cx: &mut Context<Self>,
23303    ) {
23304        let permalink_task = self.get_permalink_to_line(cx);
23305        let workspace = self.workspace();
23306
23307        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23308            Ok(permalink) => {
23309                cx.update(|_, cx| {
23310                    cx.open_url(permalink.as_ref());
23311                })
23312                .ok();
23313            }
23314            Err(err) => {
23315                let message = format!("Failed to open permalink: {err}");
23316
23317                anyhow::Result::<()>::Err(err).log_err();
23318
23319                if let Some(workspace) = workspace {
23320                    workspace.update(cx, |workspace, cx| {
23321                        struct OpenPermalinkToLine;
23322
23323                        workspace.show_toast(
23324                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23325                            cx,
23326                        )
23327                    });
23328                }
23329            }
23330        })
23331        .detach();
23332    }
23333
23334    pub fn insert_uuid_v4(
23335        &mut self,
23336        _: &InsertUuidV4,
23337        window: &mut Window,
23338        cx: &mut Context<Self>,
23339    ) {
23340        self.insert_uuid(UuidVersion::V4, window, cx);
23341    }
23342
23343    pub fn insert_uuid_v7(
23344        &mut self,
23345        _: &InsertUuidV7,
23346        window: &mut Window,
23347        cx: &mut Context<Self>,
23348    ) {
23349        self.insert_uuid(UuidVersion::V7, window, cx);
23350    }
23351
23352    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23353        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23354        self.transact(window, cx, |this, window, cx| {
23355            let edits = this
23356                .selections
23357                .all::<Point>(&this.display_snapshot(cx))
23358                .into_iter()
23359                .map(|selection| {
23360                    let uuid = match version {
23361                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23362                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23363                    };
23364
23365                    (selection.range(), uuid.to_string())
23366                });
23367            this.edit(edits, cx);
23368            this.refresh_edit_prediction(true, false, window, cx);
23369        });
23370    }
23371
23372    pub fn open_selections_in_multibuffer(
23373        &mut self,
23374        _: &OpenSelectionsInMultibuffer,
23375        window: &mut Window,
23376        cx: &mut Context<Self>,
23377    ) {
23378        let multibuffer = self.buffer.read(cx);
23379
23380        let Some(buffer) = multibuffer.as_singleton() else {
23381            return;
23382        };
23383
23384        let Some(workspace) = self.workspace() else {
23385            return;
23386        };
23387
23388        let title = multibuffer.title(cx).to_string();
23389
23390        let locations = self
23391            .selections
23392            .all_anchors(&self.display_snapshot(cx))
23393            .iter()
23394            .map(|selection| {
23395                (
23396                    buffer.clone(),
23397                    (selection.start.text_anchor..selection.end.text_anchor)
23398                        .to_point(buffer.read(cx)),
23399                )
23400            })
23401            .into_group_map();
23402
23403        cx.spawn_in(window, async move |_, cx| {
23404            workspace.update_in(cx, |workspace, window, cx| {
23405                Self::open_locations_in_multibuffer(
23406                    workspace,
23407                    locations,
23408                    format!("Selections for '{title}'"),
23409                    false,
23410                    false,
23411                    MultibufferSelectionMode::All,
23412                    window,
23413                    cx,
23414                );
23415            })
23416        })
23417        .detach();
23418    }
23419
23420    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23421    /// last highlight added will be used.
23422    ///
23423    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23424    pub fn highlight_rows<T: 'static>(
23425        &mut self,
23426        range: Range<Anchor>,
23427        color: Hsla,
23428        options: RowHighlightOptions,
23429        cx: &mut Context<Self>,
23430    ) {
23431        let snapshot = self.buffer().read(cx).snapshot(cx);
23432        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23433        let ix = row_highlights.binary_search_by(|highlight| {
23434            Ordering::Equal
23435                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23436                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23437        });
23438
23439        if let Err(mut ix) = ix {
23440            let index = post_inc(&mut self.highlight_order);
23441
23442            // If this range intersects with the preceding highlight, then merge it with
23443            // the preceding highlight. Otherwise insert a new highlight.
23444            let mut merged = false;
23445            if ix > 0 {
23446                let prev_highlight = &mut row_highlights[ix - 1];
23447                if prev_highlight
23448                    .range
23449                    .end
23450                    .cmp(&range.start, &snapshot)
23451                    .is_ge()
23452                {
23453                    ix -= 1;
23454                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23455                        prev_highlight.range.end = range.end;
23456                    }
23457                    merged = true;
23458                    prev_highlight.index = index;
23459                    prev_highlight.color = color;
23460                    prev_highlight.options = options;
23461                }
23462            }
23463
23464            if !merged {
23465                row_highlights.insert(
23466                    ix,
23467                    RowHighlight {
23468                        range,
23469                        index,
23470                        color,
23471                        options,
23472                        type_id: TypeId::of::<T>(),
23473                    },
23474                );
23475            }
23476
23477            // If any of the following highlights intersect with this one, merge them.
23478            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23479                let highlight = &row_highlights[ix];
23480                if next_highlight
23481                    .range
23482                    .start
23483                    .cmp(&highlight.range.end, &snapshot)
23484                    .is_le()
23485                {
23486                    if next_highlight
23487                        .range
23488                        .end
23489                        .cmp(&highlight.range.end, &snapshot)
23490                        .is_gt()
23491                    {
23492                        row_highlights[ix].range.end = next_highlight.range.end;
23493                    }
23494                    row_highlights.remove(ix + 1);
23495                } else {
23496                    break;
23497                }
23498            }
23499        }
23500    }
23501
23502    /// Remove any highlighted row ranges of the given type that intersect the
23503    /// given ranges.
23504    pub fn remove_highlighted_rows<T: 'static>(
23505        &mut self,
23506        ranges_to_remove: Vec<Range<Anchor>>,
23507        cx: &mut Context<Self>,
23508    ) {
23509        let snapshot = self.buffer().read(cx).snapshot(cx);
23510        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23511        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23512        row_highlights.retain(|highlight| {
23513            while let Some(range_to_remove) = ranges_to_remove.peek() {
23514                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23515                    Ordering::Less | Ordering::Equal => {
23516                        ranges_to_remove.next();
23517                    }
23518                    Ordering::Greater => {
23519                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23520                            Ordering::Less | Ordering::Equal => {
23521                                return false;
23522                            }
23523                            Ordering::Greater => break,
23524                        }
23525                    }
23526                }
23527            }
23528
23529            true
23530        })
23531    }
23532
23533    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23534    pub fn clear_row_highlights<T: 'static>(&mut self) {
23535        self.highlighted_rows.remove(&TypeId::of::<T>());
23536    }
23537
23538    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23539    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23540        self.highlighted_rows
23541            .get(&TypeId::of::<T>())
23542            .map_or(&[] as &[_], |vec| vec.as_slice())
23543            .iter()
23544            .map(|highlight| (highlight.range.clone(), highlight.color))
23545    }
23546
23547    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23548    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23549    /// Allows to ignore certain kinds of highlights.
23550    pub fn highlighted_display_rows(
23551        &self,
23552        window: &mut Window,
23553        cx: &mut App,
23554    ) -> BTreeMap<DisplayRow, LineHighlight> {
23555        let snapshot = self.snapshot(window, cx);
23556        let mut used_highlight_orders = HashMap::default();
23557        self.highlighted_rows
23558            .iter()
23559            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23560            .fold(
23561                BTreeMap::<DisplayRow, LineHighlight>::new(),
23562                |mut unique_rows, highlight| {
23563                    let start = highlight.range.start.to_display_point(&snapshot);
23564                    let end = highlight.range.end.to_display_point(&snapshot);
23565                    let start_row = start.row().0;
23566                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23567                    {
23568                        end.row().0.saturating_sub(1)
23569                    } else {
23570                        end.row().0
23571                    };
23572                    for row in start_row..=end_row {
23573                        let used_index =
23574                            used_highlight_orders.entry(row).or_insert(highlight.index);
23575                        if highlight.index >= *used_index {
23576                            *used_index = highlight.index;
23577                            unique_rows.insert(
23578                                DisplayRow(row),
23579                                LineHighlight {
23580                                    include_gutter: highlight.options.include_gutter,
23581                                    border: None,
23582                                    background: highlight.color.into(),
23583                                    type_id: Some(highlight.type_id),
23584                                },
23585                            );
23586                        }
23587                    }
23588                    unique_rows
23589                },
23590            )
23591    }
23592
23593    pub fn highlighted_display_row_for_autoscroll(
23594        &self,
23595        snapshot: &DisplaySnapshot,
23596    ) -> Option<DisplayRow> {
23597        self.highlighted_rows
23598            .values()
23599            .flat_map(|highlighted_rows| highlighted_rows.iter())
23600            .filter_map(|highlight| {
23601                if highlight.options.autoscroll {
23602                    Some(highlight.range.start.to_display_point(snapshot).row())
23603                } else {
23604                    None
23605                }
23606            })
23607            .min()
23608    }
23609
23610    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23611        self.highlight_background(
23612            HighlightKey::SearchWithinRange,
23613            ranges,
23614            |_, colors| colors.colors().editor_document_highlight_read_background,
23615            cx,
23616        )
23617    }
23618
23619    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23620        self.breadcrumb_header = Some(new_header);
23621    }
23622
23623    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23624        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23625    }
23626
23627    pub fn highlight_background(
23628        &mut self,
23629        key: HighlightKey,
23630        ranges: &[Range<Anchor>],
23631        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23632        cx: &mut Context<Self>,
23633    ) {
23634        self.background_highlights
23635            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23636        self.scrollbar_marker_state.dirty = true;
23637        cx.notify();
23638    }
23639
23640    pub fn clear_background_highlights(
23641        &mut self,
23642        key: HighlightKey,
23643        cx: &mut Context<Self>,
23644    ) -> Option<BackgroundHighlight> {
23645        let text_highlights = self.background_highlights.remove(&key)?;
23646        if !text_highlights.1.is_empty() {
23647            self.scrollbar_marker_state.dirty = true;
23648            cx.notify();
23649        }
23650        Some(text_highlights)
23651    }
23652
23653    pub fn highlight_gutter<T: 'static>(
23654        &mut self,
23655        ranges: impl Into<Vec<Range<Anchor>>>,
23656        color_fetcher: fn(&App) -> Hsla,
23657        cx: &mut Context<Self>,
23658    ) {
23659        self.gutter_highlights
23660            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23661        cx.notify();
23662    }
23663
23664    pub fn clear_gutter_highlights<T: 'static>(
23665        &mut self,
23666        cx: &mut Context<Self>,
23667    ) -> Option<GutterHighlight> {
23668        cx.notify();
23669        self.gutter_highlights.remove(&TypeId::of::<T>())
23670    }
23671
23672    pub fn insert_gutter_highlight<T: 'static>(
23673        &mut self,
23674        range: Range<Anchor>,
23675        color_fetcher: fn(&App) -> Hsla,
23676        cx: &mut Context<Self>,
23677    ) {
23678        let snapshot = self.buffer().read(cx).snapshot(cx);
23679        let mut highlights = self
23680            .gutter_highlights
23681            .remove(&TypeId::of::<T>())
23682            .map(|(_, highlights)| highlights)
23683            .unwrap_or_default();
23684        let ix = highlights.binary_search_by(|highlight| {
23685            Ordering::Equal
23686                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23687                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23688        });
23689        if let Err(ix) = ix {
23690            highlights.insert(ix, range);
23691        }
23692        self.gutter_highlights
23693            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23694    }
23695
23696    pub fn remove_gutter_highlights<T: 'static>(
23697        &mut self,
23698        ranges_to_remove: Vec<Range<Anchor>>,
23699        cx: &mut Context<Self>,
23700    ) {
23701        let snapshot = self.buffer().read(cx).snapshot(cx);
23702        let Some((color_fetcher, mut gutter_highlights)) =
23703            self.gutter_highlights.remove(&TypeId::of::<T>())
23704        else {
23705            return;
23706        };
23707        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23708        gutter_highlights.retain(|highlight| {
23709            while let Some(range_to_remove) = ranges_to_remove.peek() {
23710                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23711                    Ordering::Less | Ordering::Equal => {
23712                        ranges_to_remove.next();
23713                    }
23714                    Ordering::Greater => {
23715                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23716                            Ordering::Less | Ordering::Equal => {
23717                                return false;
23718                            }
23719                            Ordering::Greater => break,
23720                        }
23721                    }
23722                }
23723            }
23724
23725            true
23726        });
23727        self.gutter_highlights
23728            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23729    }
23730
23731    #[cfg(any(test, feature = "test-support"))]
23732    pub fn all_text_highlights(
23733        &self,
23734        window: &mut Window,
23735        cx: &mut Context<Self>,
23736    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23737        let snapshot = self.snapshot(window, cx);
23738        self.display_map.update(cx, |display_map, _| {
23739            display_map
23740                .all_text_highlights()
23741                .map(|(_, highlight)| {
23742                    let (style, ranges) = highlight.as_ref();
23743                    (
23744                        *style,
23745                        ranges
23746                            .iter()
23747                            .map(|range| range.clone().to_display_points(&snapshot))
23748                            .collect(),
23749                    )
23750                })
23751                .collect()
23752        })
23753    }
23754
23755    #[cfg(any(test, feature = "test-support"))]
23756    pub fn all_text_background_highlights(
23757        &self,
23758        window: &mut Window,
23759        cx: &mut Context<Self>,
23760    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23761        let snapshot = self.snapshot(window, cx);
23762        let buffer = &snapshot.buffer_snapshot();
23763        let start = buffer.anchor_before(MultiBufferOffset(0));
23764        let end = buffer.anchor_after(buffer.len());
23765        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23766    }
23767
23768    #[cfg(any(test, feature = "test-support"))]
23769    pub fn sorted_background_highlights_in_range(
23770        &self,
23771        search_range: Range<Anchor>,
23772        display_snapshot: &DisplaySnapshot,
23773        theme: &Theme,
23774    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23775        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23776        res.sort_by(|a, b| {
23777            a.0.start
23778                .cmp(&b.0.start)
23779                .then_with(|| a.0.end.cmp(&b.0.end))
23780                .then_with(|| a.1.cmp(&b.1))
23781        });
23782        res
23783    }
23784
23785    #[cfg(any(test, feature = "test-support"))]
23786    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23787        let snapshot = self.buffer().read(cx).snapshot(cx);
23788
23789        let highlights = self
23790            .background_highlights
23791            .get(&HighlightKey::BufferSearchHighlights);
23792
23793        if let Some((_color, ranges)) = highlights {
23794            ranges
23795                .iter()
23796                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23797                .collect_vec()
23798        } else {
23799            vec![]
23800        }
23801    }
23802
23803    fn document_highlights_for_position<'a>(
23804        &'a self,
23805        position: Anchor,
23806        buffer: &'a MultiBufferSnapshot,
23807    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23808        let read_highlights = self
23809            .background_highlights
23810            .get(&HighlightKey::DocumentHighlightRead)
23811            .map(|h| &h.1);
23812        let write_highlights = self
23813            .background_highlights
23814            .get(&HighlightKey::DocumentHighlightWrite)
23815            .map(|h| &h.1);
23816        let left_position = position.bias_left(buffer);
23817        let right_position = position.bias_right(buffer);
23818        read_highlights
23819            .into_iter()
23820            .chain(write_highlights)
23821            .flat_map(move |ranges| {
23822                let start_ix = match ranges.binary_search_by(|probe| {
23823                    let cmp = probe.end.cmp(&left_position, buffer);
23824                    if cmp.is_ge() {
23825                        Ordering::Greater
23826                    } else {
23827                        Ordering::Less
23828                    }
23829                }) {
23830                    Ok(i) | Err(i) => i,
23831                };
23832
23833                ranges[start_ix..]
23834                    .iter()
23835                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23836            })
23837    }
23838
23839    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23840        self.background_highlights
23841            .get(&key)
23842            .is_some_and(|(_, highlights)| !highlights.is_empty())
23843    }
23844
23845    /// Returns all background highlights for a given range.
23846    ///
23847    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23848    pub fn background_highlights_in_range(
23849        &self,
23850        search_range: Range<Anchor>,
23851        display_snapshot: &DisplaySnapshot,
23852        theme: &Theme,
23853    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23854        let mut results = Vec::new();
23855        for (color_fetcher, ranges) in self.background_highlights.values() {
23856            let start_ix = match ranges.binary_search_by(|probe| {
23857                let cmp = probe
23858                    .end
23859                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23860                if cmp.is_gt() {
23861                    Ordering::Greater
23862                } else {
23863                    Ordering::Less
23864                }
23865            }) {
23866                Ok(i) | Err(i) => i,
23867            };
23868            for (index, range) in ranges[start_ix..].iter().enumerate() {
23869                if range
23870                    .start
23871                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23872                    .is_ge()
23873                {
23874                    break;
23875                }
23876
23877                let color = color_fetcher(&(start_ix + index), theme);
23878                let start = range.start.to_display_point(display_snapshot);
23879                let end = range.end.to_display_point(display_snapshot);
23880                results.push((start..end, color))
23881            }
23882        }
23883        results
23884    }
23885
23886    pub fn gutter_highlights_in_range(
23887        &self,
23888        search_range: Range<Anchor>,
23889        display_snapshot: &DisplaySnapshot,
23890        cx: &App,
23891    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23892        let mut results = Vec::new();
23893        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23894            let color = color_fetcher(cx);
23895            let start_ix = match ranges.binary_search_by(|probe| {
23896                let cmp = probe
23897                    .end
23898                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23899                if cmp.is_gt() {
23900                    Ordering::Greater
23901                } else {
23902                    Ordering::Less
23903                }
23904            }) {
23905                Ok(i) | Err(i) => i,
23906            };
23907            for range in &ranges[start_ix..] {
23908                if range
23909                    .start
23910                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23911                    .is_ge()
23912                {
23913                    break;
23914                }
23915
23916                let start = range.start.to_display_point(display_snapshot);
23917                let end = range.end.to_display_point(display_snapshot);
23918                results.push((start..end, color))
23919            }
23920        }
23921        results
23922    }
23923
23924    /// Get the text ranges corresponding to the redaction query
23925    pub fn redacted_ranges(
23926        &self,
23927        search_range: Range<Anchor>,
23928        display_snapshot: &DisplaySnapshot,
23929        cx: &App,
23930    ) -> Vec<Range<DisplayPoint>> {
23931        display_snapshot
23932            .buffer_snapshot()
23933            .redacted_ranges(search_range, |file| {
23934                if let Some(file) = file {
23935                    file.is_private()
23936                        && EditorSettings::get(
23937                            Some(SettingsLocation {
23938                                worktree_id: file.worktree_id(cx),
23939                                path: file.path().as_ref(),
23940                            }),
23941                            cx,
23942                        )
23943                        .redact_private_values
23944                } else {
23945                    false
23946                }
23947            })
23948            .map(|range| {
23949                range.start.to_display_point(display_snapshot)
23950                    ..range.end.to_display_point(display_snapshot)
23951            })
23952            .collect()
23953    }
23954
23955    pub fn highlight_text_key(
23956        &mut self,
23957        key: HighlightKey,
23958        ranges: Vec<Range<Anchor>>,
23959        style: HighlightStyle,
23960        merge: bool,
23961        cx: &mut Context<Self>,
23962    ) {
23963        self.display_map.update(cx, |map, cx| {
23964            map.highlight_text(key, ranges, style, merge, cx);
23965        });
23966        cx.notify();
23967    }
23968
23969    pub fn highlight_text(
23970        &mut self,
23971        key: HighlightKey,
23972        ranges: Vec<Range<Anchor>>,
23973        style: HighlightStyle,
23974        cx: &mut Context<Self>,
23975    ) {
23976        self.display_map.update(cx, |map, cx| {
23977            map.highlight_text(key, ranges, style, false, cx)
23978        });
23979        cx.notify();
23980    }
23981
23982    pub fn text_highlights<'a>(
23983        &'a self,
23984        key: HighlightKey,
23985        cx: &'a App,
23986    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23987        self.display_map.read(cx).text_highlights(key)
23988    }
23989
23990    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23991        let cleared = self
23992            .display_map
23993            .update(cx, |map, _| map.clear_highlights(key));
23994        if cleared {
23995            cx.notify();
23996        }
23997    }
23998
23999    pub fn clear_highlights_with(
24000        &mut self,
24001        f: &mut dyn FnMut(&HighlightKey) -> bool,
24002        cx: &mut Context<Self>,
24003    ) {
24004        let cleared = self
24005            .display_map
24006            .update(cx, |map, _| map.clear_highlights_with(f));
24007        if cleared {
24008            cx.notify();
24009        }
24010    }
24011
24012    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
24013        (self.read_only(cx) || self.blink_manager.read(cx).visible())
24014            && self.focus_handle.is_focused(window)
24015    }
24016
24017    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
24018        self.show_cursor_when_unfocused = is_enabled;
24019        cx.notify();
24020    }
24021
24022    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
24023        cx.notify();
24024    }
24025
24026    fn on_debug_session_event(
24027        &mut self,
24028        _session: Entity<Session>,
24029        event: &SessionEvent,
24030        cx: &mut Context<Self>,
24031    ) {
24032        if let SessionEvent::InvalidateInlineValue = event {
24033            self.refresh_inline_values(cx);
24034        }
24035    }
24036
24037    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
24038        let Some(semantics) = self.semantics_provider.clone() else {
24039            return;
24040        };
24041
24042        if !self.inline_value_cache.enabled {
24043            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24044            self.splice_inlays(&inlays, Vec::new(), cx);
24045            return;
24046        }
24047
24048        let current_execution_position = self
24049            .highlighted_rows
24050            .get(&TypeId::of::<ActiveDebugLine>())
24051            .and_then(|lines| lines.last().map(|line| line.range.end));
24052
24053        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24054            let inline_values = editor
24055                .update(cx, |editor, cx| {
24056                    let Some(current_execution_position) = current_execution_position else {
24057                        return Some(Task::ready(Ok(Vec::new())));
24058                    };
24059
24060                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24061                        let snapshot = buffer.snapshot(cx);
24062
24063                        let excerpt = snapshot.excerpt_containing(
24064                            current_execution_position..current_execution_position,
24065                        )?;
24066
24067                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24068                    })?;
24069
24070                    let range =
24071                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24072
24073                    semantics.inline_values(buffer, range, cx)
24074                })
24075                .ok()
24076                .flatten()?
24077                .await
24078                .context("refreshing debugger inlays")
24079                .log_err()?;
24080
24081            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24082
24083            for (buffer_id, inline_value) in inline_values
24084                .into_iter()
24085                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24086            {
24087                buffer_inline_values
24088                    .entry(buffer_id)
24089                    .or_default()
24090                    .push(inline_value);
24091            }
24092
24093            editor
24094                .update(cx, |editor, cx| {
24095                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24096                    let mut new_inlays = Vec::default();
24097
24098                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24099                        let buffer_id = buffer_snapshot.remote_id();
24100                        buffer_inline_values
24101                            .get(&buffer_id)
24102                            .into_iter()
24103                            .flatten()
24104                            .for_each(|hint| {
24105                                let inlay = Inlay::debugger(
24106                                    post_inc(&mut editor.next_inlay_id),
24107                                    Anchor::in_buffer(excerpt_id, hint.position),
24108                                    hint.text(),
24109                                );
24110                                if !inlay.text().chars().contains(&'\n') {
24111                                    new_inlays.push(inlay);
24112                                }
24113                            });
24114                    }
24115
24116                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24117                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24118
24119                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24120                })
24121                .ok()?;
24122            Some(())
24123        });
24124    }
24125
24126    fn on_buffer_event(
24127        &mut self,
24128        multibuffer: &Entity<MultiBuffer>,
24129        event: &multi_buffer::Event,
24130        window: &mut Window,
24131        cx: &mut Context<Self>,
24132    ) {
24133        match event {
24134            multi_buffer::Event::Edited {
24135                edited_buffer,
24136                is_local,
24137            } => {
24138                self.scrollbar_marker_state.dirty = true;
24139                self.active_indent_guides_state.dirty = true;
24140                self.refresh_active_diagnostics(cx);
24141                self.refresh_code_actions(window, cx);
24142                self.refresh_single_line_folds(window, cx);
24143                let snapshot = self.snapshot(window, cx);
24144                self.refresh_matching_bracket_highlights(&snapshot, cx);
24145                self.refresh_outline_symbols_at_cursor(cx);
24146                self.refresh_sticky_headers(&snapshot, cx);
24147                if *is_local && self.has_active_edit_prediction() {
24148                    self.update_visible_edit_prediction(window, cx);
24149                }
24150
24151                // Clean up orphaned review comments after edits
24152                self.cleanup_orphaned_review_comments(cx);
24153
24154                if let Some(buffer) = edited_buffer {
24155                    if buffer.read(cx).file().is_none() {
24156                        cx.emit(EditorEvent::TitleChanged);
24157                    }
24158
24159                    if self.project.is_some() {
24160                        let buffer_id = buffer.read(cx).remote_id();
24161                        self.register_buffer(buffer_id, cx);
24162                        self.update_lsp_data(Some(buffer_id), window, cx);
24163                        self.refresh_inlay_hints(
24164                            InlayHintRefreshReason::BufferEdited(buffer_id),
24165                            cx,
24166                        );
24167                    }
24168                }
24169
24170                cx.emit(EditorEvent::BufferEdited);
24171                cx.emit(SearchEvent::MatchesInvalidated);
24172
24173                let Some(project) = &self.project else { return };
24174                let (telemetry, is_via_ssh) = {
24175                    let project = project.read(cx);
24176                    let telemetry = project.client().telemetry().clone();
24177                    let is_via_ssh = project.is_via_remote_server();
24178                    (telemetry, is_via_ssh)
24179                };
24180                telemetry.log_edit_event("editor", is_via_ssh);
24181            }
24182            multi_buffer::Event::ExcerptsAdded {
24183                buffer,
24184                predecessor,
24185                excerpts,
24186            } => {
24187                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24188                let buffer_id = buffer.read(cx).remote_id();
24189                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24190                    && let Some(project) = &self.project
24191                {
24192                    update_uncommitted_diff_for_buffer(
24193                        cx.entity(),
24194                        project,
24195                        [buffer.clone()],
24196                        self.buffer.clone(),
24197                        cx,
24198                    )
24199                    .detach();
24200                }
24201                self.semantic_token_state
24202                    .invalidate_buffer(&buffer.read(cx).remote_id());
24203                self.update_lsp_data(Some(buffer_id), window, cx);
24204                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24205                self.colorize_brackets(false, cx);
24206                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24207                cx.emit(EditorEvent::ExcerptsAdded {
24208                    buffer: buffer.clone(),
24209                    predecessor: *predecessor,
24210                    excerpts: excerpts.clone(),
24211                });
24212            }
24213            multi_buffer::Event::ExcerptsRemoved {
24214                ids,
24215                removed_buffer_ids,
24216            } => {
24217                if let Some(inlay_hints) = &mut self.inlay_hints {
24218                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24219                }
24220                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24221                for buffer_id in removed_buffer_ids {
24222                    self.registered_buffers.remove(buffer_id);
24223                    self.tasks
24224                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24225                    self.semantic_token_state.invalidate_buffer(buffer_id);
24226                    self.display_map.update(cx, |display_map, cx| {
24227                        display_map.invalidate_semantic_highlights(*buffer_id);
24228                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24229                    });
24230                }
24231
24232                self.display_map.update(cx, |display_map, cx| {
24233                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24234                });
24235
24236                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24237                cx.emit(EditorEvent::ExcerptsRemoved {
24238                    ids: ids.clone(),
24239                    removed_buffer_ids: removed_buffer_ids.clone(),
24240                });
24241            }
24242            multi_buffer::Event::ExcerptsEdited {
24243                excerpt_ids,
24244                buffer_ids,
24245            } => {
24246                self.display_map.update(cx, |map, cx| {
24247                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24248                });
24249                cx.emit(EditorEvent::ExcerptsEdited {
24250                    ids: excerpt_ids.clone(),
24251                });
24252            }
24253            multi_buffer::Event::ExcerptsExpanded { ids } => {
24254                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24255                self.refresh_document_highlights(cx);
24256                let snapshot = multibuffer.read(cx).snapshot(cx);
24257                for id in ids {
24258                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24259                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24260                        self.semantic_token_state
24261                            .invalidate_buffer(&buffer.remote_id());
24262                    }
24263                }
24264                self.colorize_brackets(false, cx);
24265                self.update_lsp_data(None, window, cx);
24266                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24267            }
24268            multi_buffer::Event::Reparsed(buffer_id) => {
24269                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24270                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24271                self.colorize_brackets(true, cx);
24272                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24273
24274                cx.emit(EditorEvent::Reparsed(*buffer_id));
24275            }
24276            multi_buffer::Event::DiffHunksToggled => {
24277                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24278            }
24279            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24280                if !is_fresh_language {
24281                    self.registered_buffers.remove(&buffer_id);
24282                }
24283                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24284                cx.emit(EditorEvent::Reparsed(*buffer_id));
24285                cx.notify();
24286            }
24287            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24288            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24289            multi_buffer::Event::FileHandleChanged
24290            | multi_buffer::Event::Reloaded
24291            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24292            multi_buffer::Event::DiagnosticsUpdated => {
24293                self.update_diagnostics_state(window, cx);
24294            }
24295            _ => {}
24296        };
24297    }
24298
24299    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24300        if !self.diagnostics_enabled() {
24301            return;
24302        }
24303        self.refresh_active_diagnostics(cx);
24304        self.refresh_inline_diagnostics(true, window, cx);
24305        self.scrollbar_marker_state.dirty = true;
24306        cx.notify();
24307    }
24308
24309    pub fn start_temporary_diff_override(&mut self) {
24310        self.load_diff_task.take();
24311        self.temporary_diff_override = true;
24312    }
24313
24314    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24315        self.temporary_diff_override = false;
24316        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24317        self.buffer.update(cx, |buffer, cx| {
24318            buffer.set_all_diff_hunks_collapsed(cx);
24319        });
24320
24321        if let Some(project) = self.project.clone() {
24322            self.load_diff_task = Some(
24323                update_uncommitted_diff_for_buffer(
24324                    cx.entity(),
24325                    &project,
24326                    self.buffer.read(cx).all_buffers(),
24327                    self.buffer.clone(),
24328                    cx,
24329                )
24330                .shared(),
24331            );
24332        }
24333    }
24334
24335    fn on_display_map_changed(
24336        &mut self,
24337        _: Entity<DisplayMap>,
24338        _: &mut Window,
24339        cx: &mut Context<Self>,
24340    ) {
24341        cx.notify();
24342    }
24343
24344    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24345        if !self.mode.is_full() {
24346            return None;
24347        }
24348
24349        let theme_settings = theme::ThemeSettings::get_global(cx);
24350        let theme = cx.theme();
24351        let accent_colors = theme.accents().clone();
24352
24353        let accent_overrides = theme_settings
24354            .theme_overrides
24355            .get(theme.name.as_ref())
24356            .map(|theme_style| &theme_style.accents)
24357            .into_iter()
24358            .flatten()
24359            .chain(
24360                theme_settings
24361                    .experimental_theme_overrides
24362                    .as_ref()
24363                    .map(|overrides| &overrides.accents)
24364                    .into_iter()
24365                    .flatten(),
24366            )
24367            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24368            .collect();
24369
24370        Some(AccentData {
24371            colors: accent_colors,
24372            overrides: accent_overrides,
24373        })
24374    }
24375
24376    fn fetch_applicable_language_settings(
24377        &self,
24378        cx: &App,
24379    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24380        if !self.mode.is_full() {
24381            return HashMap::default();
24382        }
24383
24384        self.buffer().read(cx).all_buffers().into_iter().fold(
24385            HashMap::default(),
24386            |mut acc, buffer| {
24387                let buffer = buffer.read(cx);
24388                let language = buffer.language().map(|language| language.name());
24389                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24390                    let file = buffer.file();
24391                    v.insert(language_settings(language, file, cx).into_owned());
24392                }
24393                acc
24394            },
24395        )
24396    }
24397
24398    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24399        let new_language_settings = self.fetch_applicable_language_settings(cx);
24400        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24401        self.applicable_language_settings = new_language_settings;
24402
24403        let new_accents = self.fetch_accent_data(cx);
24404        let accents_changed = new_accents != self.accent_data;
24405        self.accent_data = new_accents;
24406
24407        if self.diagnostics_enabled() {
24408            let new_severity = EditorSettings::get_global(cx)
24409                .diagnostics_max_severity
24410                .unwrap_or(DiagnosticSeverity::Hint);
24411            self.set_max_diagnostics_severity(new_severity, cx);
24412        }
24413        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24414        self.update_edit_prediction_settings(cx);
24415        self.refresh_edit_prediction(true, false, window, cx);
24416        self.refresh_inline_values(cx);
24417
24418        let old_cursor_shape = self.cursor_shape;
24419        let old_show_breadcrumbs = self.show_breadcrumbs;
24420
24421        {
24422            let editor_settings = EditorSettings::get_global(cx);
24423            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24424            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24425            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24426            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24427        }
24428
24429        if old_cursor_shape != self.cursor_shape {
24430            cx.emit(EditorEvent::CursorShapeChanged);
24431        }
24432
24433        if old_show_breadcrumbs != self.show_breadcrumbs {
24434            cx.emit(EditorEvent::BreadcrumbsChanged);
24435        }
24436
24437        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24438            let project_settings = ProjectSettings::get_global(cx);
24439            (
24440                project_settings.session.restore_unsaved_buffers,
24441                project_settings.diagnostics.inline.enabled,
24442                project_settings.git.inline_blame.enabled,
24443            )
24444        };
24445        self.buffer_serialization = self
24446            .should_serialize_buffer()
24447            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24448
24449        if self.mode.is_full() {
24450            if self.show_inline_diagnostics != show_inline_diagnostics {
24451                self.show_inline_diagnostics = show_inline_diagnostics;
24452                self.refresh_inline_diagnostics(false, window, cx);
24453            }
24454
24455            if self.git_blame_inline_enabled != inline_blame_enabled {
24456                self.toggle_git_blame_inline_internal(false, window, cx);
24457            }
24458
24459            let minimap_settings = EditorSettings::get_global(cx).minimap;
24460            if self.minimap_visibility != MinimapVisibility::Disabled {
24461                if self.minimap_visibility.settings_visibility()
24462                    != minimap_settings.minimap_enabled()
24463                {
24464                    self.set_minimap_visibility(
24465                        MinimapVisibility::for_mode(self.mode(), cx),
24466                        window,
24467                        cx,
24468                    );
24469                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24470                    minimap_entity.update(cx, |minimap_editor, cx| {
24471                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24472                    })
24473                }
24474            }
24475
24476            if language_settings_changed || accents_changed {
24477                self.colorize_brackets(true, cx);
24478            }
24479
24480            if language_settings_changed {
24481                self.clear_disabled_lsp_folding_ranges(window, cx);
24482                self.refresh_document_symbols(None, cx);
24483            }
24484
24485            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24486                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24487            }) {
24488                if !inlay_splice.is_empty() {
24489                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24490                }
24491                self.refresh_document_colors(None, window, cx);
24492            }
24493
24494            self.refresh_inlay_hints(
24495                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24496                    self.selections.newest_anchor().head(),
24497                    &self.buffer.read(cx).snapshot(cx),
24498                    cx,
24499                )),
24500                cx,
24501            );
24502
24503            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24504                .global_lsp_settings
24505                .semantic_token_rules
24506                .clone();
24507            let semantic_token_rules_changed = self
24508                .semantic_token_state
24509                .update_rules(new_semantic_token_rules);
24510            if language_settings_changed || semantic_token_rules_changed {
24511                self.invalidate_semantic_tokens(None);
24512                self.refresh_semantic_tokens(None, None, cx);
24513            }
24514        }
24515
24516        cx.notify();
24517    }
24518
24519    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24520        if !self.mode.is_full() {
24521            return;
24522        }
24523
24524        let new_accents = self.fetch_accent_data(cx);
24525        if new_accents != self.accent_data {
24526            self.accent_data = new_accents;
24527            self.colorize_brackets(true, cx);
24528        }
24529
24530        self.invalidate_semantic_tokens(None);
24531        self.refresh_semantic_tokens(None, None, cx);
24532    }
24533
24534    pub fn set_searchable(&mut self, searchable: bool) {
24535        self.searchable = searchable;
24536    }
24537
24538    pub fn searchable(&self) -> bool {
24539        self.searchable
24540    }
24541
24542    pub fn open_excerpts_in_split(
24543        &mut self,
24544        _: &OpenExcerptsSplit,
24545        window: &mut Window,
24546        cx: &mut Context<Self>,
24547    ) {
24548        self.open_excerpts_common(None, true, window, cx)
24549    }
24550
24551    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24552        self.open_excerpts_common(None, false, window, cx)
24553    }
24554
24555    pub(crate) fn open_excerpts_common(
24556        &mut self,
24557        jump_data: Option<JumpData>,
24558        split: bool,
24559        window: &mut Window,
24560        cx: &mut Context<Self>,
24561    ) {
24562        if self.buffer.read(cx).is_singleton() {
24563            cx.propagate();
24564            return;
24565        }
24566
24567        let mut new_selections_by_buffer = HashMap::default();
24568        match &jump_data {
24569            Some(JumpData::MultiBufferPoint {
24570                excerpt_id,
24571                position,
24572                anchor,
24573                line_offset_from_top,
24574            }) => {
24575                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24576                if let Some(buffer) = multi_buffer_snapshot
24577                    .buffer_id_for_excerpt(*excerpt_id)
24578                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24579                {
24580                    let buffer_snapshot = buffer.read(cx).snapshot();
24581                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24582                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24583                    } else {
24584                        buffer_snapshot.clip_point(*position, Bias::Left)
24585                    };
24586                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24587                    new_selections_by_buffer.insert(
24588                        buffer,
24589                        (
24590                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24591                            Some(*line_offset_from_top),
24592                        ),
24593                    );
24594                }
24595            }
24596            Some(JumpData::MultiBufferRow {
24597                row,
24598                line_offset_from_top,
24599            }) => {
24600                let point = MultiBufferPoint::new(row.0, 0);
24601                if let Some((buffer, buffer_point, _)) =
24602                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24603                {
24604                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24605                    new_selections_by_buffer
24606                        .entry(buffer)
24607                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24608                        .0
24609                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24610                }
24611            }
24612            None => {
24613                let selections = self
24614                    .selections
24615                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24616                let multi_buffer = self.buffer.read(cx);
24617                for selection in selections {
24618                    for (snapshot, range, _, anchor) in multi_buffer
24619                        .snapshot(cx)
24620                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24621                    {
24622                        if let Some(anchor) = anchor {
24623                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24624                            else {
24625                                continue;
24626                            };
24627                            let offset = text::ToOffset::to_offset(
24628                                &anchor.text_anchor,
24629                                &buffer_handle.read(cx).snapshot(),
24630                            );
24631                            let range = BufferOffset(offset)..BufferOffset(offset);
24632                            new_selections_by_buffer
24633                                .entry(buffer_handle)
24634                                .or_insert((Vec::new(), None))
24635                                .0
24636                                .push(range)
24637                        } else {
24638                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24639                            else {
24640                                continue;
24641                            };
24642                            new_selections_by_buffer
24643                                .entry(buffer_handle)
24644                                .or_insert((Vec::new(), None))
24645                                .0
24646                                .push(range)
24647                        }
24648                    }
24649                }
24650            }
24651        }
24652
24653        if self.delegate_open_excerpts {
24654            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24655                .into_iter()
24656                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24657                .collect();
24658            if !selections_by_buffer.is_empty() {
24659                cx.emit(EditorEvent::OpenExcerptsRequested {
24660                    selections_by_buffer,
24661                    split,
24662                });
24663            }
24664            return;
24665        }
24666
24667        let Some(workspace) = self.workspace() else {
24668            cx.propagate();
24669            return;
24670        };
24671
24672        new_selections_by_buffer
24673            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24674
24675        if new_selections_by_buffer.is_empty() {
24676            return;
24677        }
24678
24679        Self::open_buffers_in_workspace(
24680            workspace.downgrade(),
24681            new_selections_by_buffer,
24682            split,
24683            window,
24684            cx,
24685        );
24686    }
24687
24688    pub(crate) fn open_buffers_in_workspace(
24689        workspace: WeakEntity<Workspace>,
24690        new_selections_by_buffer: HashMap<
24691            Entity<language::Buffer>,
24692            (Vec<Range<BufferOffset>>, Option<u32>),
24693        >,
24694        split: bool,
24695        window: &mut Window,
24696        cx: &mut App,
24697    ) {
24698        // We defer the pane interaction because we ourselves are a workspace item
24699        // and activating a new item causes the pane to call a method on us reentrantly,
24700        // which panics if we're on the stack.
24701        window.defer(cx, move |window, cx| {
24702            workspace
24703                .update(cx, |workspace, cx| {
24704                    let pane = if split {
24705                        workspace.adjacent_pane(window, cx)
24706                    } else {
24707                        workspace.active_pane().clone()
24708                    };
24709
24710                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24711                        let buffer_read = buffer.read(cx);
24712                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24713                            (true, project::File::from_dyn(Some(file)).is_some())
24714                        } else {
24715                            (false, false)
24716                        };
24717
24718                        // If project file is none workspace.open_project_item will fail to open the excerpt
24719                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24720                        // so we check if there's a tab match in that case first
24721                        let editor = (!has_file || !is_project_file)
24722                            .then(|| {
24723                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24724                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24725                                // Instead, we try to activate the existing editor in the pane first.
24726                                let (editor, pane_item_index, pane_item_id) =
24727                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24728                                        let editor = item.downcast::<Editor>()?;
24729                                        let singleton_buffer =
24730                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24731                                        if singleton_buffer == buffer {
24732                                            Some((editor, i, item.item_id()))
24733                                        } else {
24734                                            None
24735                                        }
24736                                    })?;
24737                                pane.update(cx, |pane, cx| {
24738                                    pane.activate_item(pane_item_index, true, true, window, cx);
24739                                    if !PreviewTabsSettings::get_global(cx)
24740                                        .enable_preview_from_multibuffer
24741                                    {
24742                                        pane.unpreview_item_if_preview(pane_item_id);
24743                                    }
24744                                });
24745                                Some(editor)
24746                            })
24747                            .flatten()
24748                            .unwrap_or_else(|| {
24749                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24750                                    .enable_keep_preview_on_code_navigation;
24751                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24752                                    .enable_preview_from_multibuffer;
24753                                workspace.open_project_item::<Self>(
24754                                    pane.clone(),
24755                                    buffer,
24756                                    true,
24757                                    true,
24758                                    keep_old_preview,
24759                                    allow_new_preview,
24760                                    window,
24761                                    cx,
24762                                )
24763                            });
24764
24765                        editor.update(cx, |editor, cx| {
24766                            if has_file && !is_project_file {
24767                                editor.set_read_only(true);
24768                            }
24769                            let autoscroll = match scroll_offset {
24770                                Some(scroll_offset) => {
24771                                    Autoscroll::top_relative(scroll_offset as usize)
24772                                }
24773                                None => Autoscroll::newest(),
24774                            };
24775                            let nav_history = editor.nav_history.take();
24776                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24777                            let Some((excerpt_id, _, buffer_snapshot)) =
24778                                multibuffer_snapshot.as_singleton()
24779                            else {
24780                                return;
24781                            };
24782                            editor.change_selections(
24783                                SelectionEffects::scroll(autoscroll),
24784                                window,
24785                                cx,
24786                                |s| {
24787                                    s.select_ranges(ranges.into_iter().map(|range| {
24788                                        let range = buffer_snapshot.anchor_before(range.start)
24789                                            ..buffer_snapshot.anchor_after(range.end);
24790                                        multibuffer_snapshot
24791                                            .anchor_range_in_excerpt(excerpt_id, range)
24792                                            .unwrap()
24793                                    }));
24794                                },
24795                            );
24796                            editor.nav_history = nav_history;
24797                        });
24798                    }
24799                })
24800                .ok();
24801        });
24802    }
24803
24804    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24805        let snapshot = self.buffer.read(cx).read(cx);
24806        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24807        Some(
24808            ranges
24809                .iter()
24810                .map(move |range| {
24811                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24812                })
24813                .collect(),
24814        )
24815    }
24816
24817    fn selection_replacement_ranges(
24818        &self,
24819        range: Range<MultiBufferOffsetUtf16>,
24820        cx: &mut App,
24821    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24822        let selections = self
24823            .selections
24824            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24825        let newest_selection = selections
24826            .iter()
24827            .max_by_key(|selection| selection.id)
24828            .unwrap();
24829        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24830        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24831        let snapshot = self.buffer.read(cx).read(cx);
24832        selections
24833            .into_iter()
24834            .map(|mut selection| {
24835                selection.start.0.0 =
24836                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24837                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24838                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24839                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24840            })
24841            .collect()
24842    }
24843
24844    fn report_editor_event(
24845        &self,
24846        reported_event: ReportEditorEvent,
24847        file_extension: Option<String>,
24848        cx: &App,
24849    ) {
24850        if cfg!(any(test, feature = "test-support")) {
24851            return;
24852        }
24853
24854        let Some(project) = &self.project else { return };
24855
24856        // If None, we are in a file without an extension
24857        let file = self
24858            .buffer
24859            .read(cx)
24860            .as_singleton()
24861            .and_then(|b| b.read(cx).file());
24862        let file_extension = file_extension.or(file
24863            .as_ref()
24864            .and_then(|file| Path::new(file.file_name(cx)).extension())
24865            .and_then(|e| e.to_str())
24866            .map(|a| a.to_string()));
24867
24868        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24869            .map(|vim_mode| vim_mode.0)
24870            .unwrap_or(false);
24871
24872        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24873        let copilot_enabled = edit_predictions_provider
24874            == language::language_settings::EditPredictionProvider::Copilot;
24875        let copilot_enabled_for_language = self
24876            .buffer
24877            .read(cx)
24878            .language_settings(cx)
24879            .show_edit_predictions;
24880
24881        let project = project.read(cx);
24882        let event_type = reported_event.event_type();
24883
24884        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24885            telemetry::event!(
24886                event_type,
24887                type = if auto_saved {"autosave"} else {"manual"},
24888                file_extension,
24889                vim_mode,
24890                copilot_enabled,
24891                copilot_enabled_for_language,
24892                edit_predictions_provider,
24893                is_via_ssh = project.is_via_remote_server(),
24894            );
24895        } else {
24896            telemetry::event!(
24897                event_type,
24898                file_extension,
24899                vim_mode,
24900                copilot_enabled,
24901                copilot_enabled_for_language,
24902                edit_predictions_provider,
24903                is_via_ssh = project.is_via_remote_server(),
24904            );
24905        };
24906    }
24907
24908    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24909    /// with each line being an array of {text, highlight} objects.
24910    fn copy_highlight_json(
24911        &mut self,
24912        _: &CopyHighlightJson,
24913        window: &mut Window,
24914        cx: &mut Context<Self>,
24915    ) {
24916        #[derive(Serialize)]
24917        struct Chunk<'a> {
24918            text: String,
24919            highlight: Option<&'a str>,
24920        }
24921
24922        let snapshot = self.buffer.read(cx).snapshot(cx);
24923        let range = self
24924            .selected_text_range(false, window, cx)
24925            .and_then(|selection| {
24926                if selection.range.is_empty() {
24927                    None
24928                } else {
24929                    Some(
24930                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24931                            selection.range.start,
24932                        )))
24933                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24934                                selection.range.end,
24935                            ))),
24936                    )
24937                }
24938            })
24939            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24940
24941        let chunks = snapshot.chunks(range, true);
24942        let mut lines = Vec::new();
24943        let mut line: VecDeque<Chunk> = VecDeque::new();
24944
24945        let Some(style) = self.style.as_ref() else {
24946            return;
24947        };
24948
24949        for chunk in chunks {
24950            let highlight = chunk
24951                .syntax_highlight_id
24952                .and_then(|id| id.name(&style.syntax));
24953            let mut chunk_lines = chunk.text.split('\n').peekable();
24954            while let Some(text) = chunk_lines.next() {
24955                let mut merged_with_last_token = false;
24956                if let Some(last_token) = line.back_mut()
24957                    && last_token.highlight == highlight
24958                {
24959                    last_token.text.push_str(text);
24960                    merged_with_last_token = true;
24961                }
24962
24963                if !merged_with_last_token {
24964                    line.push_back(Chunk {
24965                        text: text.into(),
24966                        highlight,
24967                    });
24968                }
24969
24970                if chunk_lines.peek().is_some() {
24971                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24972                        line.pop_front();
24973                    }
24974                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24975                        line.pop_back();
24976                    }
24977
24978                    lines.push(mem::take(&mut line));
24979                }
24980            }
24981        }
24982
24983        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24984            return;
24985        };
24986        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24987    }
24988
24989    pub fn open_context_menu(
24990        &mut self,
24991        _: &OpenContextMenu,
24992        window: &mut Window,
24993        cx: &mut Context<Self>,
24994    ) {
24995        self.request_autoscroll(Autoscroll::newest(), cx);
24996        let position = self
24997            .selections
24998            .newest_display(&self.display_snapshot(cx))
24999            .start;
25000        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
25001    }
25002
25003    pub fn replay_insert_event(
25004        &mut self,
25005        text: &str,
25006        relative_utf16_range: Option<Range<isize>>,
25007        window: &mut Window,
25008        cx: &mut Context<Self>,
25009    ) {
25010        if !self.input_enabled {
25011            cx.emit(EditorEvent::InputIgnored { text: text.into() });
25012            return;
25013        }
25014        if let Some(relative_utf16_range) = relative_utf16_range {
25015            let selections = self
25016                .selections
25017                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
25018            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25019                let new_ranges = selections.into_iter().map(|range| {
25020                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
25021                        range
25022                            .head()
25023                            .0
25024                            .0
25025                            .saturating_add_signed(relative_utf16_range.start),
25026                    ));
25027                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
25028                        range
25029                            .head()
25030                            .0
25031                            .0
25032                            .saturating_add_signed(relative_utf16_range.end),
25033                    ));
25034                    start..end
25035                });
25036                s.select_ranges(new_ranges);
25037            });
25038        }
25039
25040        self.handle_input(text, window, cx);
25041    }
25042
25043    pub fn is_focused(&self, window: &Window) -> bool {
25044        self.focus_handle.is_focused(window)
25045    }
25046
25047    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25048        cx.emit(EditorEvent::Focused);
25049
25050        if let Some(descendant) = self
25051            .last_focused_descendant
25052            .take()
25053            .and_then(|descendant| descendant.upgrade())
25054        {
25055            window.focus(&descendant, cx);
25056        } else {
25057            if let Some(blame) = self.blame.as_ref() {
25058                blame.update(cx, GitBlame::focus)
25059            }
25060
25061            self.blink_manager.update(cx, BlinkManager::enable);
25062            self.show_cursor_names(window, cx);
25063            self.buffer.update(cx, |buffer, cx| {
25064                buffer.finalize_last_transaction(cx);
25065                if self.leader_id.is_none() {
25066                    buffer.set_active_selections(
25067                        &self.selections.disjoint_anchors_arc(),
25068                        self.selections.line_mode(),
25069                        self.cursor_shape,
25070                        cx,
25071                    );
25072                }
25073            });
25074
25075            if let Some(position_map) = self.last_position_map.clone() {
25076                EditorElement::mouse_moved(
25077                    self,
25078                    &MouseMoveEvent {
25079                        position: window.mouse_position(),
25080                        pressed_button: None,
25081                        modifiers: window.modifiers(),
25082                    },
25083                    &position_map,
25084                    None,
25085                    window,
25086                    cx,
25087                );
25088            }
25089        }
25090    }
25091
25092    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25093        cx.emit(EditorEvent::FocusedIn)
25094    }
25095
25096    fn handle_focus_out(
25097        &mut self,
25098        event: FocusOutEvent,
25099        _window: &mut Window,
25100        cx: &mut Context<Self>,
25101    ) {
25102        if event.blurred != self.focus_handle {
25103            self.last_focused_descendant = Some(event.blurred);
25104        }
25105        self.selection_drag_state = SelectionDragState::None;
25106        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25107    }
25108
25109    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25110        self.blink_manager.update(cx, BlinkManager::disable);
25111        self.buffer
25112            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25113
25114        if let Some(blame) = self.blame.as_ref() {
25115            blame.update(cx, GitBlame::blur)
25116        }
25117        if !self.hover_state.focused(window, cx) {
25118            hide_hover(self, cx);
25119        }
25120        if !self
25121            .context_menu
25122            .borrow()
25123            .as_ref()
25124            .is_some_and(|context_menu| context_menu.focused(window, cx))
25125        {
25126            self.hide_context_menu(window, cx);
25127        }
25128        self.take_active_edit_prediction(cx);
25129        cx.emit(EditorEvent::Blurred);
25130        cx.notify();
25131    }
25132
25133    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25134        let mut pending: String = window
25135            .pending_input_keystrokes()
25136            .into_iter()
25137            .flatten()
25138            .filter_map(|keystroke| keystroke.key_char.clone())
25139            .collect();
25140
25141        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25142            pending = "".to_string();
25143        }
25144
25145        let existing_pending = self
25146            .text_highlights(HighlightKey::PendingInput, cx)
25147            .map(|(_, ranges)| ranges.to_vec());
25148        if existing_pending.is_none() && pending.is_empty() {
25149            return;
25150        }
25151        let transaction =
25152            self.transact(window, cx, |this, window, cx| {
25153                let selections = this
25154                    .selections
25155                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25156                let edits = selections
25157                    .iter()
25158                    .map(|selection| (selection.end..selection.end, pending.clone()));
25159                this.edit(edits, cx);
25160                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25161                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25162                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25163                    }));
25164                });
25165                if let Some(existing_ranges) = existing_pending {
25166                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25167                    this.edit(edits, cx);
25168                }
25169            });
25170
25171        let snapshot = self.snapshot(window, cx);
25172        let ranges = self
25173            .selections
25174            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25175            .into_iter()
25176            .map(|selection| {
25177                snapshot.buffer_snapshot().anchor_after(selection.end)
25178                    ..snapshot
25179                        .buffer_snapshot()
25180                        .anchor_before(selection.end + pending.len())
25181            })
25182            .collect();
25183
25184        if pending.is_empty() {
25185            self.clear_highlights(HighlightKey::PendingInput, cx);
25186        } else {
25187            self.highlight_text(
25188                HighlightKey::PendingInput,
25189                ranges,
25190                HighlightStyle {
25191                    underline: Some(UnderlineStyle {
25192                        thickness: px(1.),
25193                        color: None,
25194                        wavy: false,
25195                    }),
25196                    ..Default::default()
25197                },
25198                cx,
25199            );
25200        }
25201
25202        self.ime_transaction = self.ime_transaction.or(transaction);
25203        if let Some(transaction) = self.ime_transaction {
25204            self.buffer.update(cx, |buffer, cx| {
25205                buffer.group_until_transaction(transaction, cx);
25206            });
25207        }
25208
25209        if self
25210            .text_highlights(HighlightKey::PendingInput, cx)
25211            .is_none()
25212        {
25213            self.ime_transaction.take();
25214        }
25215    }
25216
25217    pub fn register_action_renderer(
25218        &mut self,
25219        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25220    ) -> Subscription {
25221        let id = self.next_editor_action_id.post_inc();
25222        self.editor_actions
25223            .borrow_mut()
25224            .insert(id, Box::new(listener));
25225
25226        let editor_actions = self.editor_actions.clone();
25227        Subscription::new(move || {
25228            editor_actions.borrow_mut().remove(&id);
25229        })
25230    }
25231
25232    pub fn register_action<A: Action>(
25233        &mut self,
25234        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25235    ) -> Subscription {
25236        let id = self.next_editor_action_id.post_inc();
25237        let listener = Arc::new(listener);
25238        self.editor_actions.borrow_mut().insert(
25239            id,
25240            Box::new(move |_, window, _| {
25241                let listener = listener.clone();
25242                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25243                    let action = action.downcast_ref().unwrap();
25244                    if phase == DispatchPhase::Bubble {
25245                        listener(action, window, cx)
25246                    }
25247                })
25248            }),
25249        );
25250
25251        let editor_actions = self.editor_actions.clone();
25252        Subscription::new(move || {
25253            editor_actions.borrow_mut().remove(&id);
25254        })
25255    }
25256
25257    pub fn file_header_size(&self) -> u32 {
25258        FILE_HEADER_HEIGHT
25259    }
25260
25261    pub fn restore(
25262        &mut self,
25263        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25264        window: &mut Window,
25265        cx: &mut Context<Self>,
25266    ) {
25267        self.buffer().update(cx, |multi_buffer, cx| {
25268            for (buffer_id, changes) in revert_changes {
25269                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25270                    buffer.update(cx, |buffer, cx| {
25271                        buffer.edit(
25272                            changes
25273                                .into_iter()
25274                                .map(|(range, text)| (range, text.to_string())),
25275                            None,
25276                            cx,
25277                        );
25278                    });
25279                }
25280            }
25281        });
25282        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25283            selections.refresh()
25284        });
25285    }
25286
25287    pub fn to_pixel_point(
25288        &mut self,
25289        source: Anchor,
25290        editor_snapshot: &EditorSnapshot,
25291        window: &mut Window,
25292        cx: &mut App,
25293    ) -> Option<gpui::Point<Pixels>> {
25294        let source_point = source.to_display_point(editor_snapshot);
25295        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25296    }
25297
25298    pub fn display_to_pixel_point(
25299        &mut self,
25300        source: DisplayPoint,
25301        editor_snapshot: &EditorSnapshot,
25302        window: &mut Window,
25303        cx: &mut App,
25304    ) -> Option<gpui::Point<Pixels>> {
25305        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25306        let text_layout_details = self.text_layout_details(window, cx);
25307        let scroll_top = text_layout_details
25308            .scroll_anchor
25309            .scroll_position(editor_snapshot)
25310            .y;
25311
25312        if source.row().as_f64() < scroll_top.floor() {
25313            return None;
25314        }
25315        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25316        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25317        Some(gpui::Point::new(source_x, source_y))
25318    }
25319
25320    pub fn has_visible_completions_menu(&self) -> bool {
25321        !self.edit_prediction_preview_is_active()
25322            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25323                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25324            })
25325    }
25326
25327    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25328        if self.mode.is_minimap() {
25329            return;
25330        }
25331        self.addons
25332            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25333    }
25334
25335    pub fn unregister_addon<T: Addon>(&mut self) {
25336        self.addons.remove(&std::any::TypeId::of::<T>());
25337    }
25338
25339    pub fn addon<T: Addon>(&self) -> Option<&T> {
25340        let type_id = std::any::TypeId::of::<T>();
25341        self.addons
25342            .get(&type_id)
25343            .and_then(|item| item.to_any().downcast_ref::<T>())
25344    }
25345
25346    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25347        let type_id = std::any::TypeId::of::<T>();
25348        self.addons
25349            .get_mut(&type_id)
25350            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25351    }
25352
25353    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25354        let text_layout_details = self.text_layout_details(window, cx);
25355        let style = &text_layout_details.editor_style;
25356        let font_id = window.text_system().resolve_font(&style.text.font());
25357        let font_size = style.text.font_size.to_pixels(window.rem_size());
25358        let line_height = style.text.line_height_in_pixels(window.rem_size());
25359        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25360        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25361
25362        CharacterDimensions {
25363            em_width,
25364            em_advance,
25365            line_height,
25366        }
25367    }
25368
25369    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25370        self.load_diff_task.clone()
25371    }
25372
25373    fn read_metadata_from_db(
25374        &mut self,
25375        item_id: u64,
25376        workspace_id: WorkspaceId,
25377        window: &mut Window,
25378        cx: &mut Context<Editor>,
25379    ) {
25380        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25381            && !self.mode.is_minimap()
25382            && WorkspaceSettings::get(None, cx).restore_on_startup
25383                != RestoreOnStartupBehavior::EmptyTab
25384        {
25385            let buffer_snapshot = OnceCell::new();
25386
25387            // Get file path for path-based fold lookup
25388            let file_path: Option<Arc<Path>> =
25389                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25390                    project::File::from_dyn(buffer.read(cx).file())
25391                        .map(|file| Arc::from(file.abs_path(cx)))
25392                });
25393
25394            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25395            let (folds, needs_migration) = if let Some(ref path) = file_path {
25396                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
25397                    && !folds.is_empty()
25398                {
25399                    (Some(folds), false)
25400                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25401                    && !folds.is_empty()
25402                {
25403                    // Found old editor_folds data, will migrate to file_folds
25404                    (Some(folds), true)
25405                } else {
25406                    (None, false)
25407                }
25408            } else {
25409                // No file path, try editor_folds as fallback
25410                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
25411                (folds.filter(|f| !f.is_empty()), false)
25412            };
25413
25414            if let Some(folds) = folds {
25415                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25416                let snapshot_len = snapshot.len().0;
25417
25418                // Helper: search for fingerprint in buffer, return offset if found
25419                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25420                    // Ensure we start at a character boundary (defensive)
25421                    let search_start = snapshot
25422                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25423                        .0;
25424                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25425
25426                    let mut byte_offset = search_start;
25427                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25428                        if byte_offset > search_end {
25429                            break;
25430                        }
25431                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25432                            return Some(byte_offset);
25433                        }
25434                        byte_offset += ch.len_utf8();
25435                    }
25436                    None
25437                };
25438
25439                // Track search position to handle duplicate fingerprints correctly.
25440                // Folds are stored in document order, so we advance after each match.
25441                let mut search_start = 0usize;
25442
25443                // Collect db_folds for migration (only folds with valid fingerprints)
25444                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25445
25446                let valid_folds: Vec<_> = folds
25447                    .into_iter()
25448                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25449                        // Skip folds without fingerprints (old data before migration)
25450                        let sfp = start_fp?;
25451                        let efp = end_fp?;
25452                        let efp_len = efp.len();
25453
25454                        // Fast path: check if fingerprints match at stored offsets
25455                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25456                        let start_matches = stored_start < snapshot_len
25457                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25458                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25459                        let end_matches = efp_check_pos >= stored_start
25460                            && stored_end <= snapshot_len
25461                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25462
25463                        let (new_start, new_end) = if start_matches && end_matches {
25464                            // Offsets unchanged, use stored values
25465                            (stored_start, stored_end)
25466                        } else if sfp == efp {
25467                            // Short fold: identical fingerprints can only match once per search
25468                            // Use stored fold length to compute new_end
25469                            let new_start = find_fingerprint(&sfp, search_start)?;
25470                            let fold_len = stored_end - stored_start;
25471                            let new_end = new_start + fold_len;
25472                            (new_start, new_end)
25473                        } else {
25474                            // Slow path: search for fingerprints in buffer
25475                            let new_start = find_fingerprint(&sfp, search_start)?;
25476                            // Search for end_fp after start, then add efp_len to get actual fold end
25477                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25478                            let new_end = efp_pos + efp_len;
25479                            (new_start, new_end)
25480                        };
25481
25482                        // Advance search position for next fold
25483                        search_start = new_end;
25484
25485                        // Validate fold makes sense (end must be after start)
25486                        if new_end <= new_start {
25487                            return None;
25488                        }
25489
25490                        // Collect for migration if needed
25491                        if needs_migration {
25492                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25493                        }
25494
25495                        Some(
25496                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25497                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25498                        )
25499                    })
25500                    .collect();
25501
25502                if !valid_folds.is_empty() {
25503                    self.fold_ranges(valid_folds, false, window, cx);
25504
25505                    // Migrate from editor_folds to file_folds if we loaded from old table
25506                    if needs_migration {
25507                        if let Some(ref path) = file_path {
25508                            let path = path.clone();
25509                            cx.spawn(async move |_, _| {
25510                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25511                                    .await
25512                                    .log_err();
25513                            })
25514                            .detach();
25515                        }
25516                    }
25517                }
25518            }
25519
25520            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25521                && !selections.is_empty()
25522            {
25523                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25524                // skip adding the initial selection to selection history
25525                self.selection_history.mode = SelectionHistoryMode::Skipping;
25526                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25527                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25528                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25529                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25530                    }));
25531                });
25532                self.selection_history.mode = SelectionHistoryMode::Normal;
25533            };
25534        }
25535
25536        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25537    }
25538
25539    /// Load folds from the file_folds database table by file path.
25540    /// Used when manually opening a file that was previously closed.
25541    fn load_folds_from_db(
25542        &mut self,
25543        workspace_id: WorkspaceId,
25544        file_path: PathBuf,
25545        window: &mut Window,
25546        cx: &mut Context<Editor>,
25547    ) {
25548        if self.mode.is_minimap()
25549            || WorkspaceSettings::get(None, cx).restore_on_startup
25550                == RestoreOnStartupBehavior::EmptyTab
25551        {
25552            return;
25553        }
25554
25555        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25556            return;
25557        };
25558        if folds.is_empty() {
25559            return;
25560        }
25561
25562        let snapshot = self.buffer.read(cx).snapshot(cx);
25563        let snapshot_len = snapshot.len().0;
25564
25565        // Helper: search for fingerprint in buffer, return offset if found
25566        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25567            let search_start = snapshot
25568                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25569                .0;
25570            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25571
25572            let mut byte_offset = search_start;
25573            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25574                if byte_offset > search_end {
25575                    break;
25576                }
25577                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25578                    return Some(byte_offset);
25579                }
25580                byte_offset += ch.len_utf8();
25581            }
25582            None
25583        };
25584
25585        let mut search_start = 0usize;
25586
25587        let valid_folds: Vec<_> = folds
25588            .into_iter()
25589            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25590                let sfp = start_fp?;
25591                let efp = end_fp?;
25592                let efp_len = efp.len();
25593
25594                let start_matches = stored_start < snapshot_len
25595                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25596                let efp_check_pos = stored_end.saturating_sub(efp_len);
25597                let end_matches = efp_check_pos >= stored_start
25598                    && stored_end <= snapshot_len
25599                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25600
25601                let (new_start, new_end) = if start_matches && end_matches {
25602                    (stored_start, stored_end)
25603                } else if sfp == efp {
25604                    let new_start = find_fingerprint(&sfp, search_start)?;
25605                    let fold_len = stored_end - stored_start;
25606                    let new_end = new_start + fold_len;
25607                    (new_start, new_end)
25608                } else {
25609                    let new_start = find_fingerprint(&sfp, search_start)?;
25610                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25611                    let new_end = efp_pos + efp_len;
25612                    (new_start, new_end)
25613                };
25614
25615                search_start = new_end;
25616
25617                if new_end <= new_start {
25618                    return None;
25619                }
25620
25621                Some(
25622                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25623                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25624                )
25625            })
25626            .collect();
25627
25628        if !valid_folds.is_empty() {
25629            self.fold_ranges(valid_folds, false, window, cx);
25630        }
25631    }
25632
25633    fn lsp_data_enabled(&self) -> bool {
25634        self.enable_lsp_data && self.mode().is_full()
25635    }
25636
25637    fn update_lsp_data(
25638        &mut self,
25639        for_buffer: Option<BufferId>,
25640        window: &mut Window,
25641        cx: &mut Context<'_, Self>,
25642    ) {
25643        if !self.lsp_data_enabled() {
25644            return;
25645        }
25646
25647        if let Some(buffer_id) = for_buffer {
25648            self.pull_diagnostics(buffer_id, window, cx);
25649        }
25650        self.refresh_semantic_tokens(for_buffer, None, cx);
25651        self.refresh_document_colors(for_buffer, window, cx);
25652        self.refresh_folding_ranges(for_buffer, window, cx);
25653        self.refresh_document_symbols(for_buffer, cx);
25654    }
25655
25656    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25657        if !self.lsp_data_enabled() {
25658            return;
25659        }
25660        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25661            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25662        }
25663    }
25664
25665    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25666        if !self.lsp_data_enabled() {
25667            return;
25668        }
25669
25670        if !self.registered_buffers.contains_key(&buffer_id)
25671            && let Some(project) = self.project.as_ref()
25672        {
25673            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25674                project.update(cx, |project, cx| {
25675                    self.registered_buffers.insert(
25676                        buffer_id,
25677                        project.register_buffer_with_language_servers(&buffer, cx),
25678                    );
25679                });
25680            } else {
25681                self.registered_buffers.remove(&buffer_id);
25682            }
25683        }
25684    }
25685
25686    fn create_style(&self, cx: &App) -> EditorStyle {
25687        let settings = ThemeSettings::get_global(cx);
25688
25689        let mut text_style = match self.mode {
25690            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25691                color: cx.theme().colors().editor_foreground,
25692                font_family: settings.ui_font.family.clone(),
25693                font_features: settings.ui_font.features.clone(),
25694                font_fallbacks: settings.ui_font.fallbacks.clone(),
25695                font_size: rems(0.875).into(),
25696                font_weight: settings.ui_font.weight,
25697                line_height: relative(settings.buffer_line_height.value()),
25698                ..Default::default()
25699            },
25700            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25701                color: cx.theme().colors().editor_foreground,
25702                font_family: settings.buffer_font.family.clone(),
25703                font_features: settings.buffer_font.features.clone(),
25704                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25705                font_size: settings.buffer_font_size(cx).into(),
25706                font_weight: settings.buffer_font.weight,
25707                line_height: relative(settings.buffer_line_height.value()),
25708                ..Default::default()
25709            },
25710        };
25711        if let Some(text_style_refinement) = &self.text_style_refinement {
25712            text_style.refine(text_style_refinement)
25713        }
25714
25715        let background = match self.mode {
25716            EditorMode::SingleLine => cx.theme().system().transparent,
25717            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25718            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25719            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25720        };
25721
25722        EditorStyle {
25723            background,
25724            border: cx.theme().colors().border,
25725            local_player: cx.theme().players().local(),
25726            text: text_style,
25727            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25728            syntax: cx.theme().syntax().clone(),
25729            status: cx.theme().status().clone(),
25730            inlay_hints_style: make_inlay_hints_style(cx),
25731            edit_prediction_styles: make_suggestion_styles(cx),
25732            unnecessary_code_fade: settings.unnecessary_code_fade,
25733            show_underlines: self.diagnostics_enabled(),
25734        }
25735    }
25736
25737    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25738        let multibuffer = self.buffer().read(cx);
25739        let is_singleton = multibuffer.is_singleton();
25740        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25741        let buffer = multibuffer.buffer(*buffer_id)?;
25742
25743        let buffer = buffer.read(cx);
25744        let settings = ThemeSettings::get_global(cx);
25745        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25746        let mut breadcrumbs = if is_singleton {
25747            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25748                buffer
25749                    .snapshot()
25750                    .resolve_file_path(
25751                        self.project
25752                            .as_ref()
25753                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25754                            .unwrap_or_default(),
25755                        cx,
25756                    )
25757                    .unwrap_or_else(|| {
25758                        if multibuffer.is_singleton() {
25759                            multibuffer.title(cx).to_string()
25760                        } else {
25761                            "untitled".to_string()
25762                        }
25763                    })
25764            });
25765            vec![BreadcrumbText {
25766                text,
25767                highlights: None,
25768                font: Some(settings.buffer_font.clone()),
25769            }]
25770        } else {
25771            vec![]
25772        };
25773
25774        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25775            text: symbol.text.clone(),
25776            highlights: Some(symbol.highlight_ranges.clone()),
25777            font: Some(settings.buffer_font.clone()),
25778        }));
25779        Some(breadcrumbs)
25780    }
25781
25782    fn disable_lsp_data(&mut self) {
25783        self.enable_lsp_data = false;
25784    }
25785
25786    fn disable_runnables(&mut self) {
25787        self.enable_runnables = false;
25788    }
25789}
25790
25791fn edit_for_markdown_paste<'a>(
25792    buffer: &MultiBufferSnapshot,
25793    range: Range<MultiBufferOffset>,
25794    to_insert: &'a str,
25795    url: Option<url::Url>,
25796) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25797    if url.is_none() {
25798        return (range, Cow::Borrowed(to_insert));
25799    };
25800
25801    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25802
25803    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25804        Cow::Borrowed(to_insert)
25805    } else {
25806        Cow::Owned(format!("[{old_text}]({to_insert})"))
25807    };
25808    (range, new_text)
25809}
25810
25811fn process_completion_for_edit(
25812    completion: &Completion,
25813    intent: CompletionIntent,
25814    buffer: &Entity<Buffer>,
25815    cursor_position: &text::Anchor,
25816    cx: &mut Context<Editor>,
25817) -> CompletionEdit {
25818    let buffer = buffer.read(cx);
25819    let buffer_snapshot = buffer.snapshot();
25820    let (snippet, new_text) = if completion.is_snippet() {
25821        let mut snippet_source = completion.new_text.clone();
25822        // Workaround for typescript language server issues so that methods don't expand within
25823        // strings and functions with type expressions. The previous point is used because the query
25824        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25825        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25826        let previous_point = if previous_point.column > 0 {
25827            cursor_position.to_previous_offset(&buffer_snapshot)
25828        } else {
25829            cursor_position.to_offset(&buffer_snapshot)
25830        };
25831        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25832            && scope.prefers_label_for_snippet_in_completion()
25833            && let Some(label) = completion.label()
25834            && matches!(
25835                completion.kind(),
25836                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25837            )
25838        {
25839            snippet_source = label;
25840        }
25841        match Snippet::parse(&snippet_source).log_err() {
25842            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25843            None => (None, completion.new_text.clone()),
25844        }
25845    } else {
25846        (None, completion.new_text.clone())
25847    };
25848
25849    let mut range_to_replace = {
25850        let replace_range = &completion.replace_range;
25851        if let CompletionSource::Lsp {
25852            insert_range: Some(insert_range),
25853            ..
25854        } = &completion.source
25855        {
25856            debug_assert_eq!(
25857                insert_range.start, replace_range.start,
25858                "insert_range and replace_range should start at the same position"
25859            );
25860            debug_assert!(
25861                insert_range
25862                    .start
25863                    .cmp(cursor_position, &buffer_snapshot)
25864                    .is_le(),
25865                "insert_range should start before or at cursor position"
25866            );
25867            debug_assert!(
25868                replace_range
25869                    .start
25870                    .cmp(cursor_position, &buffer_snapshot)
25871                    .is_le(),
25872                "replace_range should start before or at cursor position"
25873            );
25874
25875            let should_replace = match intent {
25876                CompletionIntent::CompleteWithInsert => false,
25877                CompletionIntent::CompleteWithReplace => true,
25878                CompletionIntent::Complete | CompletionIntent::Compose => {
25879                    let insert_mode =
25880                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25881                            .completions
25882                            .lsp_insert_mode;
25883                    match insert_mode {
25884                        LspInsertMode::Insert => false,
25885                        LspInsertMode::Replace => true,
25886                        LspInsertMode::ReplaceSubsequence => {
25887                            let mut text_to_replace = buffer.chars_for_range(
25888                                buffer.anchor_before(replace_range.start)
25889                                    ..buffer.anchor_after(replace_range.end),
25890                            );
25891                            let mut current_needle = text_to_replace.next();
25892                            for haystack_ch in completion.label.text.chars() {
25893                                if let Some(needle_ch) = current_needle
25894                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25895                                {
25896                                    current_needle = text_to_replace.next();
25897                                }
25898                            }
25899                            current_needle.is_none()
25900                        }
25901                        LspInsertMode::ReplaceSuffix => {
25902                            if replace_range
25903                                .end
25904                                .cmp(cursor_position, &buffer_snapshot)
25905                                .is_gt()
25906                            {
25907                                let range_after_cursor = *cursor_position..replace_range.end;
25908                                let text_after_cursor = buffer
25909                                    .text_for_range(
25910                                        buffer.anchor_before(range_after_cursor.start)
25911                                            ..buffer.anchor_after(range_after_cursor.end),
25912                                    )
25913                                    .collect::<String>()
25914                                    .to_ascii_lowercase();
25915                                completion
25916                                    .label
25917                                    .text
25918                                    .to_ascii_lowercase()
25919                                    .ends_with(&text_after_cursor)
25920                            } else {
25921                                true
25922                            }
25923                        }
25924                    }
25925                }
25926            };
25927
25928            if should_replace {
25929                replace_range.clone()
25930            } else {
25931                insert_range.clone()
25932            }
25933        } else {
25934            replace_range.clone()
25935        }
25936    };
25937
25938    if range_to_replace
25939        .end
25940        .cmp(cursor_position, &buffer_snapshot)
25941        .is_lt()
25942    {
25943        range_to_replace.end = *cursor_position;
25944    }
25945
25946    let replace_range = range_to_replace.to_offset(buffer);
25947    CompletionEdit {
25948        new_text,
25949        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25950        snippet,
25951    }
25952}
25953
25954struct CompletionEdit {
25955    new_text: String,
25956    replace_range: Range<BufferOffset>,
25957    snippet: Option<Snippet>,
25958}
25959
25960fn comment_delimiter_for_newline(
25961    start_point: &Point,
25962    buffer: &MultiBufferSnapshot,
25963    language: &LanguageScope,
25964) -> Option<Arc<str>> {
25965    let delimiters = language.line_comment_prefixes();
25966    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25967    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25968
25969    let num_of_whitespaces = snapshot
25970        .chars_for_range(range.clone())
25971        .take_while(|c| c.is_whitespace())
25972        .count();
25973    let comment_candidate = snapshot
25974        .chars_for_range(range.clone())
25975        .skip(num_of_whitespaces)
25976        .take(max_len_of_delimiter + 2)
25977        .collect::<String>();
25978    let (delimiter, trimmed_len, is_repl) = delimiters
25979        .iter()
25980        .filter_map(|delimiter| {
25981            let prefix = delimiter.trim_end();
25982            if comment_candidate.starts_with(prefix) {
25983                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25984                {
25985                    stripped_comment.starts_with(" %%")
25986                } else {
25987                    false
25988                };
25989                Some((delimiter, prefix.len(), is_repl))
25990            } else {
25991                None
25992            }
25993        })
25994        .max_by_key(|(_, len, _)| *len)?;
25995
25996    if let Some(BlockCommentConfig {
25997        start: block_start, ..
25998    }) = language.block_comment()
25999    {
26000        let block_start_trimmed = block_start.trim_end();
26001        if block_start_trimmed.starts_with(delimiter.trim_end()) {
26002            let line_content = snapshot
26003                .chars_for_range(range.clone())
26004                .skip(num_of_whitespaces)
26005                .take(block_start_trimmed.len())
26006                .collect::<String>();
26007
26008            if line_content.starts_with(block_start_trimmed) {
26009                return None;
26010            }
26011        }
26012    }
26013
26014    let cursor_is_placed_after_comment_marker =
26015        num_of_whitespaces + trimmed_len <= start_point.column as usize;
26016    if cursor_is_placed_after_comment_marker {
26017        if !is_repl {
26018            return Some(delimiter.clone());
26019        }
26020
26021        let line_content_after_cursor: String = snapshot
26022            .chars_for_range(range)
26023            .skip(start_point.column as usize)
26024            .collect();
26025
26026        if line_content_after_cursor.trim().is_empty() {
26027            return None;
26028        } else {
26029            return Some(delimiter.clone());
26030        }
26031    } else {
26032        None
26033    }
26034}
26035
26036fn documentation_delimiter_for_newline(
26037    start_point: &Point,
26038    buffer: &MultiBufferSnapshot,
26039    language: &LanguageScope,
26040    newline_config: &mut NewlineConfig,
26041) -> Option<Arc<str>> {
26042    let BlockCommentConfig {
26043        start: start_tag,
26044        end: end_tag,
26045        prefix: delimiter,
26046        tab_size: len,
26047    } = language.documentation_comment()?;
26048    let is_within_block_comment = buffer
26049        .language_scope_at(*start_point)
26050        .is_some_and(|scope| scope.override_name() == Some("comment"));
26051    if !is_within_block_comment {
26052        return None;
26053    }
26054
26055    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26056
26057    let num_of_whitespaces = snapshot
26058        .chars_for_range(range.clone())
26059        .take_while(|c| c.is_whitespace())
26060        .count();
26061
26062    // 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.
26063    let column = start_point.column;
26064    let cursor_is_after_start_tag = {
26065        let start_tag_len = start_tag.len();
26066        let start_tag_line = snapshot
26067            .chars_for_range(range.clone())
26068            .skip(num_of_whitespaces)
26069            .take(start_tag_len)
26070            .collect::<String>();
26071        if start_tag_line.starts_with(start_tag.as_ref()) {
26072            num_of_whitespaces + start_tag_len <= column as usize
26073        } else {
26074            false
26075        }
26076    };
26077
26078    let cursor_is_after_delimiter = {
26079        let delimiter_trim = delimiter.trim_end();
26080        let delimiter_line = snapshot
26081            .chars_for_range(range.clone())
26082            .skip(num_of_whitespaces)
26083            .take(delimiter_trim.len())
26084            .collect::<String>();
26085        if delimiter_line.starts_with(delimiter_trim) {
26086            num_of_whitespaces + delimiter_trim.len() <= column as usize
26087        } else {
26088            false
26089        }
26090    };
26091
26092    let mut needs_extra_line = false;
26093    let mut extra_line_additional_indent = IndentSize::spaces(0);
26094
26095    let cursor_is_before_end_tag_if_exists = {
26096        let mut char_position = 0u32;
26097        let mut end_tag_offset = None;
26098
26099        'outer: for chunk in snapshot.text_for_range(range) {
26100            if let Some(byte_pos) = chunk.find(&**end_tag) {
26101                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26102                end_tag_offset = Some(char_position + chars_before_match);
26103                break 'outer;
26104            }
26105            char_position += chunk.chars().count() as u32;
26106        }
26107
26108        if let Some(end_tag_offset) = end_tag_offset {
26109            let cursor_is_before_end_tag = column <= end_tag_offset;
26110            if cursor_is_after_start_tag {
26111                if cursor_is_before_end_tag {
26112                    needs_extra_line = true;
26113                }
26114                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26115                if cursor_is_at_start_of_end_tag {
26116                    extra_line_additional_indent.len = *len;
26117                }
26118            }
26119            cursor_is_before_end_tag
26120        } else {
26121            true
26122        }
26123    };
26124
26125    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26126        && cursor_is_before_end_tag_if_exists
26127    {
26128        let additional_indent = if cursor_is_after_start_tag {
26129            IndentSize::spaces(*len)
26130        } else {
26131            IndentSize::spaces(0)
26132        };
26133
26134        *newline_config = NewlineConfig::Newline {
26135            additional_indent,
26136            extra_line_additional_indent: if needs_extra_line {
26137                Some(extra_line_additional_indent)
26138            } else {
26139                None
26140            },
26141            prevent_auto_indent: true,
26142        };
26143        Some(delimiter.clone())
26144    } else {
26145        None
26146    }
26147}
26148
26149const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26150
26151fn list_delimiter_for_newline(
26152    start_point: &Point,
26153    buffer: &MultiBufferSnapshot,
26154    language: &LanguageScope,
26155    newline_config: &mut NewlineConfig,
26156) -> Option<Arc<str>> {
26157    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26158
26159    let num_of_whitespaces = snapshot
26160        .chars_for_range(range.clone())
26161        .take_while(|c| c.is_whitespace())
26162        .count();
26163
26164    let task_list_entries: Vec<_> = language
26165        .task_list()
26166        .into_iter()
26167        .flat_map(|config| {
26168            config
26169                .prefixes
26170                .iter()
26171                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26172        })
26173        .collect();
26174    let unordered_list_entries: Vec<_> = language
26175        .unordered_list()
26176        .iter()
26177        .map(|marker| (marker.as_ref(), marker.as_ref()))
26178        .collect();
26179
26180    let all_entries: Vec<_> = task_list_entries
26181        .into_iter()
26182        .chain(unordered_list_entries)
26183        .collect();
26184
26185    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26186        let candidate: String = snapshot
26187            .chars_for_range(range.clone())
26188            .skip(num_of_whitespaces)
26189            .take(max_prefix_len)
26190            .collect();
26191
26192        if let Some((prefix, continuation)) = all_entries
26193            .iter()
26194            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26195            .max_by_key(|(prefix, _)| prefix.len())
26196        {
26197            let end_of_prefix = num_of_whitespaces + prefix.len();
26198            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26199            let has_content_after_marker = snapshot
26200                .chars_for_range(range)
26201                .skip(end_of_prefix)
26202                .any(|c| !c.is_whitespace());
26203
26204            if has_content_after_marker && cursor_is_after_prefix {
26205                return Some((*continuation).into());
26206            }
26207
26208            if start_point.column as usize == end_of_prefix {
26209                if num_of_whitespaces == 0 {
26210                    *newline_config = NewlineConfig::ClearCurrentLine;
26211                } else {
26212                    *newline_config = NewlineConfig::UnindentCurrentLine {
26213                        continuation: (*continuation).into(),
26214                    };
26215                }
26216            }
26217
26218            return None;
26219        }
26220    }
26221
26222    let candidate: String = snapshot
26223        .chars_for_range(range.clone())
26224        .skip(num_of_whitespaces)
26225        .take(ORDERED_LIST_MAX_MARKER_LEN)
26226        .collect();
26227
26228    for ordered_config in language.ordered_list() {
26229        let regex = match Regex::new(&ordered_config.pattern) {
26230            Ok(r) => r,
26231            Err(_) => continue,
26232        };
26233
26234        if let Some(captures) = regex.captures(&candidate) {
26235            let full_match = captures.get(0)?;
26236            let marker_len = full_match.len();
26237            let end_of_prefix = num_of_whitespaces + marker_len;
26238            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26239
26240            let has_content_after_marker = snapshot
26241                .chars_for_range(range)
26242                .skip(end_of_prefix)
26243                .any(|c| !c.is_whitespace());
26244
26245            if has_content_after_marker && cursor_is_after_prefix {
26246                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26247                let continuation = ordered_config
26248                    .format
26249                    .replace("{1}", &(number + 1).to_string());
26250                return Some(continuation.into());
26251            }
26252
26253            if start_point.column as usize == end_of_prefix {
26254                let continuation = ordered_config.format.replace("{1}", "1");
26255                if num_of_whitespaces == 0 {
26256                    *newline_config = NewlineConfig::ClearCurrentLine;
26257                } else {
26258                    *newline_config = NewlineConfig::UnindentCurrentLine {
26259                        continuation: continuation.into(),
26260                    };
26261                }
26262            }
26263
26264            return None;
26265        }
26266    }
26267
26268    None
26269}
26270
26271fn is_list_prefix_row(
26272    row: MultiBufferRow,
26273    buffer: &MultiBufferSnapshot,
26274    language: &LanguageScope,
26275) -> bool {
26276    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26277        return false;
26278    };
26279
26280    let num_of_whitespaces = snapshot
26281        .chars_for_range(range.clone())
26282        .take_while(|c| c.is_whitespace())
26283        .count();
26284
26285    let task_list_prefixes: Vec<_> = language
26286        .task_list()
26287        .into_iter()
26288        .flat_map(|config| {
26289            config
26290                .prefixes
26291                .iter()
26292                .map(|p| p.as_ref())
26293                .collect::<Vec<_>>()
26294        })
26295        .collect();
26296    let unordered_list_markers: Vec<_> = language
26297        .unordered_list()
26298        .iter()
26299        .map(|marker| marker.as_ref())
26300        .collect();
26301    let all_prefixes: Vec<_> = task_list_prefixes
26302        .into_iter()
26303        .chain(unordered_list_markers)
26304        .collect();
26305    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26306        let candidate: String = snapshot
26307            .chars_for_range(range.clone())
26308            .skip(num_of_whitespaces)
26309            .take(max_prefix_len)
26310            .collect();
26311        if all_prefixes
26312            .iter()
26313            .any(|prefix| candidate.starts_with(*prefix))
26314        {
26315            return true;
26316        }
26317    }
26318
26319    let ordered_list_candidate: String = snapshot
26320        .chars_for_range(range)
26321        .skip(num_of_whitespaces)
26322        .take(ORDERED_LIST_MAX_MARKER_LEN)
26323        .collect();
26324    for ordered_config in language.ordered_list() {
26325        let regex = match Regex::new(&ordered_config.pattern) {
26326            Ok(r) => r,
26327            Err(_) => continue,
26328        };
26329        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26330            return captures.get(0).is_some();
26331        }
26332    }
26333
26334    false
26335}
26336
26337#[derive(Debug)]
26338enum NewlineConfig {
26339    /// Insert newline with optional additional indent and optional extra blank line
26340    Newline {
26341        additional_indent: IndentSize,
26342        extra_line_additional_indent: Option<IndentSize>,
26343        prevent_auto_indent: bool,
26344    },
26345    /// Clear the current line
26346    ClearCurrentLine,
26347    /// Unindent the current line and add continuation
26348    UnindentCurrentLine { continuation: Arc<str> },
26349}
26350
26351impl NewlineConfig {
26352    fn has_extra_line(&self) -> bool {
26353        matches!(
26354            self,
26355            Self::Newline {
26356                extra_line_additional_indent: Some(_),
26357                ..
26358            }
26359        )
26360    }
26361
26362    fn insert_extra_newline_brackets(
26363        buffer: &MultiBufferSnapshot,
26364        range: Range<MultiBufferOffset>,
26365        language: &language::LanguageScope,
26366    ) -> bool {
26367        let leading_whitespace_len = buffer
26368            .reversed_chars_at(range.start)
26369            .take_while(|c| c.is_whitespace() && *c != '\n')
26370            .map(|c| c.len_utf8())
26371            .sum::<usize>();
26372        let trailing_whitespace_len = buffer
26373            .chars_at(range.end)
26374            .take_while(|c| c.is_whitespace() && *c != '\n')
26375            .map(|c| c.len_utf8())
26376            .sum::<usize>();
26377        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26378
26379        language.brackets().any(|(pair, enabled)| {
26380            let pair_start = pair.start.trim_end();
26381            let pair_end = pair.end.trim_start();
26382
26383            enabled
26384                && pair.newline
26385                && buffer.contains_str_at(range.end, pair_end)
26386                && buffer.contains_str_at(
26387                    range.start.saturating_sub_usize(pair_start.len()),
26388                    pair_start,
26389                )
26390        })
26391    }
26392
26393    fn insert_extra_newline_tree_sitter(
26394        buffer: &MultiBufferSnapshot,
26395        range: Range<MultiBufferOffset>,
26396    ) -> bool {
26397        let (buffer, range) = match buffer
26398            .range_to_buffer_ranges(range.start..=range.end)
26399            .as_slice()
26400        {
26401            [(buffer, range, _)] => (*buffer, range.clone()),
26402            _ => return false,
26403        };
26404        let pair = {
26405            let mut result: Option<BracketMatch<usize>> = None;
26406
26407            for pair in buffer
26408                .all_bracket_ranges(range.start.0..range.end.0)
26409                .filter(move |pair| {
26410                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26411                })
26412            {
26413                let len = pair.close_range.end - pair.open_range.start;
26414
26415                if let Some(existing) = &result {
26416                    let existing_len = existing.close_range.end - existing.open_range.start;
26417                    if len > existing_len {
26418                        continue;
26419                    }
26420                }
26421
26422                result = Some(pair);
26423            }
26424
26425            result
26426        };
26427        let Some(pair) = pair else {
26428            return false;
26429        };
26430        pair.newline_only
26431            && buffer
26432                .chars_for_range(pair.open_range.end..range.start.0)
26433                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26434                .all(|c| c.is_whitespace() && c != '\n')
26435    }
26436}
26437
26438fn update_uncommitted_diff_for_buffer(
26439    editor: Entity<Editor>,
26440    project: &Entity<Project>,
26441    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26442    buffer: Entity<MultiBuffer>,
26443    cx: &mut App,
26444) -> Task<()> {
26445    let mut tasks = Vec::new();
26446    project.update(cx, |project, cx| {
26447        for buffer in buffers {
26448            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26449                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26450            }
26451        }
26452    });
26453    cx.spawn(async move |cx| {
26454        let diffs = future::join_all(tasks).await;
26455        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26456            return;
26457        }
26458
26459        buffer.update(cx, |buffer, cx| {
26460            for diff in diffs.into_iter().flatten() {
26461                buffer.add_diff(diff, cx);
26462            }
26463        });
26464    })
26465}
26466
26467fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26468    let tab_size = tab_size.get() as usize;
26469    let mut width = offset;
26470
26471    for ch in text.chars() {
26472        width += if ch == '\t' {
26473            tab_size - (width % tab_size)
26474        } else {
26475            1
26476        };
26477    }
26478
26479    width - offset
26480}
26481
26482#[cfg(test)]
26483mod tests {
26484    use super::*;
26485
26486    #[test]
26487    fn test_string_size_with_expanded_tabs() {
26488        let nz = |val| NonZeroU32::new(val).unwrap();
26489        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26490        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26491        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26492        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26493        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26494        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26495        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26496        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26497    }
26498}
26499
26500/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26501struct WordBreakingTokenizer<'a> {
26502    input: &'a str,
26503}
26504
26505impl<'a> WordBreakingTokenizer<'a> {
26506    fn new(input: &'a str) -> Self {
26507        Self { input }
26508    }
26509}
26510
26511fn is_char_ideographic(ch: char) -> bool {
26512    use unicode_script::Script::*;
26513    use unicode_script::UnicodeScript;
26514    matches!(ch.script(), Han | Tangut | Yi)
26515}
26516
26517fn is_grapheme_ideographic(text: &str) -> bool {
26518    text.chars().any(is_char_ideographic)
26519}
26520
26521fn is_grapheme_whitespace(text: &str) -> bool {
26522    text.chars().any(|x| x.is_whitespace())
26523}
26524
26525fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26526    text.chars()
26527        .next()
26528        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26529}
26530
26531#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26532enum WordBreakToken<'a> {
26533    Word { token: &'a str, grapheme_len: usize },
26534    InlineWhitespace { token: &'a str, grapheme_len: usize },
26535    Newline,
26536}
26537
26538impl<'a> Iterator for WordBreakingTokenizer<'a> {
26539    /// Yields a span, the count of graphemes in the token, and whether it was
26540    /// whitespace. Note that it also breaks at word boundaries.
26541    type Item = WordBreakToken<'a>;
26542
26543    fn next(&mut self) -> Option<Self::Item> {
26544        use unicode_segmentation::UnicodeSegmentation;
26545        if self.input.is_empty() {
26546            return None;
26547        }
26548
26549        let mut iter = self.input.graphemes(true).peekable();
26550        let mut offset = 0;
26551        let mut grapheme_len = 0;
26552        if let Some(first_grapheme) = iter.next() {
26553            let is_newline = first_grapheme == "\n";
26554            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26555            offset += first_grapheme.len();
26556            grapheme_len += 1;
26557            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26558                if let Some(grapheme) = iter.peek().copied()
26559                    && should_stay_with_preceding_ideograph(grapheme)
26560                {
26561                    offset += grapheme.len();
26562                    grapheme_len += 1;
26563                }
26564            } else {
26565                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26566                let mut next_word_bound = words.peek().copied();
26567                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26568                    next_word_bound = words.next();
26569                }
26570                while let Some(grapheme) = iter.peek().copied() {
26571                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26572                        break;
26573                    };
26574                    if is_grapheme_whitespace(grapheme) != is_whitespace
26575                        || (grapheme == "\n") != is_newline
26576                    {
26577                        break;
26578                    };
26579                    offset += grapheme.len();
26580                    grapheme_len += 1;
26581                    iter.next();
26582                }
26583            }
26584            let token = &self.input[..offset];
26585            self.input = &self.input[offset..];
26586            if token == "\n" {
26587                Some(WordBreakToken::Newline)
26588            } else if is_whitespace {
26589                Some(WordBreakToken::InlineWhitespace {
26590                    token,
26591                    grapheme_len,
26592                })
26593            } else {
26594                Some(WordBreakToken::Word {
26595                    token,
26596                    grapheme_len,
26597                })
26598            }
26599        } else {
26600            None
26601        }
26602    }
26603}
26604
26605#[test]
26606fn test_word_breaking_tokenizer() {
26607    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26608        ("", &[]),
26609        ("  ", &[whitespace("  ", 2)]),
26610        ("Ʒ", &[word("Ʒ", 1)]),
26611        ("Ǽ", &[word("Ǽ", 1)]),
26612        ("", &[word("", 1)]),
26613        ("⋑⋑", &[word("⋑⋑", 2)]),
26614        (
26615            "原理,进而",
26616            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26617        ),
26618        (
26619            "hello world",
26620            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26621        ),
26622        (
26623            "hello, world",
26624            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26625        ),
26626        (
26627            "  hello world",
26628            &[
26629                whitespace("  ", 2),
26630                word("hello", 5),
26631                whitespace(" ", 1),
26632                word("world", 5),
26633            ],
26634        ),
26635        (
26636            "这是什么 \n 钢笔",
26637            &[
26638                word("", 1),
26639                word("", 1),
26640                word("", 1),
26641                word("", 1),
26642                whitespace(" ", 1),
26643                newline(),
26644                whitespace(" ", 1),
26645                word("", 1),
26646                word("", 1),
26647            ],
26648        ),
26649        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26650    ];
26651
26652    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26653        WordBreakToken::Word {
26654            token,
26655            grapheme_len,
26656        }
26657    }
26658
26659    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26660        WordBreakToken::InlineWhitespace {
26661            token,
26662            grapheme_len,
26663        }
26664    }
26665
26666    fn newline() -> WordBreakToken<'static> {
26667        WordBreakToken::Newline
26668    }
26669
26670    for (input, result) in tests {
26671        assert_eq!(
26672            WordBreakingTokenizer::new(input)
26673                .collect::<Vec<_>>()
26674                .as_slice(),
26675            *result,
26676        );
26677    }
26678}
26679
26680fn wrap_with_prefix(
26681    first_line_prefix: String,
26682    subsequent_lines_prefix: String,
26683    unwrapped_text: String,
26684    wrap_column: usize,
26685    tab_size: NonZeroU32,
26686    preserve_existing_whitespace: bool,
26687) -> String {
26688    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26689    let subsequent_lines_prefix_len =
26690        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26691    let mut wrapped_text = String::new();
26692    let mut current_line = first_line_prefix;
26693    let mut is_first_line = true;
26694
26695    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26696    let mut current_line_len = first_line_prefix_len;
26697    let mut in_whitespace = false;
26698    for token in tokenizer {
26699        let have_preceding_whitespace = in_whitespace;
26700        match token {
26701            WordBreakToken::Word {
26702                token,
26703                grapheme_len,
26704            } => {
26705                in_whitespace = false;
26706                let current_prefix_len = if is_first_line {
26707                    first_line_prefix_len
26708                } else {
26709                    subsequent_lines_prefix_len
26710                };
26711                if current_line_len + grapheme_len > wrap_column
26712                    && current_line_len != current_prefix_len
26713                {
26714                    wrapped_text.push_str(current_line.trim_end());
26715                    wrapped_text.push('\n');
26716                    is_first_line = false;
26717                    current_line = subsequent_lines_prefix.clone();
26718                    current_line_len = subsequent_lines_prefix_len;
26719                }
26720                current_line.push_str(token);
26721                current_line_len += grapheme_len;
26722            }
26723            WordBreakToken::InlineWhitespace {
26724                mut token,
26725                mut grapheme_len,
26726            } => {
26727                in_whitespace = true;
26728                if have_preceding_whitespace && !preserve_existing_whitespace {
26729                    continue;
26730                }
26731                if !preserve_existing_whitespace {
26732                    // Keep a single whitespace grapheme as-is
26733                    if let Some(first) =
26734                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26735                    {
26736                        token = first;
26737                    } else {
26738                        token = " ";
26739                    }
26740                    grapheme_len = 1;
26741                }
26742                let current_prefix_len = if is_first_line {
26743                    first_line_prefix_len
26744                } else {
26745                    subsequent_lines_prefix_len
26746                };
26747                if current_line_len + grapheme_len > wrap_column {
26748                    wrapped_text.push_str(current_line.trim_end());
26749                    wrapped_text.push('\n');
26750                    is_first_line = false;
26751                    current_line = subsequent_lines_prefix.clone();
26752                    current_line_len = subsequent_lines_prefix_len;
26753                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26754                    current_line.push_str(token);
26755                    current_line_len += grapheme_len;
26756                }
26757            }
26758            WordBreakToken::Newline => {
26759                in_whitespace = true;
26760                let current_prefix_len = if is_first_line {
26761                    first_line_prefix_len
26762                } else {
26763                    subsequent_lines_prefix_len
26764                };
26765                if preserve_existing_whitespace {
26766                    wrapped_text.push_str(current_line.trim_end());
26767                    wrapped_text.push('\n');
26768                    is_first_line = false;
26769                    current_line = subsequent_lines_prefix.clone();
26770                    current_line_len = subsequent_lines_prefix_len;
26771                } else if have_preceding_whitespace {
26772                    continue;
26773                } else if current_line_len + 1 > wrap_column
26774                    && current_line_len != current_prefix_len
26775                {
26776                    wrapped_text.push_str(current_line.trim_end());
26777                    wrapped_text.push('\n');
26778                    is_first_line = false;
26779                    current_line = subsequent_lines_prefix.clone();
26780                    current_line_len = subsequent_lines_prefix_len;
26781                } else if current_line_len != current_prefix_len {
26782                    current_line.push(' ');
26783                    current_line_len += 1;
26784                }
26785            }
26786        }
26787    }
26788
26789    if !current_line.is_empty() {
26790        wrapped_text.push_str(&current_line);
26791    }
26792    wrapped_text
26793}
26794
26795#[test]
26796fn test_wrap_with_prefix() {
26797    assert_eq!(
26798        wrap_with_prefix(
26799            "# ".to_string(),
26800            "# ".to_string(),
26801            "abcdefg".to_string(),
26802            4,
26803            NonZeroU32::new(4).unwrap(),
26804            false,
26805        ),
26806        "# abcdefg"
26807    );
26808    assert_eq!(
26809        wrap_with_prefix(
26810            "".to_string(),
26811            "".to_string(),
26812            "\thello world".to_string(),
26813            8,
26814            NonZeroU32::new(4).unwrap(),
26815            false,
26816        ),
26817        "hello\nworld"
26818    );
26819    assert_eq!(
26820        wrap_with_prefix(
26821            "// ".to_string(),
26822            "// ".to_string(),
26823            "xx \nyy zz aa bb cc".to_string(),
26824            12,
26825            NonZeroU32::new(4).unwrap(),
26826            false,
26827        ),
26828        "// xx yy zz\n// aa bb cc"
26829    );
26830    assert_eq!(
26831        wrap_with_prefix(
26832            String::new(),
26833            String::new(),
26834            "这是什么 \n 钢笔".to_string(),
26835            3,
26836            NonZeroU32::new(4).unwrap(),
26837            false,
26838        ),
26839        "这是什\n么 钢\n"
26840    );
26841    assert_eq!(
26842        wrap_with_prefix(
26843            String::new(),
26844            String::new(),
26845            format!("foo{}bar", '\u{2009}'), // thin space
26846            80,
26847            NonZeroU32::new(4).unwrap(),
26848            false,
26849        ),
26850        format!("foo{}bar", '\u{2009}')
26851    );
26852}
26853
26854pub trait CollaborationHub {
26855    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26856    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26857    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26858}
26859
26860impl CollaborationHub for Entity<Project> {
26861    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26862        self.read(cx).collaborators()
26863    }
26864
26865    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26866        self.read(cx).user_store().read(cx).participant_indices()
26867    }
26868
26869    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26870        let this = self.read(cx);
26871        let user_ids = this.collaborators().values().map(|c| c.user_id);
26872        this.user_store().read(cx).participant_names(user_ids, cx)
26873    }
26874}
26875
26876pub trait SemanticsProvider {
26877    fn hover(
26878        &self,
26879        buffer: &Entity<Buffer>,
26880        position: text::Anchor,
26881        cx: &mut App,
26882    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26883
26884    fn inline_values(
26885        &self,
26886        buffer_handle: Entity<Buffer>,
26887        range: Range<text::Anchor>,
26888        cx: &mut App,
26889    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26890
26891    fn applicable_inlay_chunks(
26892        &self,
26893        buffer: &Entity<Buffer>,
26894        ranges: &[Range<text::Anchor>],
26895        cx: &mut App,
26896    ) -> Vec<Range<BufferRow>>;
26897
26898    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26899
26900    fn inlay_hints(
26901        &self,
26902        invalidate: InvalidationStrategy,
26903        buffer: Entity<Buffer>,
26904        ranges: Vec<Range<text::Anchor>>,
26905        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26906        cx: &mut App,
26907    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26908
26909    fn semantic_tokens(
26910        &self,
26911        buffer: Entity<Buffer>,
26912        refresh: Option<RefreshForServer>,
26913        cx: &mut App,
26914    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
26915
26916    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26917
26918    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26919
26920    fn document_highlights(
26921        &self,
26922        buffer: &Entity<Buffer>,
26923        position: text::Anchor,
26924        cx: &mut App,
26925    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26926
26927    fn definitions(
26928        &self,
26929        buffer: &Entity<Buffer>,
26930        position: text::Anchor,
26931        kind: GotoDefinitionKind,
26932        cx: &mut App,
26933    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26934
26935    fn range_for_rename(
26936        &self,
26937        buffer: &Entity<Buffer>,
26938        position: text::Anchor,
26939        cx: &mut App,
26940    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26941
26942    fn perform_rename(
26943        &self,
26944        buffer: &Entity<Buffer>,
26945        position: text::Anchor,
26946        new_name: String,
26947        cx: &mut App,
26948    ) -> Option<Task<Result<ProjectTransaction>>>;
26949}
26950
26951pub trait CompletionProvider {
26952    fn completions(
26953        &self,
26954        excerpt_id: ExcerptId,
26955        buffer: &Entity<Buffer>,
26956        buffer_position: text::Anchor,
26957        trigger: CompletionContext,
26958        window: &mut Window,
26959        cx: &mut Context<Editor>,
26960    ) -> Task<Result<Vec<CompletionResponse>>>;
26961
26962    fn resolve_completions(
26963        &self,
26964        _buffer: Entity<Buffer>,
26965        _completion_indices: Vec<usize>,
26966        _completions: Rc<RefCell<Box<[Completion]>>>,
26967        _cx: &mut Context<Editor>,
26968    ) -> Task<Result<bool>> {
26969        Task::ready(Ok(false))
26970    }
26971
26972    fn apply_additional_edits_for_completion(
26973        &self,
26974        _buffer: Entity<Buffer>,
26975        _completions: Rc<RefCell<Box<[Completion]>>>,
26976        _completion_index: usize,
26977        _push_to_history: bool,
26978        _cx: &mut Context<Editor>,
26979    ) -> Task<Result<Option<language::Transaction>>> {
26980        Task::ready(Ok(None))
26981    }
26982
26983    fn is_completion_trigger(
26984        &self,
26985        buffer: &Entity<Buffer>,
26986        position: language::Anchor,
26987        text: &str,
26988        trigger_in_words: bool,
26989        cx: &mut Context<Editor>,
26990    ) -> bool;
26991
26992    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26993
26994    fn sort_completions(&self) -> bool {
26995        true
26996    }
26997
26998    fn filter_completions(&self) -> bool {
26999        true
27000    }
27001
27002    fn show_snippets(&self) -> bool {
27003        false
27004    }
27005}
27006
27007pub trait CodeActionProvider {
27008    fn id(&self) -> Arc<str>;
27009
27010    fn code_actions(
27011        &self,
27012        buffer: &Entity<Buffer>,
27013        range: Range<text::Anchor>,
27014        window: &mut Window,
27015        cx: &mut App,
27016    ) -> Task<Result<Vec<CodeAction>>>;
27017
27018    fn apply_code_action(
27019        &self,
27020        buffer_handle: Entity<Buffer>,
27021        action: CodeAction,
27022        excerpt_id: ExcerptId,
27023        push_to_history: bool,
27024        window: &mut Window,
27025        cx: &mut App,
27026    ) -> Task<Result<ProjectTransaction>>;
27027}
27028
27029impl CodeActionProvider for Entity<Project> {
27030    fn id(&self) -> Arc<str> {
27031        "project".into()
27032    }
27033
27034    fn code_actions(
27035        &self,
27036        buffer: &Entity<Buffer>,
27037        range: Range<text::Anchor>,
27038        _window: &mut Window,
27039        cx: &mut App,
27040    ) -> Task<Result<Vec<CodeAction>>> {
27041        self.update(cx, |project, cx| {
27042            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27043            let code_actions = project.code_actions(buffer, range, None, cx);
27044            cx.background_spawn(async move {
27045                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27046                Ok(code_lens_actions
27047                    .context("code lens fetch")?
27048                    .into_iter()
27049                    .flatten()
27050                    .chain(
27051                        code_actions
27052                            .context("code action fetch")?
27053                            .into_iter()
27054                            .flatten(),
27055                    )
27056                    .collect())
27057            })
27058        })
27059    }
27060
27061    fn apply_code_action(
27062        &self,
27063        buffer_handle: Entity<Buffer>,
27064        action: CodeAction,
27065        _excerpt_id: ExcerptId,
27066        push_to_history: bool,
27067        _window: &mut Window,
27068        cx: &mut App,
27069    ) -> Task<Result<ProjectTransaction>> {
27070        self.update(cx, |project, cx| {
27071            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27072        })
27073    }
27074}
27075
27076fn snippet_completions(
27077    project: &Project,
27078    buffer: &Entity<Buffer>,
27079    buffer_anchor: text::Anchor,
27080    classifier: CharClassifier,
27081    cx: &mut App,
27082) -> Task<Result<CompletionResponse>> {
27083    let languages = buffer.read(cx).languages_at(buffer_anchor);
27084    let snippet_store = project.snippets().read(cx);
27085
27086    let scopes: Vec<_> = languages
27087        .iter()
27088        .filter_map(|language| {
27089            let language_name = language.lsp_id();
27090            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27091
27092            if snippets.is_empty() {
27093                None
27094            } else {
27095                Some((language.default_scope(), snippets))
27096            }
27097        })
27098        .collect();
27099
27100    if scopes.is_empty() {
27101        return Task::ready(Ok(CompletionResponse {
27102            completions: vec![],
27103            display_options: CompletionDisplayOptions::default(),
27104            is_incomplete: false,
27105        }));
27106    }
27107
27108    let snapshot = buffer.read(cx).text_snapshot();
27109    let executor = cx.background_executor().clone();
27110
27111    cx.background_spawn(async move {
27112        let is_word_char = |c| classifier.is_word(c);
27113
27114        let mut is_incomplete = false;
27115        let mut completions: Vec<Completion> = Vec::new();
27116
27117        const MAX_PREFIX_LEN: usize = 128;
27118        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27119        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27120        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27121
27122        let max_buffer_window: String = snapshot
27123            .text_for_range(window_start..buffer_offset)
27124            .collect();
27125
27126        if max_buffer_window.is_empty() {
27127            return Ok(CompletionResponse {
27128                completions: vec![],
27129                display_options: CompletionDisplayOptions::default(),
27130                is_incomplete: true,
27131            });
27132        }
27133
27134        for (_scope, snippets) in scopes.into_iter() {
27135            // Sort snippets by word count to match longer snippet prefixes first.
27136            let mut sorted_snippet_candidates = snippets
27137                .iter()
27138                .enumerate()
27139                .flat_map(|(snippet_ix, snippet)| {
27140                    snippet
27141                        .prefix
27142                        .iter()
27143                        .enumerate()
27144                        .map(move |(prefix_ix, prefix)| {
27145                            let word_count =
27146                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27147                            ((snippet_ix, prefix_ix), prefix, word_count)
27148                        })
27149                })
27150                .collect_vec();
27151            sorted_snippet_candidates
27152                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27153
27154            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27155
27156            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27157                .take(
27158                    sorted_snippet_candidates
27159                        .first()
27160                        .map(|(_, _, word_count)| *word_count)
27161                        .unwrap_or_default(),
27162                )
27163                .collect_vec();
27164
27165            const MAX_RESULTS: usize = 100;
27166            // Each match also remembers how many characters from the buffer it consumed
27167            let mut matches: Vec<(StringMatch, usize)> = vec![];
27168
27169            let mut snippet_list_cutoff_index = 0;
27170            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27171                let word_count = buffer_index + 1;
27172                // Increase `snippet_list_cutoff_index` until we have all of the
27173                // snippets with sufficiently many words.
27174                while sorted_snippet_candidates
27175                    .get(snippet_list_cutoff_index)
27176                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27177                        *snippet_word_count >= word_count
27178                    })
27179                {
27180                    snippet_list_cutoff_index += 1;
27181                }
27182
27183                // Take only the candidates with at least `word_count` many words
27184                let snippet_candidates_at_word_len =
27185                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27186
27187                let candidates = snippet_candidates_at_word_len
27188                    .iter()
27189                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27190                    .enumerate() // index in `sorted_snippet_candidates`
27191                    // First char must match
27192                    .filter(|(_ix, prefix)| {
27193                        itertools::equal(
27194                            prefix
27195                                .chars()
27196                                .next()
27197                                .into_iter()
27198                                .flat_map(|c| c.to_lowercase()),
27199                            buffer_window
27200                                .chars()
27201                                .next()
27202                                .into_iter()
27203                                .flat_map(|c| c.to_lowercase()),
27204                        )
27205                    })
27206                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27207                    .collect::<Vec<StringMatchCandidate>>();
27208
27209                matches.extend(
27210                    fuzzy::match_strings(
27211                        &candidates,
27212                        &buffer_window,
27213                        buffer_window.chars().any(|c| c.is_uppercase()),
27214                        true,
27215                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27216                        &Default::default(),
27217                        executor.clone(),
27218                    )
27219                    .await
27220                    .into_iter()
27221                    .map(|string_match| (string_match, buffer_window.len())),
27222                );
27223
27224                if matches.len() >= MAX_RESULTS {
27225                    break;
27226                }
27227            }
27228
27229            let to_lsp = |point: &text::Anchor| {
27230                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27231                point_to_lsp(end)
27232            };
27233            let lsp_end = to_lsp(&buffer_anchor);
27234
27235            if matches.len() >= MAX_RESULTS {
27236                is_incomplete = true;
27237            }
27238
27239            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27240                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27241                    sorted_snippet_candidates[string_match.candidate_id];
27242                let snippet = &snippets[snippet_index];
27243                let start = buffer_offset - buffer_window_len;
27244                let start = snapshot.anchor_before(start);
27245                let range = start..buffer_anchor;
27246                let lsp_start = to_lsp(&start);
27247                let lsp_range = lsp::Range {
27248                    start: lsp_start,
27249                    end: lsp_end,
27250                };
27251                Completion {
27252                    replace_range: range,
27253                    new_text: snippet.body.clone(),
27254                    source: CompletionSource::Lsp {
27255                        insert_range: None,
27256                        server_id: LanguageServerId(usize::MAX),
27257                        resolved: true,
27258                        lsp_completion: Box::new(lsp::CompletionItem {
27259                            label: snippet.prefix.first().unwrap().clone(),
27260                            kind: Some(CompletionItemKind::SNIPPET),
27261                            label_details: snippet.description.as_ref().map(|description| {
27262                                lsp::CompletionItemLabelDetails {
27263                                    detail: Some(description.clone()),
27264                                    description: None,
27265                                }
27266                            }),
27267                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27268                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27269                                lsp::InsertReplaceEdit {
27270                                    new_text: snippet.body.clone(),
27271                                    insert: lsp_range,
27272                                    replace: lsp_range,
27273                                },
27274                            )),
27275                            filter_text: Some(snippet.body.clone()),
27276                            sort_text: Some(char::MAX.to_string()),
27277                            ..lsp::CompletionItem::default()
27278                        }),
27279                        lsp_defaults: None,
27280                    },
27281                    label: CodeLabel {
27282                        text: matching_prefix.clone(),
27283                        runs: Vec::new(),
27284                        filter_range: 0..matching_prefix.len(),
27285                    },
27286                    icon_path: None,
27287                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27288                        single_line: snippet.name.clone().into(),
27289                        plain_text: snippet
27290                            .description
27291                            .clone()
27292                            .map(|description| description.into()),
27293                    }),
27294                    insert_text_mode: None,
27295                    confirm: None,
27296                    match_start: Some(start),
27297                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27298                }
27299            }));
27300        }
27301
27302        Ok(CompletionResponse {
27303            completions,
27304            display_options: CompletionDisplayOptions::default(),
27305            is_incomplete,
27306        })
27307    })
27308}
27309
27310impl CompletionProvider for Entity<Project> {
27311    fn completions(
27312        &self,
27313        _excerpt_id: ExcerptId,
27314        buffer: &Entity<Buffer>,
27315        buffer_position: text::Anchor,
27316        options: CompletionContext,
27317        _window: &mut Window,
27318        cx: &mut Context<Editor>,
27319    ) -> Task<Result<Vec<CompletionResponse>>> {
27320        self.update(cx, |project, cx| {
27321            let task = project.completions(buffer, buffer_position, options, cx);
27322            cx.background_spawn(task)
27323        })
27324    }
27325
27326    fn resolve_completions(
27327        &self,
27328        buffer: Entity<Buffer>,
27329        completion_indices: Vec<usize>,
27330        completions: Rc<RefCell<Box<[Completion]>>>,
27331        cx: &mut Context<Editor>,
27332    ) -> Task<Result<bool>> {
27333        self.update(cx, |project, cx| {
27334            project.lsp_store().update(cx, |lsp_store, cx| {
27335                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27336            })
27337        })
27338    }
27339
27340    fn apply_additional_edits_for_completion(
27341        &self,
27342        buffer: Entity<Buffer>,
27343        completions: Rc<RefCell<Box<[Completion]>>>,
27344        completion_index: usize,
27345        push_to_history: bool,
27346        cx: &mut Context<Editor>,
27347    ) -> Task<Result<Option<language::Transaction>>> {
27348        self.update(cx, |project, cx| {
27349            project.lsp_store().update(cx, |lsp_store, cx| {
27350                lsp_store.apply_additional_edits_for_completion(
27351                    buffer,
27352                    completions,
27353                    completion_index,
27354                    push_to_history,
27355                    cx,
27356                )
27357            })
27358        })
27359    }
27360
27361    fn is_completion_trigger(
27362        &self,
27363        buffer: &Entity<Buffer>,
27364        position: language::Anchor,
27365        text: &str,
27366        trigger_in_words: bool,
27367        cx: &mut Context<Editor>,
27368    ) -> bool {
27369        let mut chars = text.chars();
27370        let char = if let Some(char) = chars.next() {
27371            char
27372        } else {
27373            return false;
27374        };
27375        if chars.next().is_some() {
27376            return false;
27377        }
27378
27379        let buffer = buffer.read(cx);
27380        let snapshot = buffer.snapshot();
27381        let classifier = snapshot
27382            .char_classifier_at(position)
27383            .scope_context(Some(CharScopeContext::Completion));
27384        if trigger_in_words && classifier.is_word(char) {
27385            return true;
27386        }
27387
27388        buffer.completion_triggers().contains(text)
27389    }
27390
27391    fn show_snippets(&self) -> bool {
27392        true
27393    }
27394}
27395
27396impl SemanticsProvider for WeakEntity<Project> {
27397    fn hover(
27398        &self,
27399        buffer: &Entity<Buffer>,
27400        position: text::Anchor,
27401        cx: &mut App,
27402    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27403        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27404            .ok()
27405    }
27406
27407    fn document_highlights(
27408        &self,
27409        buffer: &Entity<Buffer>,
27410        position: text::Anchor,
27411        cx: &mut App,
27412    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27413        self.update(cx, |project, cx| {
27414            project.document_highlights(buffer, position, cx)
27415        })
27416        .ok()
27417    }
27418
27419    fn definitions(
27420        &self,
27421        buffer: &Entity<Buffer>,
27422        position: text::Anchor,
27423        kind: GotoDefinitionKind,
27424        cx: &mut App,
27425    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27426        self.update(cx, |project, cx| match kind {
27427            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27428            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27429            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27430            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27431        })
27432        .ok()
27433    }
27434
27435    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27436        self.update(cx, |project, cx| {
27437            if project
27438                .active_debug_session(cx)
27439                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27440            {
27441                return true;
27442            }
27443
27444            buffer.update(cx, |buffer, cx| {
27445                project.any_language_server_supports_inlay_hints(buffer, cx)
27446            })
27447        })
27448        .unwrap_or(false)
27449    }
27450
27451    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27452        self.update(cx, |project, cx| {
27453            buffer.update(cx, |buffer, cx| {
27454                project.any_language_server_supports_semantic_tokens(buffer, cx)
27455            })
27456        })
27457        .unwrap_or(false)
27458    }
27459
27460    fn inline_values(
27461        &self,
27462        buffer_handle: Entity<Buffer>,
27463        range: Range<text::Anchor>,
27464        cx: &mut App,
27465    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27466        self.update(cx, |project, cx| {
27467            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27468
27469            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27470        })
27471        .ok()
27472        .flatten()
27473    }
27474
27475    fn applicable_inlay_chunks(
27476        &self,
27477        buffer: &Entity<Buffer>,
27478        ranges: &[Range<text::Anchor>],
27479        cx: &mut App,
27480    ) -> Vec<Range<BufferRow>> {
27481        self.update(cx, |project, cx| {
27482            project.lsp_store().update(cx, |lsp_store, cx| {
27483                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27484            })
27485        })
27486        .unwrap_or_default()
27487    }
27488
27489    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27490        self.update(cx, |project, cx| {
27491            project.lsp_store().update(cx, |lsp_store, _| {
27492                lsp_store.invalidate_inlay_hints(for_buffers)
27493            })
27494        })
27495        .ok();
27496    }
27497
27498    fn inlay_hints(
27499        &self,
27500        invalidate: InvalidationStrategy,
27501        buffer: Entity<Buffer>,
27502        ranges: Vec<Range<text::Anchor>>,
27503        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27504        cx: &mut App,
27505    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27506        self.update(cx, |project, cx| {
27507            project.lsp_store().update(cx, |lsp_store, cx| {
27508                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27509            })
27510        })
27511        .ok()
27512    }
27513
27514    fn semantic_tokens(
27515        &self,
27516        buffer: Entity<Buffer>,
27517        refresh: Option<RefreshForServer>,
27518        cx: &mut App,
27519    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27520        self.update(cx, |this, cx| {
27521            this.lsp_store().update(cx, |lsp_store, cx| {
27522                lsp_store.semantic_tokens(buffer, refresh, cx)
27523            })
27524        })
27525        .ok()
27526    }
27527
27528    fn range_for_rename(
27529        &self,
27530        buffer: &Entity<Buffer>,
27531        position: text::Anchor,
27532        cx: &mut App,
27533    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27534        self.update(cx, |project, cx| {
27535            let buffer = buffer.clone();
27536            let task = project.prepare_rename(buffer.clone(), position, cx);
27537            cx.spawn(async move |_, cx| {
27538                Ok(match task.await? {
27539                    PrepareRenameResponse::Success(range) => Some(range),
27540                    PrepareRenameResponse::InvalidPosition => None,
27541                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27542                        // Fallback on using TreeSitter info to determine identifier range
27543                        buffer.read_with(cx, |buffer, _| {
27544                            let snapshot = buffer.snapshot();
27545                            let (range, kind) = snapshot.surrounding_word(position, None);
27546                            if kind != Some(CharKind::Word) {
27547                                return None;
27548                            }
27549                            Some(
27550                                snapshot.anchor_before(range.start)
27551                                    ..snapshot.anchor_after(range.end),
27552                            )
27553                        })
27554                    }
27555                })
27556            })
27557        })
27558        .ok()
27559    }
27560
27561    fn perform_rename(
27562        &self,
27563        buffer: &Entity<Buffer>,
27564        position: text::Anchor,
27565        new_name: String,
27566        cx: &mut App,
27567    ) -> Option<Task<Result<ProjectTransaction>>> {
27568        self.update(cx, |project, cx| {
27569            project.perform_rename(buffer.clone(), position, new_name, cx)
27570        })
27571        .ok()
27572    }
27573}
27574
27575fn consume_contiguous_rows(
27576    contiguous_row_selections: &mut Vec<Selection<Point>>,
27577    selection: &Selection<Point>,
27578    display_map: &DisplaySnapshot,
27579    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27580) -> (MultiBufferRow, MultiBufferRow) {
27581    contiguous_row_selections.push(selection.clone());
27582    let start_row = starting_row(selection, display_map);
27583    let mut end_row = ending_row(selection, display_map);
27584
27585    while let Some(next_selection) = selections.peek() {
27586        if next_selection.start.row <= end_row.0 {
27587            end_row = ending_row(next_selection, display_map);
27588            contiguous_row_selections.push(selections.next().unwrap().clone());
27589        } else {
27590            break;
27591        }
27592    }
27593    (start_row, end_row)
27594}
27595
27596fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27597    if selection.start.column > 0 {
27598        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27599    } else {
27600        MultiBufferRow(selection.start.row)
27601    }
27602}
27603
27604fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27605    if next_selection.end.column > 0 || next_selection.is_empty() {
27606        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27607    } else {
27608        MultiBufferRow(next_selection.end.row)
27609    }
27610}
27611
27612impl EditorSnapshot {
27613    pub fn remote_selections_in_range<'a>(
27614        &'a self,
27615        range: &'a Range<Anchor>,
27616        collaboration_hub: &dyn CollaborationHub,
27617        cx: &'a App,
27618    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27619        let participant_names = collaboration_hub.user_names(cx);
27620        let participant_indices = collaboration_hub.user_participant_indices(cx);
27621        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27622        let collaborators_by_replica_id = collaborators_by_peer_id
27623            .values()
27624            .map(|collaborator| (collaborator.replica_id, collaborator))
27625            .collect::<HashMap<_, _>>();
27626        self.buffer_snapshot()
27627            .selections_in_range(range, false)
27628            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27629                if replica_id == ReplicaId::AGENT {
27630                    Some(RemoteSelection {
27631                        replica_id,
27632                        selection,
27633                        cursor_shape,
27634                        line_mode,
27635                        collaborator_id: CollaboratorId::Agent,
27636                        user_name: Some("Agent".into()),
27637                        color: cx.theme().players().agent(),
27638                    })
27639                } else {
27640                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27641                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27642                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27643                    Some(RemoteSelection {
27644                        replica_id,
27645                        selection,
27646                        cursor_shape,
27647                        line_mode,
27648                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27649                        user_name,
27650                        color: if let Some(index) = participant_index {
27651                            cx.theme().players().color_for_participant(index.0)
27652                        } else {
27653                            cx.theme().players().absent()
27654                        },
27655                    })
27656                }
27657            })
27658    }
27659
27660    pub fn hunks_for_ranges(
27661        &self,
27662        ranges: impl IntoIterator<Item = Range<Point>>,
27663    ) -> Vec<MultiBufferDiffHunk> {
27664        let mut hunks = Vec::new();
27665        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27666            HashMap::default();
27667        for query_range in ranges {
27668            let query_rows =
27669                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27670            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27671                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27672            ) {
27673                // Include deleted hunks that are adjacent to the query range, because
27674                // otherwise they would be missed.
27675                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27676                if hunk.status().is_deleted() {
27677                    intersects_range |= hunk.row_range.start == query_rows.end;
27678                    intersects_range |= hunk.row_range.end == query_rows.start;
27679                }
27680                if intersects_range {
27681                    if !processed_buffer_rows
27682                        .entry(hunk.buffer_id)
27683                        .or_default()
27684                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27685                    {
27686                        continue;
27687                    }
27688                    hunks.push(hunk);
27689                }
27690            }
27691        }
27692
27693        hunks
27694    }
27695
27696    fn display_diff_hunks_for_rows<'a>(
27697        &'a self,
27698        display_rows: Range<DisplayRow>,
27699        folded_buffers: &'a HashSet<BufferId>,
27700    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27701        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27702        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27703
27704        self.buffer_snapshot()
27705            .diff_hunks_in_range(buffer_start..buffer_end)
27706            .filter_map(|hunk| {
27707                if folded_buffers.contains(&hunk.buffer_id)
27708                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27709                {
27710                    return None;
27711                }
27712
27713                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27714                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27715                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27716                    let line_len = self.buffer_snapshot().line_len(last_row);
27717                    Point::new(last_row.0, line_len)
27718                } else {
27719                    Point::new(hunk.row_range.end.0, 0)
27720                };
27721
27722                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27723                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27724
27725                let display_hunk = if hunk_display_start.column() != 0 {
27726                    DisplayDiffHunk::Folded {
27727                        display_row: hunk_display_start.row(),
27728                    }
27729                } else {
27730                    let mut end_row = hunk_display_end.row();
27731                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27732                        end_row.0 += 1;
27733                    }
27734                    let is_created_file = hunk.is_created_file();
27735
27736                    DisplayDiffHunk::Unfolded {
27737                        status: hunk.status(),
27738                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27739                            ..hunk.diff_base_byte_range.end.0,
27740                        word_diffs: hunk.word_diffs,
27741                        display_row_range: hunk_display_start.row()..end_row,
27742                        multi_buffer_range: Anchor::range_in_buffer(
27743                            hunk.excerpt_id,
27744                            hunk.buffer_range,
27745                        ),
27746                        is_created_file,
27747                    }
27748                };
27749
27750                Some(display_hunk)
27751            })
27752    }
27753
27754    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27755        self.display_snapshot
27756            .buffer_snapshot()
27757            .language_at(position)
27758    }
27759
27760    pub fn is_focused(&self) -> bool {
27761        self.is_focused
27762    }
27763
27764    pub fn placeholder_text(&self) -> Option<String> {
27765        self.placeholder_display_snapshot
27766            .as_ref()
27767            .map(|display_map| display_map.text())
27768    }
27769
27770    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27771        self.scroll_anchor.scroll_position(&self.display_snapshot)
27772    }
27773
27774    pub fn gutter_dimensions(
27775        &self,
27776        font_id: FontId,
27777        font_size: Pixels,
27778        style: &EditorStyle,
27779        window: &mut Window,
27780        cx: &App,
27781    ) -> GutterDimensions {
27782        if self.show_gutter
27783            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27784            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27785        {
27786            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27787                matches!(
27788                    ProjectSettings::get_global(cx).git.git_gutter,
27789                    GitGutterSetting::TrackedFiles
27790                )
27791            });
27792            let gutter_settings = EditorSettings::get_global(cx).gutter;
27793            let show_line_numbers = self
27794                .show_line_numbers
27795                .unwrap_or(gutter_settings.line_numbers);
27796            let line_gutter_width = if show_line_numbers {
27797                // Avoid flicker-like gutter resizes when the line number gains another digit by
27798                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27799                let min_width_for_number_on_gutter =
27800                    ch_advance * gutter_settings.min_line_number_digits as f32;
27801                self.max_line_number_width(style, window)
27802                    .max(min_width_for_number_on_gutter)
27803            } else {
27804                0.0.into()
27805            };
27806
27807            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27808            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27809
27810            let git_blame_entries_width =
27811                self.git_blame_gutter_max_author_length
27812                    .map(|max_author_length| {
27813                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27814                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27815
27816                        /// The number of characters to dedicate to gaps and margins.
27817                        const SPACING_WIDTH: usize = 4;
27818
27819                        let max_char_count = max_author_length.min(renderer.max_author_length())
27820                            + ::git::SHORT_SHA_LENGTH
27821                            + MAX_RELATIVE_TIMESTAMP.len()
27822                            + SPACING_WIDTH;
27823
27824                        ch_advance * max_char_count
27825                    });
27826
27827            let is_singleton = self.buffer_snapshot().is_singleton();
27828
27829            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27830            left_padding += if !is_singleton {
27831                ch_width * 4.0
27832            } else if show_runnables || show_breakpoints {
27833                ch_width * 3.0
27834            } else if show_git_gutter && show_line_numbers {
27835                ch_width * 2.0
27836            } else if show_git_gutter || show_line_numbers {
27837                ch_width
27838            } else {
27839                px(0.)
27840            };
27841
27842            let shows_folds = is_singleton && gutter_settings.folds;
27843
27844            let right_padding = if shows_folds && show_line_numbers {
27845                ch_width * 4.0
27846            } else if shows_folds || (!is_singleton && show_line_numbers) {
27847                ch_width * 3.0
27848            } else if show_line_numbers {
27849                ch_width
27850            } else {
27851                px(0.)
27852            };
27853
27854            GutterDimensions {
27855                left_padding,
27856                right_padding,
27857                width: line_gutter_width + left_padding + right_padding,
27858                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27859                git_blame_entries_width,
27860            }
27861        } else if self.offset_content {
27862            GutterDimensions::default_with_margin(font_id, font_size, cx)
27863        } else {
27864            GutterDimensions::default()
27865        }
27866    }
27867
27868    pub fn render_crease_toggle(
27869        &self,
27870        buffer_row: MultiBufferRow,
27871        row_contains_cursor: bool,
27872        editor: Entity<Editor>,
27873        window: &mut Window,
27874        cx: &mut App,
27875    ) -> Option<AnyElement> {
27876        let folded = self.is_line_folded(buffer_row);
27877        let mut is_foldable = false;
27878
27879        if let Some(crease) = self
27880            .crease_snapshot
27881            .query_row(buffer_row, self.buffer_snapshot())
27882        {
27883            is_foldable = true;
27884            match crease {
27885                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27886                    if let Some(render_toggle) = render_toggle {
27887                        let toggle_callback =
27888                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27889                                if folded {
27890                                    editor.update(cx, |editor, cx| {
27891                                        editor.fold_at(buffer_row, window, cx)
27892                                    });
27893                                } else {
27894                                    editor.update(cx, |editor, cx| {
27895                                        editor.unfold_at(buffer_row, window, cx)
27896                                    });
27897                                }
27898                            });
27899                        return Some((render_toggle)(
27900                            buffer_row,
27901                            folded,
27902                            toggle_callback,
27903                            window,
27904                            cx,
27905                        ));
27906                    }
27907                }
27908            }
27909        }
27910
27911        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27912
27913        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27914            Some(
27915                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27916                    .toggle_state(folded)
27917                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27918                        if folded {
27919                            this.unfold_at(buffer_row, window, cx);
27920                        } else {
27921                            this.fold_at(buffer_row, window, cx);
27922                        }
27923                    }))
27924                    .into_any_element(),
27925            )
27926        } else {
27927            None
27928        }
27929    }
27930
27931    pub fn render_crease_trailer(
27932        &self,
27933        buffer_row: MultiBufferRow,
27934        window: &mut Window,
27935        cx: &mut App,
27936    ) -> Option<AnyElement> {
27937        let folded = self.is_line_folded(buffer_row);
27938        if let Crease::Inline { render_trailer, .. } = self
27939            .crease_snapshot
27940            .query_row(buffer_row, self.buffer_snapshot())?
27941        {
27942            let render_trailer = render_trailer.as_ref()?;
27943            Some(render_trailer(buffer_row, folded, window, cx))
27944        } else {
27945            None
27946        }
27947    }
27948
27949    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27950        let digit_count = self.widest_line_number().ilog10() + 1;
27951        column_pixels(style, digit_count as usize, window)
27952    }
27953
27954    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27955    ///
27956    /// This is positive if `base` is before `line`.
27957    fn relative_line_delta(
27958        &self,
27959        current_selection_head: DisplayRow,
27960        first_visible_row: DisplayRow,
27961        consider_wrapped_lines: bool,
27962    ) -> i64 {
27963        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27964        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27965
27966        if consider_wrapped_lines {
27967            let wrap_snapshot = self.wrap_snapshot();
27968            let base_wrap_row = wrap_snapshot
27969                .make_wrap_point(current_selection_head, Bias::Left)
27970                .row();
27971            let wrap_row = wrap_snapshot
27972                .make_wrap_point(first_visible_row, Bias::Left)
27973                .row();
27974
27975            wrap_row.0 as i64 - base_wrap_row.0 as i64
27976        } else {
27977            let fold_snapshot = self.fold_snapshot();
27978            let base_fold_row = fold_snapshot
27979                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27980                .row();
27981            let fold_row = fold_snapshot
27982                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27983                .row();
27984
27985            fold_row as i64 - base_fold_row as i64
27986        }
27987    }
27988
27989    /// Returns the unsigned relative line number to display for each row in `rows`.
27990    ///
27991    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27992    pub fn calculate_relative_line_numbers(
27993        &self,
27994        rows: &Range<DisplayRow>,
27995        current_selection_head: DisplayRow,
27996        count_wrapped_lines: bool,
27997    ) -> HashMap<DisplayRow, u32> {
27998        let initial_offset =
27999            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
28000
28001        self.row_infos(rows.start)
28002            .take(rows.len())
28003            .enumerate()
28004            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
28005            .filter(|(_row, row_info)| {
28006                row_info.buffer_row.is_some()
28007                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
28008            })
28009            .enumerate()
28010            .filter_map(|(i, (row, row_info))| {
28011                // We want to ensure here that the current line has absolute
28012                // numbering, even if we are in a soft-wrapped line. With the
28013                // exception that if we are in a deleted line, we should number this
28014                // relative with 0, as otherwise it would have no line number at all
28015                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
28016
28017                (relative_line_number != 0
28018                    || row_info
28019                        .diff_status
28020                        .is_some_and(|status| status.is_deleted()))
28021                .then_some((row, relative_line_number))
28022            })
28023            .collect()
28024    }
28025}
28026
28027pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28028    let font_size = style.text.font_size.to_pixels(window.rem_size());
28029    let layout = window.text_system().shape_line(
28030        SharedString::from(" ".repeat(column)),
28031        font_size,
28032        &[TextRun {
28033            len: column,
28034            font: style.text.font(),
28035            color: Hsla::default(),
28036            ..Default::default()
28037        }],
28038        None,
28039    );
28040
28041    layout.width
28042}
28043
28044impl Deref for EditorSnapshot {
28045    type Target = DisplaySnapshot;
28046
28047    fn deref(&self) -> &Self::Target {
28048        &self.display_snapshot
28049    }
28050}
28051
28052#[derive(Clone, Debug, PartialEq, Eq)]
28053pub enum EditorEvent {
28054    /// Emitted when the stored review comments change (added, removed, or updated).
28055    ReviewCommentsChanged {
28056        /// The new total count of review comments.
28057        total_count: usize,
28058    },
28059    InputIgnored {
28060        text: Arc<str>,
28061    },
28062    InputHandled {
28063        utf16_range_to_replace: Option<Range<isize>>,
28064        text: Arc<str>,
28065    },
28066    ExcerptsAdded {
28067        buffer: Entity<Buffer>,
28068        predecessor: ExcerptId,
28069        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28070    },
28071    ExcerptsRemoved {
28072        ids: Vec<ExcerptId>,
28073        removed_buffer_ids: Vec<BufferId>,
28074    },
28075    BufferFoldToggled {
28076        ids: Vec<ExcerptId>,
28077        folded: bool,
28078    },
28079    ExcerptsEdited {
28080        ids: Vec<ExcerptId>,
28081    },
28082    ExcerptsExpanded {
28083        ids: Vec<ExcerptId>,
28084    },
28085    ExpandExcerptsRequested {
28086        excerpt_ids: Vec<ExcerptId>,
28087        lines: u32,
28088        direction: ExpandExcerptDirection,
28089    },
28090    StageOrUnstageRequested {
28091        stage: bool,
28092        hunks: Vec<MultiBufferDiffHunk>,
28093    },
28094    OpenExcerptsRequested {
28095        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28096        split: bool,
28097    },
28098    RestoreRequested {
28099        hunks: Vec<MultiBufferDiffHunk>,
28100    },
28101    BufferEdited,
28102    Edited {
28103        transaction_id: clock::Lamport,
28104    },
28105    Reparsed(BufferId),
28106    Focused,
28107    FocusedIn,
28108    Blurred,
28109    DirtyChanged,
28110    Saved,
28111    TitleChanged,
28112    SelectionsChanged {
28113        local: bool,
28114    },
28115    ScrollPositionChanged {
28116        local: bool,
28117        autoscroll: bool,
28118    },
28119    TransactionUndone {
28120        transaction_id: clock::Lamport,
28121    },
28122    TransactionBegun {
28123        transaction_id: clock::Lamport,
28124    },
28125    CursorShapeChanged,
28126    BreadcrumbsChanged,
28127    OutlineSymbolsChanged,
28128    PushedToNavHistory {
28129        anchor: Anchor,
28130        is_deactivate: bool,
28131    },
28132}
28133
28134impl EventEmitter<EditorEvent> for Editor {}
28135
28136impl Focusable for Editor {
28137    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28138        self.focus_handle.clone()
28139    }
28140}
28141
28142impl Render for Editor {
28143    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28144        EditorElement::new(&cx.entity(), self.create_style(cx))
28145    }
28146}
28147
28148impl EntityInputHandler for Editor {
28149    fn text_for_range(
28150        &mut self,
28151        range_utf16: Range<usize>,
28152        adjusted_range: &mut Option<Range<usize>>,
28153        _: &mut Window,
28154        cx: &mut Context<Self>,
28155    ) -> Option<String> {
28156        let snapshot = self.buffer.read(cx).read(cx);
28157        let start = snapshot.clip_offset_utf16(
28158            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28159            Bias::Left,
28160        );
28161        let end = snapshot.clip_offset_utf16(
28162            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28163            Bias::Right,
28164        );
28165        if (start.0.0..end.0.0) != range_utf16 {
28166            adjusted_range.replace(start.0.0..end.0.0);
28167        }
28168        Some(snapshot.text_for_range(start..end).collect())
28169    }
28170
28171    fn selected_text_range(
28172        &mut self,
28173        ignore_disabled_input: bool,
28174        _: &mut Window,
28175        cx: &mut Context<Self>,
28176    ) -> Option<UTF16Selection> {
28177        // Prevent the IME menu from appearing when holding down an alphabetic key
28178        // while input is disabled.
28179        if !ignore_disabled_input && !self.input_enabled {
28180            return None;
28181        }
28182
28183        let selection = self
28184            .selections
28185            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28186        let range = selection.range();
28187
28188        Some(UTF16Selection {
28189            range: range.start.0.0..range.end.0.0,
28190            reversed: selection.reversed,
28191        })
28192    }
28193
28194    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28195        let snapshot = self.buffer.read(cx).read(cx);
28196        let range = self
28197            .text_highlights(HighlightKey::InputComposition, cx)?
28198            .1
28199            .first()?;
28200        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28201    }
28202
28203    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28204        self.clear_highlights(HighlightKey::InputComposition, cx);
28205        self.ime_transaction.take();
28206    }
28207
28208    fn replace_text_in_range(
28209        &mut self,
28210        range_utf16: Option<Range<usize>>,
28211        text: &str,
28212        window: &mut Window,
28213        cx: &mut Context<Self>,
28214    ) {
28215        if !self.input_enabled {
28216            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28217            return;
28218        }
28219
28220        self.transact(window, cx, |this, window, cx| {
28221            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28222                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28223                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28224                Some(this.selection_replacement_ranges(range_utf16, cx))
28225            } else {
28226                this.marked_text_ranges(cx)
28227            };
28228
28229            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28230                let newest_selection_id = this.selections.newest_anchor().id;
28231                this.selections
28232                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28233                    .iter()
28234                    .zip(ranges_to_replace.iter())
28235                    .find_map(|(selection, range)| {
28236                        if selection.id == newest_selection_id {
28237                            Some(
28238                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28239                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28240                            )
28241                        } else {
28242                            None
28243                        }
28244                    })
28245            });
28246
28247            cx.emit(EditorEvent::InputHandled {
28248                utf16_range_to_replace: range_to_replace,
28249                text: text.into(),
28250            });
28251
28252            if let Some(new_selected_ranges) = new_selected_ranges {
28253                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28254                    selections.select_ranges(new_selected_ranges)
28255                });
28256                this.backspace(&Default::default(), window, cx);
28257            }
28258
28259            this.handle_input(text, window, cx);
28260        });
28261
28262        if let Some(transaction) = self.ime_transaction {
28263            self.buffer.update(cx, |buffer, cx| {
28264                buffer.group_until_transaction(transaction, cx);
28265            });
28266        }
28267
28268        self.unmark_text(window, cx);
28269    }
28270
28271    fn replace_and_mark_text_in_range(
28272        &mut self,
28273        range_utf16: Option<Range<usize>>,
28274        text: &str,
28275        new_selected_range_utf16: Option<Range<usize>>,
28276        window: &mut Window,
28277        cx: &mut Context<Self>,
28278    ) {
28279        if !self.input_enabled {
28280            return;
28281        }
28282
28283        let transaction = self.transact(window, cx, |this, window, cx| {
28284            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28285                let snapshot = this.buffer.read(cx).read(cx);
28286                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28287                    for marked_range in &mut marked_ranges {
28288                        marked_range.end = marked_range.start + relative_range_utf16.end;
28289                        marked_range.start += relative_range_utf16.start;
28290                        marked_range.start =
28291                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28292                        marked_range.end =
28293                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28294                    }
28295                }
28296                Some(marked_ranges)
28297            } else if let Some(range_utf16) = range_utf16 {
28298                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28299                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28300                Some(this.selection_replacement_ranges(range_utf16, cx))
28301            } else {
28302                None
28303            };
28304
28305            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28306                let newest_selection_id = this.selections.newest_anchor().id;
28307                this.selections
28308                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28309                    .iter()
28310                    .zip(ranges_to_replace.iter())
28311                    .find_map(|(selection, range)| {
28312                        if selection.id == newest_selection_id {
28313                            Some(
28314                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28315                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28316                            )
28317                        } else {
28318                            None
28319                        }
28320                    })
28321            });
28322
28323            cx.emit(EditorEvent::InputHandled {
28324                utf16_range_to_replace: range_to_replace,
28325                text: text.into(),
28326            });
28327
28328            if let Some(ranges) = ranges_to_replace {
28329                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28330                    s.select_ranges(ranges)
28331                });
28332            }
28333
28334            let marked_ranges = {
28335                let snapshot = this.buffer.read(cx).read(cx);
28336                this.selections
28337                    .disjoint_anchors_arc()
28338                    .iter()
28339                    .map(|selection| {
28340                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28341                    })
28342                    .collect::<Vec<_>>()
28343            };
28344
28345            if text.is_empty() {
28346                this.unmark_text(window, cx);
28347            } else {
28348                this.highlight_text(
28349                    HighlightKey::InputComposition,
28350                    marked_ranges.clone(),
28351                    HighlightStyle {
28352                        underline: Some(UnderlineStyle {
28353                            thickness: px(1.),
28354                            color: None,
28355                            wavy: false,
28356                        }),
28357                        ..Default::default()
28358                    },
28359                    cx,
28360                );
28361            }
28362
28363            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28364            let use_autoclose = this.use_autoclose;
28365            let use_auto_surround = this.use_auto_surround;
28366            this.set_use_autoclose(false);
28367            this.set_use_auto_surround(false);
28368            this.handle_input(text, window, cx);
28369            this.set_use_autoclose(use_autoclose);
28370            this.set_use_auto_surround(use_auto_surround);
28371
28372            if let Some(new_selected_range) = new_selected_range_utf16 {
28373                let snapshot = this.buffer.read(cx).read(cx);
28374                let new_selected_ranges = marked_ranges
28375                    .into_iter()
28376                    .map(|marked_range| {
28377                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28378                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28379                            insertion_start.0 + new_selected_range.start,
28380                        ));
28381                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28382                            insertion_start.0 + new_selected_range.end,
28383                        ));
28384                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28385                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28386                    })
28387                    .collect::<Vec<_>>();
28388
28389                drop(snapshot);
28390                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28391                    selections.select_ranges(new_selected_ranges)
28392                });
28393            }
28394        });
28395
28396        self.ime_transaction = self.ime_transaction.or(transaction);
28397        if let Some(transaction) = self.ime_transaction {
28398            self.buffer.update(cx, |buffer, cx| {
28399                buffer.group_until_transaction(transaction, cx);
28400            });
28401        }
28402
28403        if self
28404            .text_highlights(HighlightKey::InputComposition, cx)
28405            .is_none()
28406        {
28407            self.ime_transaction.take();
28408        }
28409    }
28410
28411    fn bounds_for_range(
28412        &mut self,
28413        range_utf16: Range<usize>,
28414        element_bounds: gpui::Bounds<Pixels>,
28415        window: &mut Window,
28416        cx: &mut Context<Self>,
28417    ) -> Option<gpui::Bounds<Pixels>> {
28418        let text_layout_details = self.text_layout_details(window, cx);
28419        let CharacterDimensions {
28420            em_width,
28421            em_advance,
28422            line_height,
28423        } = self.character_dimensions(window, cx);
28424
28425        let snapshot = self.snapshot(window, cx);
28426        let scroll_position = snapshot.scroll_position();
28427        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28428
28429        let start =
28430            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28431        let x = Pixels::from(
28432            ScrollOffset::from(
28433                snapshot.x_for_display_point(start, &text_layout_details)
28434                    + self.gutter_dimensions.full_width(),
28435            ) - scroll_left,
28436        );
28437        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28438
28439        Some(Bounds {
28440            origin: element_bounds.origin + point(x, y),
28441            size: size(em_width, line_height),
28442        })
28443    }
28444
28445    fn character_index_for_point(
28446        &mut self,
28447        point: gpui::Point<Pixels>,
28448        _window: &mut Window,
28449        _cx: &mut Context<Self>,
28450    ) -> Option<usize> {
28451        let position_map = self.last_position_map.as_ref()?;
28452        if !position_map.text_hitbox.contains(&point) {
28453            return None;
28454        }
28455        let display_point = position_map.point_for_position(point).previous_valid;
28456        let anchor = position_map
28457            .snapshot
28458            .display_point_to_anchor(display_point, Bias::Left);
28459        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28460        Some(utf16_offset.0.0)
28461    }
28462
28463    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28464        self.expects_character_input
28465    }
28466}
28467
28468trait SelectionExt {
28469    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28470    fn spanned_rows(
28471        &self,
28472        include_end_if_at_line_start: bool,
28473        map: &DisplaySnapshot,
28474    ) -> Range<MultiBufferRow>;
28475}
28476
28477impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28478    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28479        let start = self
28480            .start
28481            .to_point(map.buffer_snapshot())
28482            .to_display_point(map);
28483        let end = self
28484            .end
28485            .to_point(map.buffer_snapshot())
28486            .to_display_point(map);
28487        if self.reversed {
28488            end..start
28489        } else {
28490            start..end
28491        }
28492    }
28493
28494    fn spanned_rows(
28495        &self,
28496        include_end_if_at_line_start: bool,
28497        map: &DisplaySnapshot,
28498    ) -> Range<MultiBufferRow> {
28499        let start = self.start.to_point(map.buffer_snapshot());
28500        let mut end = self.end.to_point(map.buffer_snapshot());
28501        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28502            end.row -= 1;
28503        }
28504
28505        let buffer_start = map.prev_line_boundary(start).0;
28506        let buffer_end = map.next_line_boundary(end).0;
28507        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28508    }
28509}
28510
28511impl<T: InvalidationRegion> InvalidationStack<T> {
28512    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28513    where
28514        S: Clone + ToOffset,
28515    {
28516        while let Some(region) = self.last() {
28517            let all_selections_inside_invalidation_ranges =
28518                if selections.len() == region.ranges().len() {
28519                    selections
28520                        .iter()
28521                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28522                        .all(|(selection, invalidation_range)| {
28523                            let head = selection.head().to_offset(buffer);
28524                            invalidation_range.start <= head && invalidation_range.end >= head
28525                        })
28526                } else {
28527                    false
28528                };
28529
28530            if all_selections_inside_invalidation_ranges {
28531                break;
28532            } else {
28533                self.pop();
28534            }
28535        }
28536    }
28537}
28538
28539#[derive(Clone)]
28540struct ErasedEditorImpl(Entity<Editor>);
28541
28542impl ui_input::ErasedEditor for ErasedEditorImpl {
28543    fn text(&self, cx: &App) -> String {
28544        self.0.read(cx).text(cx)
28545    }
28546
28547    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28548        self.0.update(cx, |this, cx| {
28549            this.set_text(text, window, cx);
28550        })
28551    }
28552
28553    fn clear(&self, window: &mut Window, cx: &mut App) {
28554        self.0.update(cx, |this, cx| this.clear(window, cx));
28555    }
28556
28557    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28558        self.0.update(cx, |this, cx| {
28559            this.set_placeholder_text(text, window, cx);
28560        });
28561    }
28562
28563    fn focus_handle(&self, cx: &App) -> FocusHandle {
28564        self.0.read(cx).focus_handle(cx)
28565    }
28566
28567    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28568        let settings = ThemeSettings::get_global(cx);
28569        let theme_color = cx.theme().colors();
28570
28571        let text_style = TextStyle {
28572            font_family: settings.ui_font.family.clone(),
28573            font_features: settings.ui_font.features.clone(),
28574            font_size: rems(0.875).into(),
28575            font_weight: settings.ui_font.weight,
28576            font_style: FontStyle::Normal,
28577            line_height: relative(1.2),
28578            color: theme_color.text,
28579            ..Default::default()
28580        };
28581        let editor_style = EditorStyle {
28582            background: theme_color.ghost_element_background,
28583            local_player: cx.theme().players().local(),
28584            syntax: cx.theme().syntax().clone(),
28585            text: text_style,
28586            ..Default::default()
28587        };
28588        EditorElement::new(&self.0, editor_style).into_any()
28589    }
28590
28591    fn as_any(&self) -> &dyn Any {
28592        &self.0
28593    }
28594
28595    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28596        self.0.update(cx, |editor, cx| {
28597            let editor_offset = editor.buffer().read(cx).len(cx);
28598            editor.change_selections(
28599                SelectionEffects::scroll(Autoscroll::Next),
28600                window,
28601                cx,
28602                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28603            );
28604        });
28605    }
28606
28607    fn subscribe(
28608        &self,
28609        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28610        window: &mut Window,
28611        cx: &mut App,
28612    ) -> Subscription {
28613        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28614            let event = match event {
28615                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28616                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28617                _ => return,
28618            };
28619            (callback)(event, window, cx);
28620        })
28621    }
28622
28623    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28624        self.0.update(cx, |editor, cx| {
28625            editor.set_masked(masked, cx);
28626        });
28627    }
28628}
28629impl<T> Default for InvalidationStack<T> {
28630    fn default() -> Self {
28631        Self(Default::default())
28632    }
28633}
28634
28635impl<T> Deref for InvalidationStack<T> {
28636    type Target = Vec<T>;
28637
28638    fn deref(&self) -> &Self::Target {
28639        &self.0
28640    }
28641}
28642
28643impl<T> DerefMut for InvalidationStack<T> {
28644    fn deref_mut(&mut self) -> &mut Self::Target {
28645        &mut self.0
28646    }
28647}
28648
28649impl InvalidationRegion for SnippetState {
28650    fn ranges(&self) -> &[Range<Anchor>] {
28651        &self.ranges[self.active_index]
28652    }
28653}
28654
28655fn edit_prediction_edit_text(
28656    current_snapshot: &BufferSnapshot,
28657    edits: &[(Range<Anchor>, impl AsRef<str>)],
28658    edit_preview: &EditPreview,
28659    include_deletions: bool,
28660    cx: &App,
28661) -> HighlightedText {
28662    let edits = edits
28663        .iter()
28664        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28665        .collect::<Vec<_>>();
28666
28667    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28668}
28669
28670fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28671    // Fallback for providers that don't provide edit_preview (like Copilot)
28672    // Just show the raw edit text with basic styling
28673    let mut text = String::new();
28674    let mut highlights = Vec::new();
28675
28676    let insertion_highlight_style = HighlightStyle {
28677        color: Some(cx.theme().colors().text),
28678        ..Default::default()
28679    };
28680
28681    for (_, edit_text) in edits {
28682        let start_offset = text.len();
28683        text.push_str(edit_text);
28684        let end_offset = text.len();
28685
28686        if start_offset < end_offset {
28687            highlights.push((start_offset..end_offset, insertion_highlight_style));
28688        }
28689    }
28690
28691    HighlightedText {
28692        text: text.into(),
28693        highlights,
28694    }
28695}
28696
28697pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28698    match severity {
28699        lsp::DiagnosticSeverity::ERROR => colors.error,
28700        lsp::DiagnosticSeverity::WARNING => colors.warning,
28701        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28702        lsp::DiagnosticSeverity::HINT => colors.info,
28703        _ => colors.ignored,
28704    }
28705}
28706
28707pub fn styled_runs_for_code_label<'a>(
28708    label: &'a CodeLabel,
28709    syntax_theme: &'a theme::SyntaxTheme,
28710    local_player: &'a theme::PlayerColor,
28711) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28712    let fade_out = HighlightStyle {
28713        fade_out: Some(0.35),
28714        ..Default::default()
28715    };
28716
28717    let mut prev_end = label.filter_range.end;
28718    label
28719        .runs
28720        .iter()
28721        .enumerate()
28722        .flat_map(move |(ix, (range, highlight_id))| {
28723            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28724                HighlightStyle {
28725                    color: Some(local_player.cursor),
28726                    ..Default::default()
28727                }
28728            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28729                HighlightStyle {
28730                    background_color: Some(local_player.selection),
28731                    ..Default::default()
28732                }
28733            } else if let Some(style) = highlight_id.style(syntax_theme) {
28734                style
28735            } else {
28736                return Default::default();
28737            };
28738            let muted_style = style.highlight(fade_out);
28739
28740            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28741            if range.start >= label.filter_range.end {
28742                if range.start > prev_end {
28743                    runs.push((prev_end..range.start, fade_out));
28744                }
28745                runs.push((range.clone(), muted_style));
28746            } else if range.end <= label.filter_range.end {
28747                runs.push((range.clone(), style));
28748            } else {
28749                runs.push((range.start..label.filter_range.end, style));
28750                runs.push((label.filter_range.end..range.end, muted_style));
28751            }
28752            prev_end = cmp::max(prev_end, range.end);
28753
28754            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28755                runs.push((prev_end..label.text.len(), fade_out));
28756            }
28757
28758            runs
28759        })
28760}
28761
28762pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28763    let mut prev_index = 0;
28764    let mut prev_codepoint: Option<char> = None;
28765    text.char_indices()
28766        .chain([(text.len(), '\0')])
28767        .filter_map(move |(index, codepoint)| {
28768            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28769            let is_boundary = index == text.len()
28770                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28771                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28772            if is_boundary {
28773                let chunk = &text[prev_index..index];
28774                prev_index = index;
28775                Some(chunk)
28776            } else {
28777                None
28778            }
28779        })
28780}
28781
28782/// Given a string of text immediately before the cursor, iterates over possible
28783/// strings a snippet could match to. More precisely: returns an iterator over
28784/// suffixes of `text` created by splitting at word boundaries (before & after
28785/// every non-word character).
28786///
28787/// Shorter suffixes are returned first.
28788pub(crate) fn snippet_candidate_suffixes<'a>(
28789    text: &'a str,
28790    is_word_char: &'a dyn Fn(char) -> bool,
28791) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28792    let mut prev_index = text.len();
28793    let mut prev_codepoint = None;
28794    text.char_indices()
28795        .rev()
28796        .chain([(0, '\0')])
28797        .filter_map(move |(index, codepoint)| {
28798            let prev_index = std::mem::replace(&mut prev_index, index);
28799            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28800            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28801                None
28802            } else {
28803                let chunk = &text[prev_index..]; // go to end of string
28804                Some(chunk)
28805            }
28806        })
28807}
28808
28809pub trait RangeToAnchorExt: Sized {
28810    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28811
28812    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28813        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28814        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28815    }
28816}
28817
28818impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28819    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28820        let start_offset = self.start.to_offset(snapshot);
28821        let end_offset = self.end.to_offset(snapshot);
28822        if start_offset == end_offset {
28823            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28824        } else {
28825            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28826        }
28827    }
28828}
28829
28830pub trait RowExt {
28831    fn as_f64(&self) -> f64;
28832
28833    fn next_row(&self) -> Self;
28834
28835    fn previous_row(&self) -> Self;
28836
28837    fn minus(&self, other: Self) -> u32;
28838}
28839
28840impl RowExt for DisplayRow {
28841    fn as_f64(&self) -> f64 {
28842        self.0 as _
28843    }
28844
28845    fn next_row(&self) -> Self {
28846        Self(self.0 + 1)
28847    }
28848
28849    fn previous_row(&self) -> Self {
28850        Self(self.0.saturating_sub(1))
28851    }
28852
28853    fn minus(&self, other: Self) -> u32 {
28854        self.0 - other.0
28855    }
28856}
28857
28858impl RowExt for MultiBufferRow {
28859    fn as_f64(&self) -> f64 {
28860        self.0 as _
28861    }
28862
28863    fn next_row(&self) -> Self {
28864        Self(self.0 + 1)
28865    }
28866
28867    fn previous_row(&self) -> Self {
28868        Self(self.0.saturating_sub(1))
28869    }
28870
28871    fn minus(&self, other: Self) -> u32 {
28872        self.0 - other.0
28873    }
28874}
28875
28876trait RowRangeExt {
28877    type Row;
28878
28879    fn len(&self) -> usize;
28880
28881    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28882}
28883
28884impl RowRangeExt for Range<MultiBufferRow> {
28885    type Row = MultiBufferRow;
28886
28887    fn len(&self) -> usize {
28888        (self.end.0 - self.start.0) as usize
28889    }
28890
28891    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28892        (self.start.0..self.end.0).map(MultiBufferRow)
28893    }
28894}
28895
28896impl RowRangeExt for Range<DisplayRow> {
28897    type Row = DisplayRow;
28898
28899    fn len(&self) -> usize {
28900        (self.end.0 - self.start.0) as usize
28901    }
28902
28903    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28904        (self.start.0..self.end.0).map(DisplayRow)
28905    }
28906}
28907
28908/// If select range has more than one line, we
28909/// just point the cursor to range.start.
28910fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28911    if range.start.row == range.end.row {
28912        range
28913    } else {
28914        range.start..range.start
28915    }
28916}
28917pub struct KillRing(ClipboardItem);
28918impl Global for KillRing {}
28919
28920const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28921
28922enum BreakpointPromptEditAction {
28923    Log,
28924    Condition,
28925    HitCondition,
28926}
28927
28928struct BreakpointPromptEditor {
28929    pub(crate) prompt: Entity<Editor>,
28930    editor: WeakEntity<Editor>,
28931    breakpoint_anchor: Anchor,
28932    breakpoint: Breakpoint,
28933    edit_action: BreakpointPromptEditAction,
28934    block_ids: HashSet<CustomBlockId>,
28935    editor_margins: Arc<Mutex<EditorMargins>>,
28936    _subscriptions: Vec<Subscription>,
28937}
28938
28939impl BreakpointPromptEditor {
28940    const MAX_LINES: u8 = 4;
28941
28942    fn new(
28943        editor: WeakEntity<Editor>,
28944        breakpoint_anchor: Anchor,
28945        breakpoint: Breakpoint,
28946        edit_action: BreakpointPromptEditAction,
28947        window: &mut Window,
28948        cx: &mut Context<Self>,
28949    ) -> Self {
28950        let base_text = match edit_action {
28951            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28952            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28953            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28954        }
28955        .map(|msg| msg.to_string())
28956        .unwrap_or_default();
28957
28958        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28959        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28960
28961        let prompt = cx.new(|cx| {
28962            let mut prompt = Editor::new(
28963                EditorMode::AutoHeight {
28964                    min_lines: 1,
28965                    max_lines: Some(Self::MAX_LINES as usize),
28966                },
28967                buffer,
28968                None,
28969                window,
28970                cx,
28971            );
28972            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28973            prompt.set_show_cursor_when_unfocused(false, cx);
28974            prompt.set_placeholder_text(
28975                match edit_action {
28976                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28977                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28978                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28979                },
28980                window,
28981                cx,
28982            );
28983
28984            prompt
28985        });
28986
28987        Self {
28988            prompt,
28989            editor,
28990            breakpoint_anchor,
28991            breakpoint,
28992            edit_action,
28993            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28994            block_ids: Default::default(),
28995            _subscriptions: vec![],
28996        }
28997    }
28998
28999    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
29000        self.block_ids.extend(block_ids)
29001    }
29002
29003    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
29004        if let Some(editor) = self.editor.upgrade() {
29005            let message = self
29006                .prompt
29007                .read(cx)
29008                .buffer
29009                .read(cx)
29010                .as_singleton()
29011                .expect("A multi buffer in breakpoint prompt isn't possible")
29012                .read(cx)
29013                .as_rope()
29014                .to_string();
29015
29016            editor.update(cx, |editor, cx| {
29017                editor.edit_breakpoint_at_anchor(
29018                    self.breakpoint_anchor,
29019                    self.breakpoint.clone(),
29020                    match self.edit_action {
29021                        BreakpointPromptEditAction::Log => {
29022                            BreakpointEditAction::EditLogMessage(message.into())
29023                        }
29024                        BreakpointPromptEditAction::Condition => {
29025                            BreakpointEditAction::EditCondition(message.into())
29026                        }
29027                        BreakpointPromptEditAction::HitCondition => {
29028                            BreakpointEditAction::EditHitCondition(message.into())
29029                        }
29030                    },
29031                    cx,
29032                );
29033
29034                editor.remove_blocks(self.block_ids.clone(), None, cx);
29035                cx.focus_self(window);
29036            });
29037        }
29038    }
29039
29040    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29041        self.editor
29042            .update(cx, |editor, cx| {
29043                editor.remove_blocks(self.block_ids.clone(), None, cx);
29044                window.focus(&editor.focus_handle, cx);
29045            })
29046            .log_err();
29047    }
29048
29049    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29050        let settings = ThemeSettings::get_global(cx);
29051        let text_style = TextStyle {
29052            color: if self.prompt.read(cx).read_only(cx) {
29053                cx.theme().colors().text_disabled
29054            } else {
29055                cx.theme().colors().text
29056            },
29057            font_family: settings.buffer_font.family.clone(),
29058            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29059            font_size: settings.buffer_font_size(cx).into(),
29060            font_weight: settings.buffer_font.weight,
29061            line_height: relative(settings.buffer_line_height.value()),
29062            ..Default::default()
29063        };
29064        EditorElement::new(
29065            &self.prompt,
29066            EditorStyle {
29067                background: cx.theme().colors().editor_background,
29068                local_player: cx.theme().players().local(),
29069                text: text_style,
29070                ..Default::default()
29071            },
29072        )
29073    }
29074
29075    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29076        let focus_handle = self.prompt.focus_handle(cx);
29077        IconButton::new("cancel", IconName::Close)
29078            .icon_color(Color::Muted)
29079            .shape(IconButtonShape::Square)
29080            .tooltip(move |_window, cx| {
29081                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29082            })
29083            .on_click(cx.listener(|this, _, window, cx| {
29084                this.cancel(&menu::Cancel, window, cx);
29085            }))
29086    }
29087
29088    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29089        let focus_handle = self.prompt.focus_handle(cx);
29090        IconButton::new("confirm", IconName::Return)
29091            .icon_color(Color::Muted)
29092            .shape(IconButtonShape::Square)
29093            .tooltip(move |_window, cx| {
29094                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29095            })
29096            .on_click(cx.listener(|this, _, window, cx| {
29097                this.confirm(&menu::Confirm, window, cx);
29098            }))
29099    }
29100}
29101
29102impl Render for BreakpointPromptEditor {
29103    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29104        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29105        let editor_margins = *self.editor_margins.lock();
29106        let gutter_dimensions = editor_margins.gutter;
29107        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29108        let right_padding = editor_margins.right + px(9.);
29109        h_flex()
29110            .key_context("Editor")
29111            .bg(cx.theme().colors().editor_background)
29112            .border_y_1()
29113            .border_color(cx.theme().status().info_border)
29114            .size_full()
29115            .py(window.line_height() / 2.5)
29116            .pr(right_padding)
29117            .on_action(cx.listener(Self::confirm))
29118            .on_action(cx.listener(Self::cancel))
29119            .child(
29120                WithRemSize::new(ui_font_size)
29121                    .h_full()
29122                    .w(left_gutter_width)
29123                    .flex()
29124                    .flex_row()
29125                    .flex_shrink_0()
29126                    .items_center()
29127                    .justify_center()
29128                    .gap_1()
29129                    .child(self.render_close_button(cx)),
29130            )
29131            .child(
29132                h_flex()
29133                    .w_full()
29134                    .justify_between()
29135                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29136                    .child(
29137                        WithRemSize::new(ui_font_size)
29138                            .flex()
29139                            .flex_row()
29140                            .items_center()
29141                            .child(self.render_confirm_button(cx)),
29142                    ),
29143            )
29144    }
29145}
29146
29147impl Focusable for BreakpointPromptEditor {
29148    fn focus_handle(&self, cx: &App) -> FocusHandle {
29149        self.prompt.focus_handle(cx)
29150    }
29151}
29152
29153fn all_edits_insertions_or_deletions(
29154    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29155    snapshot: &MultiBufferSnapshot,
29156) -> bool {
29157    let mut all_insertions = true;
29158    let mut all_deletions = true;
29159
29160    for (range, new_text) in edits.iter() {
29161        let range_is_empty = range.to_offset(snapshot).is_empty();
29162        let text_is_empty = new_text.is_empty();
29163
29164        if range_is_empty != text_is_empty {
29165            if range_is_empty {
29166                all_deletions = false;
29167            } else {
29168                all_insertions = false;
29169            }
29170        } else {
29171            return false;
29172        }
29173
29174        if !all_insertions && !all_deletions {
29175            return false;
29176        }
29177    }
29178    all_insertions || all_deletions
29179}
29180
29181struct MissingEditPredictionKeybindingTooltip;
29182
29183impl Render for MissingEditPredictionKeybindingTooltip {
29184    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29185        ui::tooltip_container(cx, |container, cx| {
29186            container
29187                .flex_shrink_0()
29188                .max_w_80()
29189                .min_h(rems_from_px(124.))
29190                .justify_between()
29191                .child(
29192                    v_flex()
29193                        .flex_1()
29194                        .text_ui_sm(cx)
29195                        .child(Label::new("Conflict with Accept Keybinding"))
29196                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29197                )
29198                .child(
29199                    h_flex()
29200                        .pb_1()
29201                        .gap_1()
29202                        .items_end()
29203                        .w_full()
29204                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29205                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29206                        }))
29207                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29208                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29209                        })),
29210                )
29211        })
29212    }
29213}
29214
29215#[derive(Debug, Clone, Copy, PartialEq)]
29216pub struct LineHighlight {
29217    pub background: Background,
29218    pub border: Option<gpui::Hsla>,
29219    pub include_gutter: bool,
29220    pub type_id: Option<TypeId>,
29221}
29222
29223struct LineManipulationResult {
29224    pub new_text: String,
29225    pub line_count_before: usize,
29226    pub line_count_after: usize,
29227}
29228
29229fn render_diff_hunk_controls(
29230    row: u32,
29231    status: &DiffHunkStatus,
29232    hunk_range: Range<Anchor>,
29233    is_created_file: bool,
29234    line_height: Pixels,
29235    editor: &Entity<Editor>,
29236    _window: &mut Window,
29237    cx: &mut App,
29238) -> AnyElement {
29239    h_flex()
29240        .h(line_height)
29241        .mr_1()
29242        .gap_1()
29243        .px_0p5()
29244        .pb_1()
29245        .border_x_1()
29246        .border_b_1()
29247        .border_color(cx.theme().colors().border_variant)
29248        .rounded_b_lg()
29249        .bg(cx.theme().colors().editor_background)
29250        .gap_1()
29251        .block_mouse_except_scroll()
29252        .shadow_md()
29253        .child(if status.has_secondary_hunk() {
29254            Button::new(("stage", row as u64), "Stage")
29255                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29256                .tooltip({
29257                    let focus_handle = editor.focus_handle(cx);
29258                    move |_window, cx| {
29259                        Tooltip::for_action_in(
29260                            "Stage Hunk",
29261                            &::git::ToggleStaged,
29262                            &focus_handle,
29263                            cx,
29264                        )
29265                    }
29266                })
29267                .on_click({
29268                    let editor = editor.clone();
29269                    move |_event, _window, cx| {
29270                        editor.update(cx, |editor, cx| {
29271                            editor.stage_or_unstage_diff_hunks(
29272                                true,
29273                                vec![hunk_range.start..hunk_range.start],
29274                                cx,
29275                            );
29276                        });
29277                    }
29278                })
29279        } else {
29280            Button::new(("unstage", row as u64), "Unstage")
29281                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29282                .tooltip({
29283                    let focus_handle = editor.focus_handle(cx);
29284                    move |_window, cx| {
29285                        Tooltip::for_action_in(
29286                            "Unstage Hunk",
29287                            &::git::ToggleStaged,
29288                            &focus_handle,
29289                            cx,
29290                        )
29291                    }
29292                })
29293                .on_click({
29294                    let editor = editor.clone();
29295                    move |_event, _window, cx| {
29296                        editor.update(cx, |editor, cx| {
29297                            editor.stage_or_unstage_diff_hunks(
29298                                false,
29299                                vec![hunk_range.start..hunk_range.start],
29300                                cx,
29301                            );
29302                        });
29303                    }
29304                })
29305        })
29306        .child(
29307            Button::new(("restore", row as u64), "Restore")
29308                .tooltip({
29309                    let focus_handle = editor.focus_handle(cx);
29310                    move |_window, cx| {
29311                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29312                    }
29313                })
29314                .on_click({
29315                    let editor = editor.clone();
29316                    move |_event, window, cx| {
29317                        editor.update(cx, |editor, cx| {
29318                            let snapshot = editor.snapshot(window, cx);
29319                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29320                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29321                        });
29322                    }
29323                })
29324                .disabled(is_created_file),
29325        )
29326        .when(
29327            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29328            |el| {
29329                el.child(
29330                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29331                        .shape(IconButtonShape::Square)
29332                        .icon_size(IconSize::Small)
29333                        // .disabled(!has_multiple_hunks)
29334                        .tooltip({
29335                            let focus_handle = editor.focus_handle(cx);
29336                            move |_window, cx| {
29337                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29338                            }
29339                        })
29340                        .on_click({
29341                            let editor = editor.clone();
29342                            move |_event, window, cx| {
29343                                editor.update(cx, |editor, cx| {
29344                                    let snapshot = editor.snapshot(window, cx);
29345                                    let position =
29346                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29347                                    editor.go_to_hunk_before_or_after_position(
29348                                        &snapshot,
29349                                        position,
29350                                        Direction::Next,
29351                                        true,
29352                                        window,
29353                                        cx,
29354                                    );
29355                                    editor.expand_selected_diff_hunks(cx);
29356                                });
29357                            }
29358                        }),
29359                )
29360                .child(
29361                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29362                        .shape(IconButtonShape::Square)
29363                        .icon_size(IconSize::Small)
29364                        // .disabled(!has_multiple_hunks)
29365                        .tooltip({
29366                            let focus_handle = editor.focus_handle(cx);
29367                            move |_window, cx| {
29368                                Tooltip::for_action_in(
29369                                    "Previous Hunk",
29370                                    &GoToPreviousHunk,
29371                                    &focus_handle,
29372                                    cx,
29373                                )
29374                            }
29375                        })
29376                        .on_click({
29377                            let editor = editor.clone();
29378                            move |_event, window, cx| {
29379                                editor.update(cx, |editor, cx| {
29380                                    let snapshot = editor.snapshot(window, cx);
29381                                    let point =
29382                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29383                                    editor.go_to_hunk_before_or_after_position(
29384                                        &snapshot,
29385                                        point,
29386                                        Direction::Prev,
29387                                        true,
29388                                        window,
29389                                        cx,
29390                                    );
29391                                    editor.expand_selected_diff_hunks(cx);
29392                                });
29393                            }
29394                        }),
29395                )
29396            },
29397        )
29398        .into_any_element()
29399}
29400
29401pub fn multibuffer_context_lines(cx: &App) -> u32 {
29402    EditorSettings::try_get(cx)
29403        .map(|settings| settings.excerpt_context_lines)
29404        .unwrap_or(2)
29405        .min(32)
29406}