editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod runnables;
   39mod rust_analyzer_ext;
   40pub mod scroll;
   41mod selections_collection;
   42pub mod semantic_tokens;
   43mod split;
   44pub mod split_editor_view;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
  137    WordsQuery,
  138    language_settings::{
  139        self, AllLanguageSettings, LanguageSettings, LspInsertMode, RewrapBehavior,
  140        WordsCompletionMode, all_language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::EditorDb;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::TaskVariables;
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207};
  208use theme_settings::{ThemeSettings, observe_buffer_font_size_adjustment};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224pub use zed_actions::editor::RevealInFileManager;
  225use zed_actions::editor::{MoveDown, MoveUp};
  226
  227use crate::{
  228    code_context_menus::CompletionsMenuSource,
  229    editor_settings::MultiCursorModifier,
  230    hover_links::{find_url, find_url_from_range},
  231    inlays::{
  232        InlineValueCache,
  233        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  234    },
  235    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  236    scroll::{ScrollOffset, ScrollPixelOffset},
  237    selections_collection::resolve_selections_wrapping_blocks,
  238    semantic_tokens::SemanticTokenState,
  239    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  240};
  241
  242pub const FILE_HEADER_HEIGHT: u32 = 2;
  243pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  244pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  245const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  246const MAX_LINE_LEN: usize = 1024;
  247const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  248const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  249pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  250#[doc(hidden)]
  251pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  252pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  253
  254pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  256pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  257pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  258
  259pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  260pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  261
  262pub type RenderDiffHunkControlsFn = Arc<
  263    dyn Fn(
  264        u32,
  265        &DiffHunkStatus,
  266        Range<Anchor>,
  267        bool,
  268        Pixels,
  269        &Entity<Editor>,
  270        &mut Window,
  271        &mut App,
  272    ) -> AnyElement,
  273>;
  274
  275enum ReportEditorEvent {
  276    Saved { auto_saved: bool },
  277    EditorOpened,
  278    Closed,
  279}
  280
  281impl ReportEditorEvent {
  282    pub fn event_type(&self) -> &'static str {
  283        match self {
  284            Self::Saved { .. } => "Editor Saved",
  285            Self::EditorOpened => "Editor Opened",
  286            Self::Closed => "Editor Closed",
  287        }
  288    }
  289}
  290
  291pub enum ActiveDebugLine {}
  292pub enum DebugStackFrameLine {}
  293
  294pub enum ConflictsOuter {}
  295pub enum ConflictsOurs {}
  296pub enum ConflictsTheirs {}
  297pub enum ConflictsOursMarker {}
  298pub enum ConflictsTheirsMarker {}
  299
  300pub struct HunkAddedColor;
  301pub struct HunkRemovedColor;
  302
  303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  304pub enum Navigated {
  305    Yes,
  306    No,
  307}
  308
  309impl Navigated {
  310    pub fn from_bool(yes: bool) -> Navigated {
  311        if yes { Navigated::Yes } else { Navigated::No }
  312    }
  313}
  314
  315#[derive(Debug, Clone, PartialEq, Eq)]
  316enum DisplayDiffHunk {
  317    Folded {
  318        display_row: DisplayRow,
  319    },
  320    Unfolded {
  321        is_created_file: bool,
  322        diff_base_byte_range: Range<usize>,
  323        display_row_range: Range<DisplayRow>,
  324        multi_buffer_range: Range<Anchor>,
  325        status: DiffHunkStatus,
  326        word_diffs: Vec<Range<MultiBufferOffset>>,
  327    },
  328}
  329
  330pub enum HideMouseCursorOrigin {
  331    TypingAction,
  332    MovementAction,
  333}
  334
  335pub fn init(cx: &mut App) {
  336    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  337    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  338
  339    workspace::register_project_item::<Editor>(cx);
  340    workspace::FollowableViewRegistry::register::<Editor>(cx);
  341    workspace::register_serializable_item::<Editor>(cx);
  342
  343    cx.observe_new(
  344        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  345            workspace.register_action(Editor::new_file);
  346            workspace.register_action(Editor::new_file_split);
  347            workspace.register_action(Editor::new_file_vertical);
  348            workspace.register_action(Editor::new_file_horizontal);
  349            workspace.register_action(Editor::cancel_language_server_work);
  350            workspace.register_action(Editor::toggle_focus);
  351        },
  352    )
  353    .detach();
  354
  355    cx.on_action(move |_: &workspace::NewFile, cx| {
  356        let app_state = workspace::AppState::global(cx);
  357        if let Some(app_state) = app_state.upgrade() {
  358            workspace::open_new(
  359                Default::default(),
  360                app_state,
  361                cx,
  362                |workspace, window, cx| {
  363                    Editor::new_file(workspace, &Default::default(), window, cx)
  364                },
  365            )
  366            .detach_and_log_err(cx);
  367        }
  368    })
  369    .on_action(move |_: &workspace::NewWindow, cx| {
  370        let app_state = workspace::AppState::global(cx);
  371        if let Some(app_state) = app_state.upgrade() {
  372            workspace::open_new(
  373                Default::default(),
  374                app_state,
  375                cx,
  376                |workspace, window, cx| {
  377                    cx.activate(true);
  378                    Editor::new_file(workspace, &Default::default(), window, cx)
  379                },
  380            )
  381            .detach_and_log_err(cx);
  382        }
  383    });
  384    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  385        Arc::new(ErasedEditorImpl(
  386            cx.new(|cx| Editor::single_line(window, cx)),
  387        )) as Arc<dyn ErasedEditor>
  388    });
  389    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  390}
  391
  392pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  393    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  394}
  395
  396pub trait DiagnosticRenderer {
  397    fn render_group(
  398        &self,
  399        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  400        buffer_id: BufferId,
  401        snapshot: EditorSnapshot,
  402        editor: WeakEntity<Editor>,
  403        language_registry: Option<Arc<LanguageRegistry>>,
  404        cx: &mut App,
  405    ) -> Vec<BlockProperties<Anchor>>;
  406
  407    fn render_hover(
  408        &self,
  409        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  410        range: Range<Point>,
  411        buffer_id: BufferId,
  412        language_registry: Option<Arc<LanguageRegistry>>,
  413        cx: &mut App,
  414    ) -> Option<Entity<markdown::Markdown>>;
  415
  416    fn open_link(
  417        &self,
  418        editor: &mut Editor,
  419        link: SharedString,
  420        window: &mut Window,
  421        cx: &mut Context<Editor>,
  422    );
  423}
  424
  425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  426
  427impl GlobalDiagnosticRenderer {
  428    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  429        cx.try_global::<Self>().map(|g| g.0.clone())
  430    }
  431}
  432
  433impl gpui::Global for GlobalDiagnosticRenderer {}
  434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  435    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  436}
  437
  438pub struct SearchWithinRange;
  439
  440trait InvalidationRegion {
  441    fn ranges(&self) -> &[Range<Anchor>];
  442}
  443
  444#[derive(Clone, Debug, PartialEq)]
  445pub enum SelectPhase {
  446    Begin {
  447        position: DisplayPoint,
  448        add: bool,
  449        click_count: usize,
  450    },
  451    BeginColumnar {
  452        position: DisplayPoint,
  453        reset: bool,
  454        mode: ColumnarMode,
  455        goal_column: u32,
  456    },
  457    Extend {
  458        position: DisplayPoint,
  459        click_count: usize,
  460    },
  461    Update {
  462        position: DisplayPoint,
  463        goal_column: u32,
  464        scroll_delta: gpui::Point<f32>,
  465    },
  466    End,
  467}
  468
  469#[derive(Clone, Debug, PartialEq)]
  470pub enum ColumnarMode {
  471    FromMouse,
  472    FromSelection,
  473}
  474
  475#[derive(Clone, Debug)]
  476pub enum SelectMode {
  477    Character,
  478    Word(Range<Anchor>),
  479    Line(Range<Anchor>),
  480    All,
  481}
  482
  483#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  484pub enum SizingBehavior {
  485    /// The editor will layout itself using `size_full` and will include the vertical
  486    /// scroll margin as requested by user settings.
  487    #[default]
  488    Default,
  489    /// The editor will layout itself using `size_full`, but will not have any
  490    /// vertical overscroll.
  491    ExcludeOverscrollMargin,
  492    /// The editor will request a vertical size according to its content and will be
  493    /// layouted without a vertical scroll margin.
  494    SizeByContent,
  495}
  496
  497#[derive(Clone, PartialEq, Eq, Debug)]
  498pub enum EditorMode {
  499    SingleLine,
  500    AutoHeight {
  501        min_lines: usize,
  502        max_lines: Option<usize>,
  503    },
  504    Full {
  505        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  506        scale_ui_elements_with_buffer_font_size: bool,
  507        /// When set to `true`, the editor will render a background for the active line.
  508        show_active_line_background: bool,
  509        /// Determines the sizing behavior for this editor
  510        sizing_behavior: SizingBehavior,
  511    },
  512    Minimap {
  513        parent: WeakEntity<Editor>,
  514    },
  515}
  516
  517impl EditorMode {
  518    pub fn full() -> Self {
  519        Self::Full {
  520            scale_ui_elements_with_buffer_font_size: true,
  521            show_active_line_background: true,
  522            sizing_behavior: SizingBehavior::Default,
  523        }
  524    }
  525
  526    #[inline]
  527    pub fn is_full(&self) -> bool {
  528        matches!(self, Self::Full { .. })
  529    }
  530
  531    #[inline]
  532    pub fn is_single_line(&self) -> bool {
  533        matches!(self, Self::SingleLine { .. })
  534    }
  535
  536    #[inline]
  537    fn is_minimap(&self) -> bool {
  538        matches!(self, Self::Minimap { .. })
  539    }
  540}
  541
  542#[derive(Copy, Clone, Debug)]
  543pub enum SoftWrap {
  544    /// Prefer not to wrap at all.
  545    ///
  546    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  547    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  548    GitDiff,
  549    /// Prefer a single line generally, unless an overly long line is encountered.
  550    None,
  551    /// Soft wrap lines that exceed the editor width.
  552    EditorWidth,
  553    /// Soft wrap lines at the preferred line length.
  554    Column(u32),
  555    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  556    Bounded(u32),
  557}
  558
  559#[derive(Clone)]
  560pub struct EditorStyle {
  561    pub background: Hsla,
  562    pub border: Hsla,
  563    pub local_player: PlayerColor,
  564    pub text: TextStyle,
  565    pub scrollbar_width: Pixels,
  566    pub syntax: Arc<SyntaxTheme>,
  567    pub status: StatusColors,
  568    pub inlay_hints_style: HighlightStyle,
  569    pub edit_prediction_styles: EditPredictionStyles,
  570    pub unnecessary_code_fade: f32,
  571    pub show_underlines: bool,
  572}
  573
  574impl Default for EditorStyle {
  575    fn default() -> Self {
  576        Self {
  577            background: Hsla::default(),
  578            border: Hsla::default(),
  579            local_player: PlayerColor::default(),
  580            text: TextStyle::default(),
  581            scrollbar_width: Pixels::default(),
  582            syntax: Default::default(),
  583            // HACK: Status colors don't have a real default.
  584            // We should look into removing the status colors from the editor
  585            // style and retrieve them directly from the theme.
  586            status: StatusColors::dark(),
  587            inlay_hints_style: HighlightStyle::default(),
  588            edit_prediction_styles: EditPredictionStyles {
  589                insertion: HighlightStyle::default(),
  590                whitespace: HighlightStyle::default(),
  591            },
  592            unnecessary_code_fade: Default::default(),
  593            show_underlines: true,
  594        }
  595    }
  596}
  597
  598pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  599    let show_background = AllLanguageSettings::get_global(cx)
  600        .defaults
  601        .inlay_hints
  602        .show_background;
  603
  604    let mut style = cx
  605        .theme()
  606        .syntax()
  607        .style_for_name("hint")
  608        .unwrap_or_default();
  609
  610    if style.color.is_none() {
  611        style.color = Some(cx.theme().status().hint);
  612    }
  613
  614    if !show_background {
  615        style.background_color = None;
  616        return style;
  617    }
  618
  619    if style.background_color.is_none() {
  620        style.background_color = Some(cx.theme().status().hint_background);
  621    }
  622
  623    style
  624}
  625
  626pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  627    EditPredictionStyles {
  628        insertion: HighlightStyle {
  629            color: Some(cx.theme().status().predictive),
  630            ..HighlightStyle::default()
  631        },
  632        whitespace: HighlightStyle {
  633            background_color: Some(cx.theme().status().created_background),
  634            ..HighlightStyle::default()
  635        },
  636    }
  637}
  638
  639type CompletionId = usize;
  640
  641pub(crate) enum EditDisplayMode {
  642    TabAccept,
  643    DiffPopover,
  644    Inline,
  645}
  646
  647enum EditPrediction {
  648    Edit {
  649        edits: Vec<(Range<Anchor>, Arc<str>)>,
  650        /// Predicted cursor position as (anchor, offset_from_anchor).
  651        /// The anchor is in multibuffer coordinates; after applying edits,
  652        /// resolve the anchor and add the offset to get the final cursor position.
  653        cursor_position: Option<(Anchor, usize)>,
  654        edit_preview: Option<EditPreview>,
  655        display_mode: EditDisplayMode,
  656        snapshot: BufferSnapshot,
  657    },
  658    /// Move to a specific location in the active editor
  659    MoveWithin {
  660        target: Anchor,
  661        snapshot: BufferSnapshot,
  662    },
  663    /// Move to a specific location in a different editor (not the active one)
  664    MoveOutside {
  665        target: language::Anchor,
  666        snapshot: BufferSnapshot,
  667    },
  668}
  669
  670struct EditPredictionState {
  671    inlay_ids: Vec<InlayId>,
  672    completion: EditPrediction,
  673    completion_id: Option<SharedString>,
  674    invalidation_range: Option<Range<Anchor>>,
  675}
  676
  677enum EditPredictionSettings {
  678    Disabled,
  679    Enabled {
  680        show_in_menu: bool,
  681        preview_requires_modifier: bool,
  682    },
  683}
  684
  685#[derive(Debug, Clone)]
  686struct InlineDiagnostic {
  687    message: SharedString,
  688    group_id: usize,
  689    is_primary: bool,
  690    start: Point,
  691    severity: lsp::DiagnosticSeverity,
  692}
  693
  694pub enum MenuEditPredictionsPolicy {
  695    Never,
  696    ByProvider,
  697}
  698
  699pub enum EditPredictionPreview {
  700    /// Modifier is not pressed
  701    Inactive { released_too_fast: bool },
  702    /// Modifier pressed
  703    Active {
  704        since: Instant,
  705        previous_scroll_position: Option<SharedScrollAnchor>,
  706    },
  707}
  708
  709#[derive(Copy, Clone, Eq, PartialEq)]
  710enum EditPredictionKeybindSurface {
  711    Inline,
  712    CursorPopoverCompact,
  713    CursorPopoverExpanded,
  714}
  715
  716#[derive(Copy, Clone, Eq, PartialEq, Debug)]
  717enum EditPredictionKeybindAction {
  718    Accept,
  719    Preview,
  720}
  721
  722struct EditPredictionKeybindDisplay {
  723    #[cfg(test)]
  724    accept_keystroke: Option<gpui::KeybindingKeystroke>,
  725    #[cfg(test)]
  726    preview_keystroke: Option<gpui::KeybindingKeystroke>,
  727    displayed_keystroke: Option<gpui::KeybindingKeystroke>,
  728    action: EditPredictionKeybindAction,
  729    missing_accept_keystroke: bool,
  730    show_hold_label: bool,
  731}
  732
  733impl EditPredictionPreview {
  734    pub fn released_too_fast(&self) -> bool {
  735        match self {
  736            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  737            EditPredictionPreview::Active { .. } => false,
  738        }
  739    }
  740
  741    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  742        if let EditPredictionPreview::Active {
  743            previous_scroll_position,
  744            ..
  745        } = self
  746        {
  747            *previous_scroll_position = scroll_position;
  748        }
  749    }
  750}
  751
  752pub struct ContextMenuOptions {
  753    pub min_entries_visible: usize,
  754    pub max_entries_visible: usize,
  755    pub placement: Option<ContextMenuPlacement>,
  756}
  757
  758#[derive(Debug, Clone, PartialEq, Eq)]
  759pub enum ContextMenuPlacement {
  760    Above,
  761    Below,
  762}
  763
  764#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  765struct EditorActionId(usize);
  766
  767impl EditorActionId {
  768    pub fn post_inc(&mut self) -> Self {
  769        let answer = self.0;
  770
  771        *self = Self(answer + 1);
  772
  773        Self(answer)
  774    }
  775}
  776
  777// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  778// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  779
  780type BackgroundHighlight = (
  781    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  782    Arc<[Range<Anchor>]>,
  783);
  784type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  785
  786#[derive(Default)]
  787struct ScrollbarMarkerState {
  788    scrollbar_size: Size<Pixels>,
  789    dirty: bool,
  790    markers: Arc<[PaintQuad]>,
  791    pending_refresh: Option<Task<Result<()>>>,
  792}
  793
  794impl ScrollbarMarkerState {
  795    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  796        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  797    }
  798}
  799
  800#[derive(Clone, Copy, PartialEq, Eq)]
  801pub enum MinimapVisibility {
  802    Disabled,
  803    Enabled {
  804        /// The configuration currently present in the users settings.
  805        setting_configuration: bool,
  806        /// Whether to override the currently set visibility from the users setting.
  807        toggle_override: bool,
  808    },
  809}
  810
  811impl MinimapVisibility {
  812    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  813        if mode.is_full() {
  814            Self::Enabled {
  815                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  816                toggle_override: false,
  817            }
  818        } else {
  819            Self::Disabled
  820        }
  821    }
  822
  823    fn hidden(&self) -> Self {
  824        match *self {
  825            Self::Enabled {
  826                setting_configuration,
  827                ..
  828            } => Self::Enabled {
  829                setting_configuration,
  830                toggle_override: setting_configuration,
  831            },
  832            Self::Disabled => Self::Disabled,
  833        }
  834    }
  835
  836    fn disabled(&self) -> bool {
  837        matches!(*self, Self::Disabled)
  838    }
  839
  840    fn settings_visibility(&self) -> bool {
  841        match *self {
  842            Self::Enabled {
  843                setting_configuration,
  844                ..
  845            } => setting_configuration,
  846            _ => false,
  847        }
  848    }
  849
  850    fn visible(&self) -> bool {
  851        match *self {
  852            Self::Enabled {
  853                setting_configuration,
  854                toggle_override,
  855            } => setting_configuration ^ toggle_override,
  856            _ => false,
  857        }
  858    }
  859
  860    fn toggle_visibility(&self) -> Self {
  861        match *self {
  862            Self::Enabled {
  863                toggle_override,
  864                setting_configuration,
  865            } => Self::Enabled {
  866                setting_configuration,
  867                toggle_override: !toggle_override,
  868            },
  869            Self::Disabled => Self::Disabled,
  870        }
  871    }
  872}
  873
  874#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  875pub enum BufferSerialization {
  876    All,
  877    NonDirtyBuffers,
  878}
  879
  880impl BufferSerialization {
  881    fn new(restore_unsaved_buffers: bool) -> Self {
  882        if restore_unsaved_buffers {
  883            Self::All
  884        } else {
  885            Self::NonDirtyBuffers
  886        }
  887    }
  888}
  889
  890/// Addons allow storing per-editor state in other crates (e.g. Vim)
  891pub trait Addon: 'static {
  892    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  893
  894    fn render_buffer_header_controls(
  895        &self,
  896        _: &ExcerptInfo,
  897        _: &Window,
  898        _: &App,
  899    ) -> Option<AnyElement> {
  900        None
  901    }
  902
  903    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  904        None
  905    }
  906
  907    fn to_any(&self) -> &dyn std::any::Any;
  908
  909    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  910        None
  911    }
  912}
  913
  914struct ChangeLocation {
  915    current: Option<Vec<Anchor>>,
  916    original: Vec<Anchor>,
  917}
  918impl ChangeLocation {
  919    fn locations(&self) -> &[Anchor] {
  920        self.current.as_ref().unwrap_or(&self.original)
  921    }
  922}
  923
  924/// A set of caret positions, registered when the editor was edited.
  925pub struct ChangeList {
  926    changes: Vec<ChangeLocation>,
  927    /// Currently "selected" change.
  928    position: Option<usize>,
  929}
  930
  931impl ChangeList {
  932    pub fn new() -> Self {
  933        Self {
  934            changes: Vec::new(),
  935            position: None,
  936        }
  937    }
  938
  939    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  940    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  941    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  942        if self.changes.is_empty() {
  943            return None;
  944        }
  945
  946        let prev = self.position.unwrap_or(self.changes.len());
  947        let next = if direction == Direction::Prev {
  948            prev.saturating_sub(count)
  949        } else {
  950            (prev + count).min(self.changes.len() - 1)
  951        };
  952        self.position = Some(next);
  953        self.changes.get(next).map(|change| change.locations())
  954    }
  955
  956    /// Adds a new change to the list, resetting the change list position.
  957    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  958        self.position.take();
  959        if let Some(last) = self.changes.last_mut()
  960            && group
  961        {
  962            last.current = Some(new_positions)
  963        } else {
  964            self.changes.push(ChangeLocation {
  965                original: new_positions,
  966                current: None,
  967            });
  968        }
  969    }
  970
  971    pub fn last(&self) -> Option<&[Anchor]> {
  972        self.changes.last().map(|change| change.locations())
  973    }
  974
  975    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  976        self.changes.last().map(|change| change.original.as_slice())
  977    }
  978
  979    pub fn invert_last_group(&mut self) {
  980        if let Some(last) = self.changes.last_mut()
  981            && let Some(current) = last.current.as_mut()
  982        {
  983            mem::swap(&mut last.original, current);
  984        }
  985    }
  986}
  987
  988#[derive(Clone)]
  989struct InlineBlamePopoverState {
  990    scroll_handle: ScrollHandle,
  991    commit_message: Option<ParsedCommitMessage>,
  992    markdown: Entity<Markdown>,
  993}
  994
  995struct InlineBlamePopover {
  996    position: gpui::Point<Pixels>,
  997    hide_task: Option<Task<()>>,
  998    popover_bounds: Option<Bounds<Pixels>>,
  999    popover_state: InlineBlamePopoverState,
 1000    keyboard_grace: bool,
 1001}
 1002
 1003enum SelectionDragState {
 1004    /// State when no drag related activity is detected.
 1005    None,
 1006    /// State when the mouse is down on a selection that is about to be dragged.
 1007    ReadyToDrag {
 1008        selection: Selection<Anchor>,
 1009        click_position: gpui::Point<Pixels>,
 1010        mouse_down_time: Instant,
 1011    },
 1012    /// State when the mouse is dragging the selection in the editor.
 1013    Dragging {
 1014        selection: Selection<Anchor>,
 1015        drop_cursor: Selection<Anchor>,
 1016        hide_drop_cursor: bool,
 1017    },
 1018}
 1019
 1020enum ColumnarSelectionState {
 1021    FromMouse {
 1022        selection_tail: Anchor,
 1023        display_point: Option<DisplayPoint>,
 1024    },
 1025    FromSelection {
 1026        selection_tail: Anchor,
 1027    },
 1028}
 1029
 1030/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1031/// a breakpoint on them.
 1032#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1033struct PhantomBreakpointIndicator {
 1034    display_row: DisplayRow,
 1035    /// There's a small debounce between hovering over the line and showing the indicator.
 1036    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1037    is_active: bool,
 1038    collides_with_existing_breakpoint: bool,
 1039}
 1040
 1041/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1042/// in diff view mode.
 1043#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1044pub(crate) struct PhantomDiffReviewIndicator {
 1045    /// The starting anchor of the selection (or the only row if not dragging).
 1046    pub start: Anchor,
 1047    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1048    pub end: Anchor,
 1049    /// There's a small debounce between hovering over the line and showing the indicator.
 1050    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1051    pub is_active: bool,
 1052}
 1053
 1054#[derive(Clone, Debug)]
 1055pub(crate) struct DiffReviewDragState {
 1056    pub start_anchor: Anchor,
 1057    pub current_anchor: Anchor,
 1058}
 1059
 1060impl DiffReviewDragState {
 1061    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1062        let start = self.start_anchor.to_display_point(snapshot).row();
 1063        let current = self.current_anchor.to_display_point(snapshot).row();
 1064
 1065        (start..=current).sorted()
 1066    }
 1067}
 1068
 1069/// Identifies a specific hunk in the diff buffer.
 1070/// Used as a key to group comments by their location.
 1071#[derive(Clone, Debug)]
 1072pub struct DiffHunkKey {
 1073    /// The file path (relative to worktree) this hunk belongs to.
 1074    pub file_path: Arc<util::rel_path::RelPath>,
 1075    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1076    pub hunk_start_anchor: Anchor,
 1077}
 1078
 1079/// A review comment stored locally before being sent to the Agent panel.
 1080#[derive(Clone)]
 1081pub struct StoredReviewComment {
 1082    /// Unique identifier for this comment (for edit/delete operations).
 1083    pub id: usize,
 1084    /// The comment text entered by the user.
 1085    pub comment: String,
 1086    /// Anchors for the code range being reviewed.
 1087    pub range: Range<Anchor>,
 1088    /// Timestamp when the comment was created (for chronological ordering).
 1089    pub created_at: Instant,
 1090    /// Whether this comment is currently being edited inline.
 1091    pub is_editing: bool,
 1092}
 1093
 1094impl StoredReviewComment {
 1095    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1096        Self {
 1097            id,
 1098            comment,
 1099            range: anchor_range,
 1100            created_at: Instant::now(),
 1101            is_editing: false,
 1102        }
 1103    }
 1104}
 1105
 1106/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1107pub(crate) struct DiffReviewOverlay {
 1108    pub anchor_range: Range<Anchor>,
 1109    /// The block ID for the overlay.
 1110    pub block_id: CustomBlockId,
 1111    /// The editor entity for the review input.
 1112    pub prompt_editor: Entity<Editor>,
 1113    /// The hunk key this overlay belongs to.
 1114    pub hunk_key: DiffHunkKey,
 1115    /// Whether the comments section is expanded.
 1116    pub comments_expanded: bool,
 1117    /// Editors for comments currently being edited inline.
 1118    /// Key: comment ID, Value: Editor entity for inline editing.
 1119    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1120    /// Subscriptions for inline edit editors' action handlers.
 1121    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1122    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1123    /// The current user's avatar URI for display in comment rows.
 1124    pub user_avatar_uri: Option<SharedUri>,
 1125    /// Subscription to keep the action handler alive.
 1126    _subscription: Subscription,
 1127}
 1128
 1129/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1130///
 1131/// See the [module level documentation](self) for more information.
 1132pub struct Editor {
 1133    focus_handle: FocusHandle,
 1134    last_focused_descendant: Option<WeakFocusHandle>,
 1135    /// The text buffer being edited
 1136    buffer: Entity<MultiBuffer>,
 1137    /// Map of how text in the buffer should be displayed.
 1138    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1139    pub display_map: Entity<DisplayMap>,
 1140    placeholder_display_map: Option<Entity<DisplayMap>>,
 1141    pub selections: SelectionsCollection,
 1142    pub scroll_manager: ScrollManager,
 1143    /// When inline assist editors are linked, they all render cursors because
 1144    /// typing enters text into each of them, even the ones that aren't focused.
 1145    pub(crate) show_cursor_when_unfocused: bool,
 1146    columnar_selection_state: Option<ColumnarSelectionState>,
 1147    add_selections_state: Option<AddSelectionsState>,
 1148    select_next_state: Option<SelectNextState>,
 1149    select_prev_state: Option<SelectNextState>,
 1150    selection_history: SelectionHistory,
 1151    defer_selection_effects: bool,
 1152    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1153    autoclose_regions: Vec<AutocloseRegion>,
 1154    snippet_stack: InvalidationStack<SnippetState>,
 1155    select_syntax_node_history: SelectSyntaxNodeHistory,
 1156    ime_transaction: Option<TransactionId>,
 1157    pub diagnostics_max_severity: DiagnosticSeverity,
 1158    active_diagnostics: ActiveDiagnostic,
 1159    show_inline_diagnostics: bool,
 1160    inline_diagnostics_update: Task<()>,
 1161    inline_diagnostics_enabled: bool,
 1162    diagnostics_enabled: bool,
 1163    word_completions_enabled: bool,
 1164    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1165    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1166    hard_wrap: Option<usize>,
 1167    project: Option<Entity<Project>>,
 1168    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1169    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1170    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1171    blink_manager: Entity<BlinkManager>,
 1172    show_cursor_names: bool,
 1173    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1174    pub show_local_selections: bool,
 1175    mode: EditorMode,
 1176    show_breadcrumbs: bool,
 1177    show_gutter: bool,
 1178    show_scrollbars: ScrollbarAxes,
 1179    minimap_visibility: MinimapVisibility,
 1180    offset_content: bool,
 1181    disable_expand_excerpt_buttons: bool,
 1182    delegate_expand_excerpts: bool,
 1183    delegate_stage_and_restore: bool,
 1184    delegate_open_excerpts: bool,
 1185    enable_lsp_data: bool,
 1186    enable_runnables: bool,
 1187    show_line_numbers: Option<bool>,
 1188    use_relative_line_numbers: Option<bool>,
 1189    show_git_diff_gutter: Option<bool>,
 1190    show_code_actions: Option<bool>,
 1191    show_runnables: Option<bool>,
 1192    show_breakpoints: Option<bool>,
 1193    show_diff_review_button: bool,
 1194    show_wrap_guides: Option<bool>,
 1195    show_indent_guides: Option<bool>,
 1196    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1197    highlight_order: usize,
 1198    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1199    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1200    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1201    scrollbar_marker_state: ScrollbarMarkerState,
 1202    active_indent_guides_state: ActiveIndentGuidesState,
 1203    nav_history: Option<ItemNavHistory>,
 1204    context_menu: RefCell<Option<CodeContextMenu>>,
 1205    context_menu_options: Option<ContextMenuOptions>,
 1206    mouse_context_menu: Option<MouseContextMenu>,
 1207    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1208    inline_blame_popover: Option<InlineBlamePopover>,
 1209    inline_blame_popover_show_task: Option<Task<()>>,
 1210    signature_help_state: SignatureHelpState,
 1211    auto_signature_help: Option<bool>,
 1212    find_all_references_task_sources: Vec<Anchor>,
 1213    next_completion_id: CompletionId,
 1214    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1215    code_actions_task: Option<Task<Result<()>>>,
 1216    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1217    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1218    debounced_selection_highlight_complete: bool,
 1219    document_highlights_task: Option<Task<()>>,
 1220    linked_editing_range_task: Option<Task<Option<()>>>,
 1221    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1222    pending_rename: Option<RenameState>,
 1223    searchable: bool,
 1224    cursor_shape: CursorShape,
 1225    /// Whether the cursor is offset one character to the left when something is
 1226    /// selected (needed for vim visual mode)
 1227    cursor_offset_on_selection: bool,
 1228    current_line_highlight: Option<CurrentLineHighlight>,
 1229    /// Whether to collapse search match ranges to just their start position.
 1230    /// When true, navigating to a match positions the cursor at the match
 1231    /// without selecting the matched text.
 1232    collapse_matches: bool,
 1233    autoindent_mode: Option<AutoindentMode>,
 1234    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1235    input_enabled: bool,
 1236    expects_character_input: bool,
 1237    use_modal_editing: bool,
 1238    read_only: bool,
 1239    leader_id: Option<CollaboratorId>,
 1240    remote_id: Option<ViewId>,
 1241    pub hover_state: HoverState,
 1242    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1243    prev_pressure_stage: Option<PressureStage>,
 1244    gutter_hovered: bool,
 1245    hovered_link_state: Option<HoveredLinkState>,
 1246    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1247    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1248    active_edit_prediction: Option<EditPredictionState>,
 1249    /// Used to prevent flickering as the user types while the menu is open
 1250    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1251    edit_prediction_settings: EditPredictionSettings,
 1252    edit_predictions_hidden_for_vim_mode: bool,
 1253    show_edit_predictions_override: Option<bool>,
 1254    show_completions_on_input_override: Option<bool>,
 1255    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1256    edit_prediction_preview: EditPredictionPreview,
 1257    in_leading_whitespace: bool,
 1258    next_inlay_id: usize,
 1259    next_color_inlay_id: usize,
 1260    _subscriptions: Vec<Subscription>,
 1261    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1262    gutter_dimensions: GutterDimensions,
 1263    style: Option<EditorStyle>,
 1264    text_style_refinement: Option<TextStyleRefinement>,
 1265    next_editor_action_id: EditorActionId,
 1266    editor_actions: Rc<
 1267        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1268    >,
 1269    use_autoclose: bool,
 1270    use_auto_surround: bool,
 1271    auto_replace_emoji_shortcode: bool,
 1272    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1273    show_git_blame_gutter: bool,
 1274    show_git_blame_inline: bool,
 1275    show_git_blame_inline_delay_task: Option<Task<()>>,
 1276    git_blame_inline_enabled: bool,
 1277    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1278    buffer_serialization: Option<BufferSerialization>,
 1279    show_selection_menu: Option<bool>,
 1280    blame: Option<Entity<GitBlame>>,
 1281    blame_subscription: Option<Subscription>,
 1282    custom_context_menu: Option<
 1283        Box<
 1284            dyn 'static
 1285                + Fn(
 1286                    &mut Self,
 1287                    DisplayPoint,
 1288                    &mut Window,
 1289                    &mut Context<Self>,
 1290                ) -> Option<Entity<ui::ContextMenu>>,
 1291        >,
 1292    >,
 1293    last_bounds: Option<Bounds<Pixels>>,
 1294    last_position_map: Option<Rc<PositionMap>>,
 1295    expect_bounds_change: Option<Bounds<Pixels>>,
 1296    runnables: RunnableData,
 1297    breakpoint_store: Option<Entity<BreakpointStore>>,
 1298    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1299    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1300    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1301    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1302    /// when hunks have comments stored.
 1303    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1304    /// Stored review comments grouped by hunk.
 1305    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1306    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1307    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1308    /// Counter for generating unique comment IDs.
 1309    next_review_comment_id: usize,
 1310    hovered_diff_hunk_row: Option<DisplayRow>,
 1311    pull_diagnostics_task: Task<()>,
 1312    in_project_search: bool,
 1313    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1314    breadcrumb_header: Option<String>,
 1315    focused_block: Option<FocusedBlock>,
 1316    next_scroll_position: NextScrollCursorCenterTopBottom,
 1317    addons: HashMap<TypeId, Box<dyn Addon>>,
 1318    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1319    load_diff_task: Option<Shared<Task<()>>>,
 1320    /// Whether we are temporarily displaying a diff other than git's
 1321    temporary_diff_override: bool,
 1322    selection_mark_mode: bool,
 1323    toggle_fold_multiple_buffers: Task<()>,
 1324    _scroll_cursor_center_top_bottom_task: Task<()>,
 1325    serialize_selections: Task<()>,
 1326    serialize_folds: Task<()>,
 1327    mouse_cursor_hidden: bool,
 1328    minimap: Option<Entity<Self>>,
 1329    hide_mouse_mode: HideMouseMode,
 1330    pub change_list: ChangeList,
 1331    inline_value_cache: InlineValueCache,
 1332    number_deleted_lines: bool,
 1333
 1334    selection_drag_state: SelectionDragState,
 1335    colors: Option<LspColorData>,
 1336    post_scroll_update: Task<()>,
 1337    refresh_colors_task: Task<()>,
 1338    use_document_folding_ranges: bool,
 1339    refresh_folding_ranges_task: Task<()>,
 1340    inlay_hints: Option<LspInlayHintData>,
 1341    folding_newlines: Task<()>,
 1342    select_next_is_case_sensitive: Option<bool>,
 1343    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1344    on_local_selections_changed:
 1345        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1346    suppress_selection_callback: bool,
 1347    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1348    accent_data: Option<AccentData>,
 1349    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1350    semantic_token_state: SemanticTokenState,
 1351    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1352    refresh_document_symbols_task: Shared<Task<()>>,
 1353    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1354    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1355    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1356    sticky_headers_task: Task<()>,
 1357    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1358    pub(crate) colorize_brackets_task: Task<()>,
 1359}
 1360
 1361#[derive(Debug, PartialEq)]
 1362struct AccentData {
 1363    colors: AccentColors,
 1364    overrides: Vec<SharedString>,
 1365}
 1366
 1367fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1368    if debounce_ms > 0 {
 1369        Some(Duration::from_millis(debounce_ms))
 1370    } else {
 1371        None
 1372    }
 1373}
 1374
 1375#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1376enum NextScrollCursorCenterTopBottom {
 1377    #[default]
 1378    Center,
 1379    Top,
 1380    Bottom,
 1381}
 1382
 1383impl NextScrollCursorCenterTopBottom {
 1384    fn next(&self) -> Self {
 1385        match self {
 1386            Self::Center => Self::Top,
 1387            Self::Top => Self::Bottom,
 1388            Self::Bottom => Self::Center,
 1389        }
 1390    }
 1391}
 1392
 1393#[derive(Clone)]
 1394pub struct EditorSnapshot {
 1395    pub mode: EditorMode,
 1396    show_gutter: bool,
 1397    offset_content: bool,
 1398    show_line_numbers: Option<bool>,
 1399    number_deleted_lines: bool,
 1400    show_git_diff_gutter: Option<bool>,
 1401    show_code_actions: Option<bool>,
 1402    show_runnables: Option<bool>,
 1403    show_breakpoints: Option<bool>,
 1404    git_blame_gutter_max_author_length: Option<usize>,
 1405    pub display_snapshot: DisplaySnapshot,
 1406    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1407    is_focused: bool,
 1408    scroll_anchor: SharedScrollAnchor,
 1409    ongoing_scroll: OngoingScroll,
 1410    current_line_highlight: CurrentLineHighlight,
 1411    gutter_hovered: bool,
 1412    semantic_tokens_enabled: bool,
 1413}
 1414
 1415#[derive(Default, Debug, Clone, Copy)]
 1416pub struct GutterDimensions {
 1417    pub left_padding: Pixels,
 1418    pub right_padding: Pixels,
 1419    pub width: Pixels,
 1420    pub margin: Pixels,
 1421    pub git_blame_entries_width: Option<Pixels>,
 1422}
 1423
 1424impl GutterDimensions {
 1425    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1426        Self {
 1427            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1428            ..Default::default()
 1429        }
 1430    }
 1431
 1432    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1433        -cx.text_system().descent(font_id, font_size)
 1434    }
 1435    /// The full width of the space taken up by the gutter.
 1436    pub fn full_width(&self) -> Pixels {
 1437        self.margin + self.width
 1438    }
 1439
 1440    /// The width of the space reserved for the fold indicators,
 1441    /// use alongside 'justify_end' and `gutter_width` to
 1442    /// right align content with the line numbers
 1443    pub fn fold_area_width(&self) -> Pixels {
 1444        self.margin + self.right_padding
 1445    }
 1446}
 1447
 1448struct CharacterDimensions {
 1449    em_width: Pixels,
 1450    em_advance: Pixels,
 1451    line_height: Pixels,
 1452}
 1453
 1454#[derive(Debug)]
 1455pub struct RemoteSelection {
 1456    pub replica_id: ReplicaId,
 1457    pub selection: Selection<Anchor>,
 1458    pub cursor_shape: CursorShape,
 1459    pub collaborator_id: CollaboratorId,
 1460    pub line_mode: bool,
 1461    pub user_name: Option<SharedString>,
 1462    pub color: PlayerColor,
 1463}
 1464
 1465#[derive(Clone, Debug)]
 1466struct SelectionHistoryEntry {
 1467    selections: Arc<[Selection<Anchor>]>,
 1468    select_next_state: Option<SelectNextState>,
 1469    select_prev_state: Option<SelectNextState>,
 1470    add_selections_state: Option<AddSelectionsState>,
 1471}
 1472
 1473#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1474enum SelectionHistoryMode {
 1475    #[default]
 1476    Normal,
 1477    Undoing,
 1478    Redoing,
 1479    Skipping,
 1480}
 1481
 1482#[derive(Clone, PartialEq, Eq, Hash)]
 1483struct HoveredCursor {
 1484    replica_id: ReplicaId,
 1485    selection_id: usize,
 1486}
 1487
 1488#[derive(Debug)]
 1489/// SelectionEffects controls the side-effects of updating the selection.
 1490///
 1491/// The default behaviour does "what you mostly want":
 1492/// - it pushes to the nav history if the cursor moved by >10 lines
 1493/// - it re-triggers completion requests
 1494/// - it scrolls to fit
 1495///
 1496/// You might want to modify these behaviours. For example when doing a "jump"
 1497/// like go to definition, we always want to add to nav history; but when scrolling
 1498/// in vim mode we never do.
 1499///
 1500/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1501/// move.
 1502#[derive(Clone)]
 1503pub struct SelectionEffects {
 1504    nav_history: Option<bool>,
 1505    completions: bool,
 1506    scroll: Option<Autoscroll>,
 1507}
 1508
 1509impl Default for SelectionEffects {
 1510    fn default() -> Self {
 1511        Self {
 1512            nav_history: None,
 1513            completions: true,
 1514            scroll: Some(Autoscroll::fit()),
 1515        }
 1516    }
 1517}
 1518impl SelectionEffects {
 1519    pub fn scroll(scroll: Autoscroll) -> Self {
 1520        Self {
 1521            scroll: Some(scroll),
 1522            ..Default::default()
 1523        }
 1524    }
 1525
 1526    pub fn no_scroll() -> Self {
 1527        Self {
 1528            scroll: None,
 1529            ..Default::default()
 1530        }
 1531    }
 1532
 1533    pub fn completions(self, completions: bool) -> Self {
 1534        Self {
 1535            completions,
 1536            ..self
 1537        }
 1538    }
 1539
 1540    pub fn nav_history(self, nav_history: bool) -> Self {
 1541        Self {
 1542            nav_history: Some(nav_history),
 1543            ..self
 1544        }
 1545    }
 1546}
 1547
 1548struct DeferredSelectionEffectsState {
 1549    changed: bool,
 1550    effects: SelectionEffects,
 1551    old_cursor_position: Anchor,
 1552    history_entry: SelectionHistoryEntry,
 1553}
 1554
 1555#[derive(Default)]
 1556struct SelectionHistory {
 1557    #[allow(clippy::type_complexity)]
 1558    selections_by_transaction:
 1559        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1560    mode: SelectionHistoryMode,
 1561    undo_stack: VecDeque<SelectionHistoryEntry>,
 1562    redo_stack: VecDeque<SelectionHistoryEntry>,
 1563}
 1564
 1565impl SelectionHistory {
 1566    #[track_caller]
 1567    fn insert_transaction(
 1568        &mut self,
 1569        transaction_id: TransactionId,
 1570        selections: Arc<[Selection<Anchor>]>,
 1571    ) {
 1572        if selections.is_empty() {
 1573            log::error!(
 1574                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1575                std::panic::Location::caller()
 1576            );
 1577            return;
 1578        }
 1579        self.selections_by_transaction
 1580            .insert(transaction_id, (selections, None));
 1581    }
 1582
 1583    #[allow(clippy::type_complexity)]
 1584    fn transaction(
 1585        &self,
 1586        transaction_id: TransactionId,
 1587    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1588        self.selections_by_transaction.get(&transaction_id)
 1589    }
 1590
 1591    #[allow(clippy::type_complexity)]
 1592    fn transaction_mut(
 1593        &mut self,
 1594        transaction_id: TransactionId,
 1595    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1596        self.selections_by_transaction.get_mut(&transaction_id)
 1597    }
 1598
 1599    fn push(&mut self, entry: SelectionHistoryEntry) {
 1600        if !entry.selections.is_empty() {
 1601            match self.mode {
 1602                SelectionHistoryMode::Normal => {
 1603                    self.push_undo(entry);
 1604                    self.redo_stack.clear();
 1605                }
 1606                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1607                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1608                SelectionHistoryMode::Skipping => {}
 1609            }
 1610        }
 1611    }
 1612
 1613    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1614        if self
 1615            .undo_stack
 1616            .back()
 1617            .is_none_or(|e| e.selections != entry.selections)
 1618        {
 1619            self.undo_stack.push_back(entry);
 1620            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1621                self.undo_stack.pop_front();
 1622            }
 1623        }
 1624    }
 1625
 1626    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1627        if self
 1628            .redo_stack
 1629            .back()
 1630            .is_none_or(|e| e.selections != entry.selections)
 1631        {
 1632            self.redo_stack.push_back(entry);
 1633            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1634                self.redo_stack.pop_front();
 1635            }
 1636        }
 1637    }
 1638}
 1639
 1640#[derive(Clone, Copy)]
 1641pub struct RowHighlightOptions {
 1642    pub autoscroll: bool,
 1643    pub include_gutter: bool,
 1644}
 1645
 1646impl Default for RowHighlightOptions {
 1647    fn default() -> Self {
 1648        Self {
 1649            autoscroll: Default::default(),
 1650            include_gutter: true,
 1651        }
 1652    }
 1653}
 1654
 1655struct RowHighlight {
 1656    index: usize,
 1657    range: Range<Anchor>,
 1658    color: Hsla,
 1659    options: RowHighlightOptions,
 1660    type_id: TypeId,
 1661}
 1662
 1663#[derive(Clone, Debug)]
 1664struct AddSelectionsState {
 1665    groups: Vec<AddSelectionsGroup>,
 1666}
 1667
 1668#[derive(Clone, Debug)]
 1669struct AddSelectionsGroup {
 1670    above: bool,
 1671    stack: Vec<usize>,
 1672}
 1673
 1674#[derive(Clone)]
 1675struct SelectNextState {
 1676    query: AhoCorasick,
 1677    wordwise: bool,
 1678    done: bool,
 1679}
 1680
 1681impl std::fmt::Debug for SelectNextState {
 1682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1683        f.debug_struct(std::any::type_name::<Self>())
 1684            .field("wordwise", &self.wordwise)
 1685            .field("done", &self.done)
 1686            .finish()
 1687    }
 1688}
 1689
 1690#[derive(Debug)]
 1691struct AutocloseRegion {
 1692    selection_id: usize,
 1693    range: Range<Anchor>,
 1694    pair: BracketPair,
 1695}
 1696
 1697#[derive(Debug)]
 1698struct SnippetState {
 1699    ranges: Vec<Vec<Range<Anchor>>>,
 1700    active_index: usize,
 1701    choices: Vec<Option<Vec<String>>>,
 1702}
 1703
 1704#[doc(hidden)]
 1705pub struct RenameState {
 1706    pub range: Range<Anchor>,
 1707    pub old_name: Arc<str>,
 1708    pub editor: Entity<Editor>,
 1709    block_id: CustomBlockId,
 1710}
 1711
 1712struct InvalidationStack<T>(Vec<T>);
 1713
 1714struct RegisteredEditPredictionDelegate {
 1715    provider: Arc<dyn EditPredictionDelegateHandle>,
 1716    _subscription: Subscription,
 1717}
 1718
 1719#[derive(Debug, PartialEq, Eq)]
 1720pub struct ActiveDiagnosticGroup {
 1721    pub active_range: Range<Anchor>,
 1722    pub active_message: String,
 1723    pub group_id: usize,
 1724    pub blocks: HashSet<CustomBlockId>,
 1725}
 1726
 1727#[derive(Debug, PartialEq, Eq)]
 1728
 1729pub(crate) enum ActiveDiagnostic {
 1730    None,
 1731    All,
 1732    Group(ActiveDiagnosticGroup),
 1733}
 1734
 1735#[derive(Serialize, Deserialize, Clone, Debug)]
 1736pub struct ClipboardSelection {
 1737    /// The number of bytes in this selection.
 1738    pub len: usize,
 1739    /// Whether this was a full-line selection.
 1740    pub is_entire_line: bool,
 1741    /// The indentation of the first line when this content was originally copied.
 1742    pub first_line_indent: u32,
 1743    #[serde(default)]
 1744    pub file_path: Option<PathBuf>,
 1745    #[serde(default)]
 1746    pub line_range: Option<RangeInclusive<u32>>,
 1747}
 1748
 1749impl ClipboardSelection {
 1750    pub fn for_buffer(
 1751        len: usize,
 1752        is_entire_line: bool,
 1753        range: Range<Point>,
 1754        buffer: &MultiBufferSnapshot,
 1755        project: Option<&Entity<Project>>,
 1756        cx: &App,
 1757    ) -> Self {
 1758        let first_line_indent = buffer
 1759            .indent_size_for_line(MultiBufferRow(range.start.row))
 1760            .len;
 1761
 1762        let file_path = util::maybe!({
 1763            let project = project?.read(cx);
 1764            let file = buffer.file_at(range.start)?;
 1765            let project_path = ProjectPath {
 1766                worktree_id: file.worktree_id(cx),
 1767                path: file.path().clone(),
 1768            };
 1769            project.absolute_path(&project_path, cx)
 1770        });
 1771
 1772        let line_range = file_path.as_ref().and_then(|_| {
 1773            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1774            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1775            if start_excerpt_id == end_excerpt_id {
 1776                Some(start_point.row..=end_point.row)
 1777            } else {
 1778                None
 1779            }
 1780        });
 1781
 1782        Self {
 1783            len,
 1784            is_entire_line,
 1785            first_line_indent,
 1786            file_path,
 1787            line_range,
 1788        }
 1789    }
 1790}
 1791
 1792// selections, scroll behavior, was newest selection reversed
 1793type SelectSyntaxNodeHistoryState = (
 1794    Box<[Selection<Anchor>]>,
 1795    SelectSyntaxNodeScrollBehavior,
 1796    bool,
 1797);
 1798
 1799#[derive(Default)]
 1800struct SelectSyntaxNodeHistory {
 1801    stack: Vec<SelectSyntaxNodeHistoryState>,
 1802    // disable temporarily to allow changing selections without losing the stack
 1803    pub disable_clearing: bool,
 1804}
 1805
 1806impl SelectSyntaxNodeHistory {
 1807    pub fn try_clear(&mut self) {
 1808        if !self.disable_clearing {
 1809            self.stack.clear();
 1810        }
 1811    }
 1812
 1813    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1814        self.stack.push(selection);
 1815    }
 1816
 1817    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1818        self.stack.pop()
 1819    }
 1820}
 1821
 1822enum SelectSyntaxNodeScrollBehavior {
 1823    CursorTop,
 1824    FitSelection,
 1825    CursorBottom,
 1826}
 1827
 1828#[derive(Debug, Clone, Copy)]
 1829pub(crate) struct NavigationData {
 1830    cursor_anchor: Anchor,
 1831    cursor_position: Point,
 1832    scroll_anchor: ScrollAnchor,
 1833    scroll_top_row: u32,
 1834}
 1835
 1836#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1837pub enum GotoDefinitionKind {
 1838    Symbol,
 1839    Declaration,
 1840    Type,
 1841    Implementation,
 1842}
 1843
 1844pub enum FormatTarget {
 1845    Buffers(HashSet<Entity<Buffer>>),
 1846    Ranges(Vec<Range<MultiBufferPoint>>),
 1847}
 1848
 1849pub(crate) struct FocusedBlock {
 1850    id: BlockId,
 1851    focus_handle: WeakFocusHandle,
 1852}
 1853
 1854#[derive(Clone, Debug)]
 1855pub enum JumpData {
 1856    MultiBufferRow {
 1857        row: MultiBufferRow,
 1858        line_offset_from_top: u32,
 1859    },
 1860    MultiBufferPoint {
 1861        excerpt_id: ExcerptId,
 1862        position: Point,
 1863        anchor: text::Anchor,
 1864        line_offset_from_top: u32,
 1865    },
 1866}
 1867
 1868pub enum MultibufferSelectionMode {
 1869    First,
 1870    All,
 1871}
 1872
 1873#[derive(Clone, Copy, Debug, Default)]
 1874pub struct RewrapOptions {
 1875    pub override_language_settings: bool,
 1876    pub preserve_existing_whitespace: bool,
 1877    pub line_length: Option<usize>,
 1878}
 1879
 1880impl Editor {
 1881    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1882        let buffer = cx.new(|cx| Buffer::local("", cx));
 1883        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1884        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1885    }
 1886
 1887    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1888        let buffer = cx.new(|cx| Buffer::local("", cx));
 1889        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1890        Self::new(EditorMode::full(), buffer, None, window, cx)
 1891    }
 1892
 1893    pub fn auto_height(
 1894        min_lines: usize,
 1895        max_lines: usize,
 1896        window: &mut Window,
 1897        cx: &mut Context<Self>,
 1898    ) -> Self {
 1899        let buffer = cx.new(|cx| Buffer::local("", cx));
 1900        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1901        Self::new(
 1902            EditorMode::AutoHeight {
 1903                min_lines,
 1904                max_lines: Some(max_lines),
 1905            },
 1906            buffer,
 1907            None,
 1908            window,
 1909            cx,
 1910        )
 1911    }
 1912
 1913    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1914    /// The editor grows as tall as needed to fit its content.
 1915    pub fn auto_height_unbounded(
 1916        min_lines: usize,
 1917        window: &mut Window,
 1918        cx: &mut Context<Self>,
 1919    ) -> Self {
 1920        let buffer = cx.new(|cx| Buffer::local("", cx));
 1921        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1922        Self::new(
 1923            EditorMode::AutoHeight {
 1924                min_lines,
 1925                max_lines: None,
 1926            },
 1927            buffer,
 1928            None,
 1929            window,
 1930            cx,
 1931        )
 1932    }
 1933
 1934    pub fn for_buffer(
 1935        buffer: Entity<Buffer>,
 1936        project: Option<Entity<Project>>,
 1937        window: &mut Window,
 1938        cx: &mut Context<Self>,
 1939    ) -> Self {
 1940        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1941        Self::new(EditorMode::full(), buffer, project, window, cx)
 1942    }
 1943
 1944    pub fn for_multibuffer(
 1945        buffer: Entity<MultiBuffer>,
 1946        project: Option<Entity<Project>>,
 1947        window: &mut Window,
 1948        cx: &mut Context<Self>,
 1949    ) -> Self {
 1950        Self::new(EditorMode::full(), buffer, project, window, cx)
 1951    }
 1952
 1953    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1954        let mut clone = Self::new(
 1955            self.mode.clone(),
 1956            self.buffer.clone(),
 1957            self.project.clone(),
 1958            window,
 1959            cx,
 1960        );
 1961        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1962            let snapshot = display_map.snapshot(cx);
 1963            clone.display_map.update(cx, |display_map, cx| {
 1964                display_map.set_state(&snapshot, cx);
 1965            });
 1966            snapshot
 1967        });
 1968        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1969        clone.folds_did_change(cx);
 1970        clone.selections.clone_state(&self.selections);
 1971        clone
 1972            .scroll_manager
 1973            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1974        clone.searchable = self.searchable;
 1975        clone.read_only = self.read_only;
 1976        clone.buffers_with_disabled_indent_guides =
 1977            self.buffers_with_disabled_indent_guides.clone();
 1978        clone
 1979    }
 1980
 1981    pub fn new(
 1982        mode: EditorMode,
 1983        buffer: Entity<MultiBuffer>,
 1984        project: Option<Entity<Project>>,
 1985        window: &mut Window,
 1986        cx: &mut Context<Self>,
 1987    ) -> Self {
 1988        Editor::new_internal(mode, buffer, project, None, window, cx)
 1989    }
 1990
 1991    pub fn refresh_sticky_headers(
 1992        &mut self,
 1993        display_snapshot: &DisplaySnapshot,
 1994        cx: &mut Context<Editor>,
 1995    ) {
 1996        if !self.mode.is_full() {
 1997            return;
 1998        }
 1999        let multi_buffer = display_snapshot.buffer_snapshot();
 2000        let scroll_anchor = self
 2001            .scroll_manager
 2002            .native_anchor(display_snapshot, cx)
 2003            .anchor;
 2004        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2005            return;
 2006        };
 2007        let buffer = buffer.clone();
 2008
 2009        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2010        let max_row = buffer.max_point().row;
 2011        let start_row = buffer_visible_start.row.min(max_row);
 2012        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2013
 2014        let syntax = self.style(cx).syntax.clone();
 2015        let background_task = cx.background_spawn(async move {
 2016            buffer
 2017                .outline_items_containing(
 2018                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2019                    true,
 2020                    Some(syntax.as_ref()),
 2021                )
 2022                .into_iter()
 2023                .map(|outline_item| OutlineItem {
 2024                    depth: outline_item.depth,
 2025                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2026                    source_range_for_text: Anchor::range_in_buffer(
 2027                        excerpt_id,
 2028                        outline_item.source_range_for_text,
 2029                    ),
 2030                    text: outline_item.text,
 2031                    highlight_ranges: outline_item.highlight_ranges,
 2032                    name_ranges: outline_item.name_ranges,
 2033                    body_range: outline_item
 2034                        .body_range
 2035                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2036                    annotation_range: outline_item
 2037                        .annotation_range
 2038                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2039                })
 2040                .collect()
 2041        });
 2042        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2043            let sticky_headers = background_task.await;
 2044            this.update(cx, |this, cx| {
 2045                this.sticky_headers = Some(sticky_headers);
 2046                cx.notify();
 2047            })
 2048            .ok();
 2049        });
 2050    }
 2051
 2052    fn new_internal(
 2053        mode: EditorMode,
 2054        multi_buffer: Entity<MultiBuffer>,
 2055        project: Option<Entity<Project>>,
 2056        display_map: Option<Entity<DisplayMap>>,
 2057        window: &mut Window,
 2058        cx: &mut Context<Self>,
 2059    ) -> Self {
 2060        debug_assert!(
 2061            display_map.is_none() || mode.is_minimap(),
 2062            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2063        );
 2064
 2065        let full_mode = mode.is_full();
 2066        let is_minimap = mode.is_minimap();
 2067        let diagnostics_max_severity = if full_mode {
 2068            EditorSettings::get_global(cx)
 2069                .diagnostics_max_severity
 2070                .unwrap_or(DiagnosticSeverity::Hint)
 2071        } else {
 2072            DiagnosticSeverity::Off
 2073        };
 2074        let style = window.text_style();
 2075        let font_size = style.font_size.to_pixels(window.rem_size());
 2076        let editor = cx.entity().downgrade();
 2077        let fold_placeholder = FoldPlaceholder {
 2078            constrain_width: false,
 2079            render: Arc::new(move |fold_id, fold_range, cx| {
 2080                let editor = editor.clone();
 2081                FoldPlaceholder::fold_element(fold_id, cx)
 2082                    .cursor_pointer()
 2083                    .child("")
 2084                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2085                    .on_click(move |_, _window, cx| {
 2086                        editor
 2087                            .update(cx, |editor, cx| {
 2088                                editor.unfold_ranges(
 2089                                    &[fold_range.start..fold_range.end],
 2090                                    true,
 2091                                    false,
 2092                                    cx,
 2093                                );
 2094                                cx.stop_propagation();
 2095                            })
 2096                            .ok();
 2097                    })
 2098                    .into_any()
 2099            }),
 2100            merge_adjacent: true,
 2101            ..FoldPlaceholder::default()
 2102        };
 2103        let display_map = display_map.unwrap_or_else(|| {
 2104            cx.new(|cx| {
 2105                DisplayMap::new(
 2106                    multi_buffer.clone(),
 2107                    style.font(),
 2108                    font_size,
 2109                    None,
 2110                    FILE_HEADER_HEIGHT,
 2111                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2112                    fold_placeholder,
 2113                    diagnostics_max_severity,
 2114                    cx,
 2115                )
 2116            })
 2117        });
 2118
 2119        let selections = SelectionsCollection::new();
 2120
 2121        let blink_manager = cx.new(|cx| {
 2122            let mut blink_manager = BlinkManager::new(
 2123                CURSOR_BLINK_INTERVAL,
 2124                |cx| EditorSettings::get_global(cx).cursor_blink,
 2125                cx,
 2126            );
 2127            if is_minimap {
 2128                blink_manager.disable(cx);
 2129            }
 2130            blink_manager
 2131        });
 2132
 2133        let soft_wrap_mode_override =
 2134            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2135
 2136        let mut project_subscriptions = Vec::new();
 2137        if full_mode && let Some(project) = project.as_ref() {
 2138            project_subscriptions.push(cx.subscribe_in(
 2139                project,
 2140                window,
 2141                |editor, _, event, window, cx| match event {
 2142                    project::Event::RefreshCodeLens => {
 2143                        // we always query lens with actions, without storing them, always refreshing them
 2144                    }
 2145                    project::Event::RefreshInlayHints {
 2146                        server_id,
 2147                        request_id,
 2148                    } => {
 2149                        editor.refresh_inlay_hints(
 2150                            InlayHintRefreshReason::RefreshRequested {
 2151                                server_id: *server_id,
 2152                                request_id: *request_id,
 2153                            },
 2154                            cx,
 2155                        );
 2156                    }
 2157                    project::Event::RefreshSemanticTokens {
 2158                        server_id,
 2159                        request_id,
 2160                    } => {
 2161                        editor.refresh_semantic_tokens(
 2162                            None,
 2163                            Some(RefreshForServer {
 2164                                server_id: *server_id,
 2165                                request_id: *request_id,
 2166                            }),
 2167                            cx,
 2168                        );
 2169                    }
 2170                    project::Event::LanguageServerRemoved(_) => {
 2171                        editor.registered_buffers.clear();
 2172                        editor.register_visible_buffers(cx);
 2173                        editor.invalidate_semantic_tokens(None);
 2174                        editor.refresh_runnables(None, window, cx);
 2175                        editor.update_lsp_data(None, window, cx);
 2176                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2177                    }
 2178                    project::Event::SnippetEdit(id, snippet_edits) => {
 2179                        // todo(lw): Non singletons
 2180                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2181                            let snapshot = buffer.read(cx).snapshot();
 2182                            let focus_handle = editor.focus_handle(cx);
 2183                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2184                                for (range, snippet) in snippet_edits {
 2185                                    let buffer_range =
 2186                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2187                                    editor
 2188                                        .insert_snippet(
 2189                                            &[MultiBufferOffset(buffer_range.start)
 2190                                                ..MultiBufferOffset(buffer_range.end)],
 2191                                            snippet.clone(),
 2192                                            window,
 2193                                            cx,
 2194                                        )
 2195                                        .ok();
 2196                                }
 2197                            }
 2198                        }
 2199                    }
 2200                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2201                        let buffer_id = *buffer_id;
 2202                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2203                            editor.register_buffer(buffer_id, cx);
 2204                            editor.refresh_runnables(Some(buffer_id), window, cx);
 2205                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2206                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2207                            refresh_linked_ranges(editor, window, cx);
 2208                            editor.refresh_code_actions(window, cx);
 2209                            editor.refresh_document_highlights(cx);
 2210                        }
 2211                    }
 2212
 2213                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2214                        let Some(workspace) = editor.workspace() else {
 2215                            return;
 2216                        };
 2217                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2218                        else {
 2219                            return;
 2220                        };
 2221
 2222                        if active_editor.entity_id() == cx.entity_id() {
 2223                            let entity_id = cx.entity_id();
 2224                            workspace.update(cx, |this, cx| {
 2225                                this.panes_mut()
 2226                                    .iter_mut()
 2227                                    .filter(|pane| pane.entity_id() != entity_id)
 2228                                    .for_each(|p| {
 2229                                        p.update(cx, |pane, _| {
 2230                                            pane.nav_history_mut().rename_item(
 2231                                                entity_id,
 2232                                                project_path.clone(),
 2233                                                abs_path.clone().into(),
 2234                                            );
 2235                                        })
 2236                                    });
 2237                            });
 2238
 2239                            Self::open_transaction_for_hidden_buffers(
 2240                                workspace,
 2241                                transaction.clone(),
 2242                                "Rename".to_string(),
 2243                                window,
 2244                                cx,
 2245                            );
 2246                        }
 2247                    }
 2248
 2249                    project::Event::WorkspaceEditApplied(transaction) => {
 2250                        let Some(workspace) = editor.workspace() else {
 2251                            return;
 2252                        };
 2253                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2254                        else {
 2255                            return;
 2256                        };
 2257
 2258                        if active_editor.entity_id() == cx.entity_id() {
 2259                            Self::open_transaction_for_hidden_buffers(
 2260                                workspace,
 2261                                transaction.clone(),
 2262                                "LSP Edit".to_string(),
 2263                                window,
 2264                                cx,
 2265                            );
 2266                        }
 2267                    }
 2268
 2269                    _ => {}
 2270                },
 2271            ));
 2272            if let Some(task_inventory) = project
 2273                .read(cx)
 2274                .task_store()
 2275                .read(cx)
 2276                .task_inventory()
 2277                .cloned()
 2278            {
 2279                project_subscriptions.push(cx.observe_in(
 2280                    &task_inventory,
 2281                    window,
 2282                    |editor, _, window, cx| {
 2283                        editor.refresh_runnables(None, window, cx);
 2284                    },
 2285                ));
 2286            };
 2287
 2288            project_subscriptions.push(cx.subscribe_in(
 2289                &project.read(cx).breakpoint_store(),
 2290                window,
 2291                |editor, _, event, window, cx| match event {
 2292                    BreakpointStoreEvent::ClearDebugLines => {
 2293                        editor.clear_row_highlights::<ActiveDebugLine>();
 2294                        editor.refresh_inline_values(cx);
 2295                    }
 2296                    BreakpointStoreEvent::SetDebugLine => {
 2297                        if editor.go_to_active_debug_line(window, cx) {
 2298                            cx.stop_propagation();
 2299                        }
 2300
 2301                        editor.refresh_inline_values(cx);
 2302                    }
 2303                    _ => {}
 2304                },
 2305            ));
 2306            let git_store = project.read(cx).git_store().clone();
 2307            let project = project.clone();
 2308            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2309                if let GitStoreEvent::RepositoryAdded = event {
 2310                    this.load_diff_task = Some(
 2311                        update_uncommitted_diff_for_buffer(
 2312                            cx.entity(),
 2313                            &project,
 2314                            this.buffer.read(cx).all_buffers(),
 2315                            this.buffer.clone(),
 2316                            cx,
 2317                        )
 2318                        .shared(),
 2319                    );
 2320                }
 2321            }));
 2322        }
 2323
 2324        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2325
 2326        let inlay_hint_settings =
 2327            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2328        let focus_handle = cx.focus_handle();
 2329        if !is_minimap {
 2330            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2331                .detach();
 2332            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2333                .detach();
 2334            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2335                .detach();
 2336            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2337                .detach();
 2338            cx.observe_pending_input(window, Self::observe_pending_input)
 2339                .detach();
 2340        }
 2341
 2342        let show_indent_guides =
 2343            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2344                Some(false)
 2345            } else {
 2346                None
 2347            };
 2348
 2349        let breakpoint_store = match (&mode, project.as_ref()) {
 2350            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2351            _ => None,
 2352        };
 2353
 2354        let mut code_action_providers = Vec::new();
 2355        let mut load_uncommitted_diff = None;
 2356        if let Some(project) = project.clone() {
 2357            load_uncommitted_diff = Some(
 2358                update_uncommitted_diff_for_buffer(
 2359                    cx.entity(),
 2360                    &project,
 2361                    multi_buffer.read(cx).all_buffers(),
 2362                    multi_buffer.clone(),
 2363                    cx,
 2364                )
 2365                .shared(),
 2366            );
 2367            code_action_providers.push(Rc::new(project) as Rc<_>);
 2368        }
 2369
 2370        let mut editor = Self {
 2371            focus_handle,
 2372            show_cursor_when_unfocused: false,
 2373            last_focused_descendant: None,
 2374            buffer: multi_buffer.clone(),
 2375            display_map: display_map.clone(),
 2376            placeholder_display_map: None,
 2377            selections,
 2378            scroll_manager: ScrollManager::new(cx),
 2379            columnar_selection_state: None,
 2380            add_selections_state: None,
 2381            select_next_state: None,
 2382            select_prev_state: None,
 2383            selection_history: SelectionHistory::default(),
 2384            defer_selection_effects: false,
 2385            deferred_selection_effects_state: None,
 2386            autoclose_regions: Vec::new(),
 2387            snippet_stack: InvalidationStack::default(),
 2388            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2389            ime_transaction: None,
 2390            active_diagnostics: ActiveDiagnostic::None,
 2391            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2392            inline_diagnostics_update: Task::ready(()),
 2393            inline_diagnostics: Vec::new(),
 2394            soft_wrap_mode_override,
 2395            diagnostics_max_severity,
 2396            hard_wrap: None,
 2397            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2398            semantics_provider: project
 2399                .as_ref()
 2400                .map(|project| Rc::new(project.downgrade()) as _),
 2401            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2402            project,
 2403            blink_manager: blink_manager.clone(),
 2404            show_local_selections: true,
 2405            show_scrollbars: ScrollbarAxes {
 2406                horizontal: full_mode,
 2407                vertical: full_mode,
 2408            },
 2409            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2410            offset_content: !matches!(mode, EditorMode::SingleLine),
 2411            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2412            show_gutter: full_mode,
 2413            show_line_numbers: (!full_mode).then_some(false),
 2414            use_relative_line_numbers: None,
 2415            disable_expand_excerpt_buttons: !full_mode,
 2416            delegate_expand_excerpts: false,
 2417            delegate_stage_and_restore: false,
 2418            delegate_open_excerpts: false,
 2419            enable_lsp_data: true,
 2420            enable_runnables: true,
 2421            show_git_diff_gutter: None,
 2422            show_code_actions: None,
 2423            show_runnables: None,
 2424            show_breakpoints: None,
 2425            show_diff_review_button: false,
 2426            show_wrap_guides: None,
 2427            show_indent_guides,
 2428            buffers_with_disabled_indent_guides: HashSet::default(),
 2429            highlight_order: 0,
 2430            highlighted_rows: HashMap::default(),
 2431            background_highlights: HashMap::default(),
 2432            gutter_highlights: HashMap::default(),
 2433            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2434            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2435            nav_history: None,
 2436            context_menu: RefCell::new(None),
 2437            context_menu_options: None,
 2438            mouse_context_menu: None,
 2439            completion_tasks: Vec::new(),
 2440            inline_blame_popover: None,
 2441            inline_blame_popover_show_task: None,
 2442            signature_help_state: SignatureHelpState::default(),
 2443            auto_signature_help: None,
 2444            find_all_references_task_sources: Vec::new(),
 2445            next_completion_id: 0,
 2446            next_inlay_id: 0,
 2447            code_action_providers,
 2448            available_code_actions: None,
 2449            code_actions_task: None,
 2450            quick_selection_highlight_task: None,
 2451            debounced_selection_highlight_task: None,
 2452            debounced_selection_highlight_complete: false,
 2453            document_highlights_task: None,
 2454            linked_editing_range_task: None,
 2455            pending_rename: None,
 2456            searchable: !is_minimap,
 2457            cursor_shape: EditorSettings::get_global(cx)
 2458                .cursor_shape
 2459                .unwrap_or_default(),
 2460            cursor_offset_on_selection: false,
 2461            current_line_highlight: None,
 2462            autoindent_mode: Some(AutoindentMode::EachLine),
 2463            collapse_matches: false,
 2464            workspace: None,
 2465            input_enabled: !is_minimap,
 2466            expects_character_input: !is_minimap,
 2467            use_modal_editing: full_mode,
 2468            read_only: is_minimap,
 2469            use_autoclose: true,
 2470            use_auto_surround: true,
 2471            auto_replace_emoji_shortcode: false,
 2472            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2473            leader_id: None,
 2474            remote_id: None,
 2475            hover_state: HoverState::default(),
 2476            pending_mouse_down: None,
 2477            prev_pressure_stage: None,
 2478            hovered_link_state: None,
 2479            edit_prediction_provider: None,
 2480            active_edit_prediction: None,
 2481            stale_edit_prediction_in_menu: None,
 2482            edit_prediction_preview: EditPredictionPreview::Inactive {
 2483                released_too_fast: false,
 2484            },
 2485            inline_diagnostics_enabled: full_mode,
 2486            diagnostics_enabled: full_mode,
 2487            word_completions_enabled: full_mode,
 2488            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2489            gutter_hovered: false,
 2490            pixel_position_of_newest_cursor: None,
 2491            last_bounds: None,
 2492            last_position_map: None,
 2493            expect_bounds_change: None,
 2494            gutter_dimensions: GutterDimensions::default(),
 2495            style: None,
 2496            show_cursor_names: false,
 2497            hovered_cursors: HashMap::default(),
 2498            next_editor_action_id: EditorActionId::default(),
 2499            editor_actions: Rc::default(),
 2500            edit_predictions_hidden_for_vim_mode: false,
 2501            show_edit_predictions_override: None,
 2502            show_completions_on_input_override: None,
 2503            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2504            edit_prediction_settings: EditPredictionSettings::Disabled,
 2505            in_leading_whitespace: false,
 2506            custom_context_menu: None,
 2507            show_git_blame_gutter: false,
 2508            show_git_blame_inline: false,
 2509            show_selection_menu: None,
 2510            show_git_blame_inline_delay_task: None,
 2511            git_blame_inline_enabled: full_mode
 2512                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2513            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2514            buffer_serialization: is_minimap.not().then(|| {
 2515                BufferSerialization::new(
 2516                    ProjectSettings::get_global(cx)
 2517                        .session
 2518                        .restore_unsaved_buffers,
 2519                )
 2520            }),
 2521            blame: None,
 2522            blame_subscription: None,
 2523
 2524            breakpoint_store,
 2525            gutter_breakpoint_indicator: (None, None),
 2526            gutter_diff_review_indicator: (None, None),
 2527            diff_review_drag_state: None,
 2528            diff_review_overlays: Vec::new(),
 2529            stored_review_comments: Vec::new(),
 2530            next_review_comment_id: 0,
 2531            hovered_diff_hunk_row: None,
 2532            _subscriptions: (!is_minimap)
 2533                .then(|| {
 2534                    vec![
 2535                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2536                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2537                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2538                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2539                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2540                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2541                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2542                        cx.observe_window_activation(window, |editor, window, cx| {
 2543                            let active = window.is_window_active();
 2544                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2545                                if active {
 2546                                    blink_manager.enable(cx);
 2547                                } else {
 2548                                    blink_manager.disable(cx);
 2549                                }
 2550                            });
 2551                            if active {
 2552                                editor.show_mouse_cursor(cx);
 2553                            }
 2554                        }),
 2555                    ]
 2556                })
 2557                .unwrap_or_default(),
 2558            runnables: RunnableData::new(),
 2559            pull_diagnostics_task: Task::ready(()),
 2560            colors: None,
 2561            refresh_colors_task: Task::ready(()),
 2562            use_document_folding_ranges: false,
 2563            refresh_folding_ranges_task: Task::ready(()),
 2564            inlay_hints: None,
 2565            next_color_inlay_id: 0,
 2566            post_scroll_update: Task::ready(()),
 2567            linked_edit_ranges: Default::default(),
 2568            in_project_search: false,
 2569            previous_search_ranges: None,
 2570            breadcrumb_header: None,
 2571            focused_block: None,
 2572            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2573            addons: HashMap::default(),
 2574            registered_buffers: HashMap::default(),
 2575            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2576            selection_mark_mode: false,
 2577            toggle_fold_multiple_buffers: Task::ready(()),
 2578            serialize_selections: Task::ready(()),
 2579            serialize_folds: Task::ready(()),
 2580            text_style_refinement: None,
 2581            load_diff_task: load_uncommitted_diff,
 2582            temporary_diff_override: false,
 2583            mouse_cursor_hidden: false,
 2584            minimap: None,
 2585            hide_mouse_mode: EditorSettings::get_global(cx)
 2586                .hide_mouse
 2587                .unwrap_or_default(),
 2588            change_list: ChangeList::new(),
 2589            mode,
 2590            selection_drag_state: SelectionDragState::None,
 2591            folding_newlines: Task::ready(()),
 2592            lookup_key: None,
 2593            select_next_is_case_sensitive: None,
 2594            on_local_selections_changed: None,
 2595            suppress_selection_callback: false,
 2596            applicable_language_settings: HashMap::default(),
 2597            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2598            accent_data: None,
 2599            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2600            number_deleted_lines: false,
 2601            refresh_matching_bracket_highlights_task: Task::ready(()),
 2602            refresh_document_symbols_task: Task::ready(()).shared(),
 2603            lsp_document_symbols: HashMap::default(),
 2604            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2605            outline_symbols_at_cursor: None,
 2606            sticky_headers_task: Task::ready(()),
 2607            sticky_headers: None,
 2608            colorize_brackets_task: Task::ready(()),
 2609        };
 2610
 2611        if is_minimap {
 2612            return editor;
 2613        }
 2614
 2615        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2616        editor.accent_data = editor.fetch_accent_data(cx);
 2617
 2618        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2619            editor
 2620                ._subscriptions
 2621                .push(cx.observe(breakpoints, |_, _, cx| {
 2622                    cx.notify();
 2623                }));
 2624        }
 2625        editor._subscriptions.extend(project_subscriptions);
 2626
 2627        editor._subscriptions.push(cx.subscribe_in(
 2628            &cx.entity(),
 2629            window,
 2630            |editor, _, e: &EditorEvent, window, cx| match e {
 2631                EditorEvent::ScrollPositionChanged { local, .. } => {
 2632                    if *local {
 2633                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2634                        editor.inline_blame_popover.take();
 2635                        let snapshot = editor.snapshot(window, cx);
 2636                        let new_anchor = editor
 2637                            .scroll_manager
 2638                            .native_anchor(&snapshot.display_snapshot, cx);
 2639                        editor.update_restoration_data(cx, move |data| {
 2640                            data.scroll_position = (
 2641                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2642                                new_anchor.offset,
 2643                            );
 2644                        });
 2645
 2646                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2647                            cx.background_executor()
 2648                                .timer(Duration::from_millis(50))
 2649                                .await;
 2650                            editor
 2651                                .update_in(cx, |editor, window, cx| {
 2652                                    editor.update_data_on_scroll(window, cx)
 2653                                })
 2654                                .ok();
 2655                        });
 2656                    }
 2657                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2658                }
 2659                EditorEvent::Edited { .. } => {
 2660                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2661                        .map(|vim_mode| vim_mode.0)
 2662                        .unwrap_or(false);
 2663                    if !vim_mode {
 2664                        let display_map = editor.display_snapshot(cx);
 2665                        let selections = editor.selections.all_adjusted_display(&display_map);
 2666                        let pop_state = editor
 2667                            .change_list
 2668                            .last()
 2669                            .map(|previous| {
 2670                                previous.len() == selections.len()
 2671                                    && previous.iter().enumerate().all(|(ix, p)| {
 2672                                        p.to_display_point(&display_map).row()
 2673                                            == selections[ix].head().row()
 2674                                    })
 2675                            })
 2676                            .unwrap_or(false);
 2677                        let new_positions = selections
 2678                            .into_iter()
 2679                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2680                            .collect();
 2681                        editor
 2682                            .change_list
 2683                            .push_to_change_list(pop_state, new_positions);
 2684                    }
 2685                }
 2686                _ => (),
 2687            },
 2688        ));
 2689
 2690        if let Some(dap_store) = editor
 2691            .project
 2692            .as_ref()
 2693            .map(|project| project.read(cx).dap_store())
 2694        {
 2695            let weak_editor = cx.weak_entity();
 2696
 2697            editor
 2698                ._subscriptions
 2699                .push(
 2700                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2701                        let session_entity = cx.entity();
 2702                        weak_editor
 2703                            .update(cx, |editor, cx| {
 2704                                editor._subscriptions.push(
 2705                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2706                                );
 2707                            })
 2708                            .ok();
 2709                    }),
 2710                );
 2711
 2712            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2713                editor
 2714                    ._subscriptions
 2715                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2716            }
 2717        }
 2718
 2719        // skip adding the initial selection to selection history
 2720        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2721        editor.end_selection(window, cx);
 2722        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2723
 2724        editor.scroll_manager.show_scrollbars(window, cx);
 2725        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2726
 2727        if full_mode {
 2728            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2729            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2730
 2731            if editor.git_blame_inline_enabled {
 2732                editor.start_git_blame_inline(false, window, cx);
 2733            }
 2734
 2735            editor.go_to_active_debug_line(window, cx);
 2736
 2737            editor.minimap =
 2738                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2739            editor.colors = Some(LspColorData::new(cx));
 2740            editor.use_document_folding_ranges = true;
 2741            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2742
 2743            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2744                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2745            }
 2746            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2747        }
 2748
 2749        editor
 2750    }
 2751
 2752    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2753        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2754    }
 2755
 2756    pub fn deploy_mouse_context_menu(
 2757        &mut self,
 2758        position: gpui::Point<Pixels>,
 2759        context_menu: Entity<ContextMenu>,
 2760        window: &mut Window,
 2761        cx: &mut Context<Self>,
 2762    ) {
 2763        self.mouse_context_menu = Some(MouseContextMenu::new(
 2764            self,
 2765            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2766            context_menu,
 2767            window,
 2768            cx,
 2769        ));
 2770    }
 2771
 2772    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2773        self.mouse_context_menu
 2774            .as_ref()
 2775            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2776    }
 2777
 2778    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2779        if self
 2780            .selections
 2781            .pending_anchor()
 2782            .is_some_and(|pending_selection| {
 2783                let snapshot = self.buffer().read(cx).snapshot(cx);
 2784                pending_selection.range().includes(range, &snapshot)
 2785            })
 2786        {
 2787            return true;
 2788        }
 2789
 2790        self.selections
 2791            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2792            .into_iter()
 2793            .any(|selection| {
 2794                // This is needed to cover a corner case, if we just check for an existing
 2795                // selection in the fold range, having a cursor at the start of the fold
 2796                // marks it as selected. Non-empty selections don't cause this.
 2797                let length = selection.end - selection.start;
 2798                length > 0
 2799            })
 2800    }
 2801
 2802    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2803        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2804    }
 2805
 2806    fn key_context_internal(
 2807        &self,
 2808        has_active_edit_prediction: bool,
 2809        window: &mut Window,
 2810        cx: &mut App,
 2811    ) -> KeyContext {
 2812        let mut key_context = KeyContext::new_with_defaults();
 2813        key_context.add("Editor");
 2814        let mode = match self.mode {
 2815            EditorMode::SingleLine => "single_line",
 2816            EditorMode::AutoHeight { .. } => "auto_height",
 2817            EditorMode::Minimap { .. } => "minimap",
 2818            EditorMode::Full { .. } => "full",
 2819        };
 2820
 2821        if EditorSettings::jupyter_enabled(cx) {
 2822            key_context.add("jupyter");
 2823        }
 2824
 2825        key_context.set("mode", mode);
 2826        if self.pending_rename.is_some() {
 2827            key_context.add("renaming");
 2828        }
 2829
 2830        if let Some(snippet_stack) = self.snippet_stack.last() {
 2831            key_context.add("in_snippet");
 2832
 2833            if snippet_stack.active_index > 0 {
 2834                key_context.add("has_previous_tabstop");
 2835            }
 2836
 2837            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2838                key_context.add("has_next_tabstop");
 2839            }
 2840        }
 2841
 2842        match self.context_menu.borrow().as_ref() {
 2843            Some(CodeContextMenu::Completions(menu)) => {
 2844                if menu.visible() {
 2845                    key_context.add("menu");
 2846                    key_context.add("showing_completions");
 2847                }
 2848            }
 2849            Some(CodeContextMenu::CodeActions(menu)) => {
 2850                if menu.visible() {
 2851                    key_context.add("menu");
 2852                    key_context.add("showing_code_actions")
 2853                }
 2854            }
 2855            None => {}
 2856        }
 2857
 2858        if self.signature_help_state.has_multiple_signatures() {
 2859            key_context.add("showing_signature_help");
 2860        }
 2861
 2862        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2863        if !self.focus_handle(cx).contains_focused(window, cx)
 2864            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2865        {
 2866            for addon in self.addons.values() {
 2867                addon.extend_key_context(&mut key_context, cx)
 2868            }
 2869        }
 2870
 2871        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2872            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2873                Some(
 2874                    file.full_path(cx)
 2875                        .extension()?
 2876                        .to_string_lossy()
 2877                        .to_lowercase(),
 2878                )
 2879            }) {
 2880                key_context.set("extension", extension);
 2881            }
 2882        } else {
 2883            key_context.add("multibuffer");
 2884        }
 2885
 2886        if has_active_edit_prediction {
 2887            key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2888            key_context.add("copilot_suggestion");
 2889        }
 2890
 2891        if self.in_leading_whitespace {
 2892            key_context.add("in_leading_whitespace");
 2893        }
 2894        if self.edit_prediction_requires_modifier() {
 2895            key_context.set("edit_prediction_mode", "subtle")
 2896        } else {
 2897            key_context.set("edit_prediction_mode", "eager");
 2898        }
 2899
 2900        if self.selection_mark_mode {
 2901            key_context.add("selection_mode");
 2902        }
 2903
 2904        let disjoint = self.selections.disjoint_anchors();
 2905        if matches!(
 2906            &self.mode,
 2907            EditorMode::SingleLine | EditorMode::AutoHeight { .. }
 2908        ) && let [selection] = disjoint
 2909            && selection.start == selection.end
 2910        {
 2911            let snapshot = self.snapshot(window, cx);
 2912            let snapshot = snapshot.buffer_snapshot();
 2913            let caret_offset = selection.end.to_offset(snapshot);
 2914
 2915            if caret_offset == MultiBufferOffset(0) {
 2916                key_context.add("start_of_input");
 2917            }
 2918
 2919            if caret_offset == snapshot.len() {
 2920                key_context.add("end_of_input");
 2921            }
 2922        }
 2923
 2924        if self.has_any_expanded_diff_hunks(cx) {
 2925            key_context.add("diffs_expanded");
 2926        }
 2927
 2928        key_context
 2929    }
 2930
 2931    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2932        self.last_bounds.as_ref()
 2933    }
 2934
 2935    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2936        if self.mouse_cursor_hidden {
 2937            self.mouse_cursor_hidden = false;
 2938            cx.notify();
 2939        }
 2940    }
 2941
 2942    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2943        let hide_mouse_cursor = match origin {
 2944            HideMouseCursorOrigin::TypingAction => {
 2945                matches!(
 2946                    self.hide_mouse_mode,
 2947                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2948                )
 2949            }
 2950            HideMouseCursorOrigin::MovementAction => {
 2951                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2952            }
 2953        };
 2954        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2955            self.mouse_cursor_hidden = hide_mouse_cursor;
 2956            cx.notify();
 2957        }
 2958    }
 2959
 2960    fn accept_edit_prediction_keystroke(
 2961        &self,
 2962        granularity: EditPredictionGranularity,
 2963        window: &mut Window,
 2964        cx: &mut App,
 2965    ) -> Option<gpui::KeybindingKeystroke> {
 2966        let key_context = self.key_context_internal(true, window, cx);
 2967
 2968        let bindings =
 2969            match granularity {
 2970                EditPredictionGranularity::Word => window
 2971                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2972                EditPredictionGranularity::Line => window
 2973                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2974                EditPredictionGranularity::Full => {
 2975                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2976                }
 2977            };
 2978
 2979        bindings
 2980            .into_iter()
 2981            .rev()
 2982            .find_map(|binding| match binding.keystrokes() {
 2983                [keystroke, ..] => Some(keystroke.clone()),
 2984                _ => None,
 2985            })
 2986    }
 2987
 2988    fn preview_edit_prediction_keystroke(
 2989        &self,
 2990        window: &mut Window,
 2991        cx: &mut App,
 2992    ) -> Option<gpui::KeybindingKeystroke> {
 2993        let key_context = self.key_context_internal(true, window, cx);
 2994        let bindings = window.bindings_for_action_in_context(&AcceptEditPrediction, key_context);
 2995        bindings
 2996            .into_iter()
 2997            .rev()
 2998            .find_map(|binding| match binding.keystrokes() {
 2999                [keystroke, ..] if keystroke.modifiers().modified() => Some(keystroke.clone()),
 3000                _ => None,
 3001            })
 3002    }
 3003
 3004    fn edit_prediction_preview_modifiers_held(
 3005        &self,
 3006        modifiers: &Modifiers,
 3007        window: &mut Window,
 3008        cx: &mut App,
 3009    ) -> bool {
 3010        let key_context = self.key_context_internal(true, window, cx);
 3011        let actions: [&dyn Action; 3] = [
 3012            &AcceptEditPrediction,
 3013            &AcceptNextWordEditPrediction,
 3014            &AcceptNextLineEditPrediction,
 3015        ];
 3016
 3017        actions.into_iter().any(|action| {
 3018            window
 3019                .bindings_for_action_in_context(action, key_context.clone())
 3020                .into_iter()
 3021                .rev()
 3022                .any(|binding| {
 3023                    binding.keystrokes().first().is_some_and(|keystroke| {
 3024                        keystroke.modifiers().modified() && keystroke.modifiers() == modifiers
 3025                    })
 3026                })
 3027        })
 3028    }
 3029
 3030    fn edit_prediction_cursor_popover_prefers_preview(
 3031        &self,
 3032        completion: &EditPredictionState,
 3033    ) -> bool {
 3034        match &completion.completion {
 3035            EditPrediction::Edit {
 3036                edits, snapshot, ..
 3037            } => {
 3038                let mut start_row: Option<u32> = None;
 3039                let mut end_row: Option<u32> = None;
 3040
 3041                for (range, text) in edits {
 3042                    let edit_start_row = range.start.text_anchor.to_point(snapshot).row;
 3043                    let old_end_row = range.end.text_anchor.to_point(snapshot).row;
 3044                    let inserted_newline_count = text
 3045                        .as_ref()
 3046                        .chars()
 3047                        .filter(|character| *character == '\n')
 3048                        .count() as u32;
 3049                    let deleted_newline_count = old_end_row - edit_start_row;
 3050                    let preview_end_row = edit_start_row + inserted_newline_count;
 3051
 3052                    start_row =
 3053                        Some(start_row.map_or(edit_start_row, |row| row.min(edit_start_row)));
 3054                    end_row = Some(end_row.map_or(preview_end_row, |row| row.max(preview_end_row)));
 3055
 3056                    if deleted_newline_count > 1 {
 3057                        end_row = Some(end_row.map_or(old_end_row, |row| row.max(old_end_row)));
 3058                    }
 3059                }
 3060
 3061                start_row
 3062                    .zip(end_row)
 3063                    .is_some_and(|(start_row, end_row)| end_row > start_row)
 3064            }
 3065            EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => false,
 3066        }
 3067    }
 3068
 3069    fn edit_prediction_keybind_display(
 3070        &self,
 3071        surface: EditPredictionKeybindSurface,
 3072        window: &mut Window,
 3073        cx: &mut App,
 3074    ) -> EditPredictionKeybindDisplay {
 3075        let accept_keystroke =
 3076            self.accept_edit_prediction_keystroke(EditPredictionGranularity::Full, window, cx);
 3077        let preview_keystroke = self.preview_edit_prediction_keystroke(window, cx);
 3078
 3079        let action = match surface {
 3080            EditPredictionKeybindSurface::Inline
 3081            | EditPredictionKeybindSurface::CursorPopoverCompact => {
 3082                if self.edit_prediction_requires_modifier() {
 3083                    EditPredictionKeybindAction::Preview
 3084                } else {
 3085                    EditPredictionKeybindAction::Accept
 3086                }
 3087            }
 3088            EditPredictionKeybindSurface::CursorPopoverExpanded => self
 3089                .active_edit_prediction
 3090                .as_ref()
 3091                .filter(|completion| {
 3092                    self.edit_prediction_cursor_popover_prefers_preview(completion)
 3093                })
 3094                .map_or(EditPredictionKeybindAction::Accept, |_| {
 3095                    EditPredictionKeybindAction::Preview
 3096                }),
 3097        };
 3098        #[cfg(test)]
 3099        let preview_copy = preview_keystroke.clone();
 3100        #[cfg(test)]
 3101        let accept_copy = accept_keystroke.clone();
 3102
 3103        let displayed_keystroke = match surface {
 3104            EditPredictionKeybindSurface::Inline => match action {
 3105                EditPredictionKeybindAction::Accept => accept_keystroke,
 3106                EditPredictionKeybindAction::Preview => preview_keystroke,
 3107            },
 3108            EditPredictionKeybindSurface::CursorPopoverCompact
 3109            | EditPredictionKeybindSurface::CursorPopoverExpanded => match action {
 3110                EditPredictionKeybindAction::Accept => accept_keystroke,
 3111                EditPredictionKeybindAction::Preview => {
 3112                    preview_keystroke.or_else(|| accept_keystroke.clone())
 3113                }
 3114            },
 3115        };
 3116
 3117        let missing_accept_keystroke = displayed_keystroke.is_none();
 3118
 3119        EditPredictionKeybindDisplay {
 3120            #[cfg(test)]
 3121            accept_keystroke: accept_copy,
 3122            #[cfg(test)]
 3123            preview_keystroke: preview_copy,
 3124            displayed_keystroke,
 3125            action,
 3126            missing_accept_keystroke,
 3127            show_hold_label: matches!(surface, EditPredictionKeybindSurface::CursorPopoverCompact)
 3128                && self.edit_prediction_preview.released_too_fast(),
 3129        }
 3130    }
 3131
 3132    pub fn new_file(
 3133        workspace: &mut Workspace,
 3134        _: &workspace::NewFile,
 3135        window: &mut Window,
 3136        cx: &mut Context<Workspace>,
 3137    ) {
 3138        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3139            "Failed to create buffer",
 3140            window,
 3141            cx,
 3142            |e, _, _| match e.error_code() {
 3143                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3144                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3145                e.error_tag("required").unwrap_or("the latest version")
 3146            )),
 3147                _ => None,
 3148            },
 3149        );
 3150    }
 3151
 3152    pub fn new_in_workspace(
 3153        workspace: &mut Workspace,
 3154        window: &mut Window,
 3155        cx: &mut Context<Workspace>,
 3156    ) -> Task<Result<Entity<Editor>>> {
 3157        let project = workspace.project().clone();
 3158        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3159
 3160        cx.spawn_in(window, async move |workspace, cx| {
 3161            let buffer = create.await?;
 3162            workspace.update_in(cx, |workspace, window, cx| {
 3163                let editor =
 3164                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3165                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3166                editor
 3167            })
 3168        })
 3169    }
 3170
 3171    fn new_file_vertical(
 3172        workspace: &mut Workspace,
 3173        _: &workspace::NewFileSplitVertical,
 3174        window: &mut Window,
 3175        cx: &mut Context<Workspace>,
 3176    ) {
 3177        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3178    }
 3179
 3180    fn new_file_horizontal(
 3181        workspace: &mut Workspace,
 3182        _: &workspace::NewFileSplitHorizontal,
 3183        window: &mut Window,
 3184        cx: &mut Context<Workspace>,
 3185    ) {
 3186        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3187    }
 3188
 3189    fn new_file_split(
 3190        workspace: &mut Workspace,
 3191        action: &workspace::NewFileSplit,
 3192        window: &mut Window,
 3193        cx: &mut Context<Workspace>,
 3194    ) {
 3195        Self::new_file_in_direction(workspace, action.0, window, cx)
 3196    }
 3197
 3198    fn new_file_in_direction(
 3199        workspace: &mut Workspace,
 3200        direction: SplitDirection,
 3201        window: &mut Window,
 3202        cx: &mut Context<Workspace>,
 3203    ) {
 3204        let project = workspace.project().clone();
 3205        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3206
 3207        cx.spawn_in(window, async move |workspace, cx| {
 3208            let buffer = create.await?;
 3209            workspace.update_in(cx, move |workspace, window, cx| {
 3210                workspace.split_item(
 3211                    direction,
 3212                    Box::new(
 3213                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3214                    ),
 3215                    window,
 3216                    cx,
 3217                )
 3218            })?;
 3219            anyhow::Ok(())
 3220        })
 3221        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3222            match e.error_code() {
 3223                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3224                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3225                e.error_tag("required").unwrap_or("the latest version")
 3226            )),
 3227                _ => None,
 3228            }
 3229        });
 3230    }
 3231
 3232    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3233        self.leader_id
 3234    }
 3235
 3236    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3237        &self.buffer
 3238    }
 3239
 3240    pub fn project(&self) -> Option<&Entity<Project>> {
 3241        self.project.as_ref()
 3242    }
 3243
 3244    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3245        self.workspace.as_ref()?.0.upgrade()
 3246    }
 3247
 3248    /// Detaches a task and shows an error notification in the workspace if available,
 3249    /// otherwise just logs the error.
 3250    pub fn detach_and_notify_err<R, E>(
 3251        &self,
 3252        task: Task<Result<R, E>>,
 3253        window: &mut Window,
 3254        cx: &mut App,
 3255    ) where
 3256        E: std::fmt::Debug + std::fmt::Display + 'static,
 3257        R: 'static,
 3258    {
 3259        if let Some(workspace) = self.workspace() {
 3260            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3261        } else {
 3262            task.detach_and_log_err(cx);
 3263        }
 3264    }
 3265
 3266    /// Returns the workspace serialization ID if this editor should be serialized.
 3267    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3268        self.workspace
 3269            .as_ref()
 3270            .filter(|_| self.should_serialize_buffer())
 3271            .and_then(|workspace| workspace.1)
 3272    }
 3273
 3274    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3275        self.buffer().read(cx).title(cx)
 3276    }
 3277
 3278    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3279        let git_blame_gutter_max_author_length = self
 3280            .render_git_blame_gutter(cx)
 3281            .then(|| {
 3282                if let Some(blame) = self.blame.as_ref() {
 3283                    let max_author_length =
 3284                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3285                    Some(max_author_length)
 3286                } else {
 3287                    None
 3288                }
 3289            })
 3290            .flatten();
 3291
 3292        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3293
 3294        EditorSnapshot {
 3295            mode: self.mode.clone(),
 3296            show_gutter: self.show_gutter,
 3297            offset_content: self.offset_content,
 3298            show_line_numbers: self.show_line_numbers,
 3299            number_deleted_lines: self.number_deleted_lines,
 3300            show_git_diff_gutter: self.show_git_diff_gutter,
 3301            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3302            show_code_actions: self.show_code_actions,
 3303            show_runnables: self.show_runnables,
 3304            show_breakpoints: self.show_breakpoints,
 3305            git_blame_gutter_max_author_length,
 3306            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3307            display_snapshot,
 3308            placeholder_display_snapshot: self
 3309                .placeholder_display_map
 3310                .as_ref()
 3311                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3312            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3313            is_focused: self.focus_handle.is_focused(window),
 3314            current_line_highlight: self
 3315                .current_line_highlight
 3316                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3317            gutter_hovered: self.gutter_hovered,
 3318        }
 3319    }
 3320
 3321    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3322        self.buffer.read(cx).language_at(point, cx)
 3323    }
 3324
 3325    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3326        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3327    }
 3328
 3329    pub fn active_excerpt(
 3330        &self,
 3331        cx: &App,
 3332    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3333        self.buffer
 3334            .read(cx)
 3335            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3336    }
 3337
 3338    pub fn mode(&self) -> &EditorMode {
 3339        &self.mode
 3340    }
 3341
 3342    pub fn set_mode(&mut self, mode: EditorMode) {
 3343        self.mode = mode;
 3344    }
 3345
 3346    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3347        self.collaboration_hub.as_deref()
 3348    }
 3349
 3350    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3351        self.collaboration_hub = Some(hub);
 3352    }
 3353
 3354    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3355        self.in_project_search = in_project_search;
 3356    }
 3357
 3358    pub fn set_custom_context_menu(
 3359        &mut self,
 3360        f: impl 'static
 3361        + Fn(
 3362            &mut Self,
 3363            DisplayPoint,
 3364            &mut Window,
 3365            &mut Context<Self>,
 3366        ) -> Option<Entity<ui::ContextMenu>>,
 3367    ) {
 3368        self.custom_context_menu = Some(Box::new(f))
 3369    }
 3370
 3371    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3372        self.completion_provider = provider;
 3373    }
 3374
 3375    #[cfg(any(test, feature = "test-support"))]
 3376    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3377        self.completion_provider.clone()
 3378    }
 3379
 3380    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3381        self.semantics_provider.clone()
 3382    }
 3383
 3384    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3385        self.semantics_provider = provider;
 3386    }
 3387
 3388    pub fn set_edit_prediction_provider<T>(
 3389        &mut self,
 3390        provider: Option<Entity<T>>,
 3391        window: &mut Window,
 3392        cx: &mut Context<Self>,
 3393    ) where
 3394        T: EditPredictionDelegate,
 3395    {
 3396        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3397            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3398                if this.focus_handle.is_focused(window) {
 3399                    this.update_visible_edit_prediction(window, cx);
 3400                }
 3401            }),
 3402            provider: Arc::new(provider),
 3403        });
 3404        self.update_edit_prediction_settings(cx);
 3405        self.refresh_edit_prediction(false, false, window, cx);
 3406    }
 3407
 3408    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3409        self.placeholder_display_map
 3410            .as_ref()
 3411            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3412    }
 3413
 3414    pub fn set_placeholder_text(
 3415        &mut self,
 3416        placeholder_text: &str,
 3417        window: &mut Window,
 3418        cx: &mut Context<Self>,
 3419    ) {
 3420        let multibuffer = cx
 3421            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3422
 3423        let style = window.text_style();
 3424
 3425        self.placeholder_display_map = Some(cx.new(|cx| {
 3426            DisplayMap::new(
 3427                multibuffer,
 3428                style.font(),
 3429                style.font_size.to_pixels(window.rem_size()),
 3430                None,
 3431                FILE_HEADER_HEIGHT,
 3432                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3433                Default::default(),
 3434                DiagnosticSeverity::Off,
 3435                cx,
 3436            )
 3437        }));
 3438        cx.notify();
 3439    }
 3440
 3441    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3442        self.cursor_shape = cursor_shape;
 3443
 3444        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3445        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3446
 3447        cx.notify();
 3448    }
 3449
 3450    pub fn cursor_shape(&self) -> CursorShape {
 3451        self.cursor_shape
 3452    }
 3453
 3454    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3455        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3456    }
 3457
 3458    pub fn set_current_line_highlight(
 3459        &mut self,
 3460        current_line_highlight: Option<CurrentLineHighlight>,
 3461    ) {
 3462        self.current_line_highlight = current_line_highlight;
 3463    }
 3464
 3465    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3466        self.collapse_matches = collapse_matches;
 3467    }
 3468
 3469    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3470        if self.collapse_matches {
 3471            return range.start..range.start;
 3472        }
 3473        range.clone()
 3474    }
 3475
 3476    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3477        self.display_map.read(cx).clip_at_line_ends
 3478    }
 3479
 3480    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3481        if self.display_map.read(cx).clip_at_line_ends != clip {
 3482            self.display_map
 3483                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3484        }
 3485    }
 3486
 3487    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3488        self.input_enabled = input_enabled;
 3489    }
 3490
 3491    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3492        self.expects_character_input = expects_character_input;
 3493    }
 3494
 3495    pub fn set_edit_predictions_hidden_for_vim_mode(
 3496        &mut self,
 3497        hidden: bool,
 3498        window: &mut Window,
 3499        cx: &mut Context<Self>,
 3500    ) {
 3501        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3502            self.edit_predictions_hidden_for_vim_mode = hidden;
 3503            if hidden {
 3504                self.update_visible_edit_prediction(window, cx);
 3505            } else {
 3506                self.refresh_edit_prediction(true, false, window, cx);
 3507            }
 3508        }
 3509    }
 3510
 3511    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3512        self.menu_edit_predictions_policy = value;
 3513    }
 3514
 3515    pub fn set_autoindent(&mut self, autoindent: bool) {
 3516        if autoindent {
 3517            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3518        } else {
 3519            self.autoindent_mode = None;
 3520        }
 3521    }
 3522
 3523    pub fn capability(&self, cx: &App) -> Capability {
 3524        if self.read_only {
 3525            Capability::ReadOnly
 3526        } else {
 3527            self.buffer.read(cx).capability()
 3528        }
 3529    }
 3530
 3531    pub fn read_only(&self, cx: &App) -> bool {
 3532        self.read_only || self.buffer.read(cx).read_only()
 3533    }
 3534
 3535    pub fn set_read_only(&mut self, read_only: bool) {
 3536        self.read_only = read_only;
 3537    }
 3538
 3539    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3540        self.use_autoclose = autoclose;
 3541    }
 3542
 3543    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3544        self.use_auto_surround = auto_surround;
 3545    }
 3546
 3547    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3548        self.auto_replace_emoji_shortcode = auto_replace;
 3549    }
 3550
 3551    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3552        self.buffer_serialization = should_serialize.then(|| {
 3553            BufferSerialization::new(
 3554                ProjectSettings::get_global(cx)
 3555                    .session
 3556                    .restore_unsaved_buffers,
 3557            )
 3558        })
 3559    }
 3560
 3561    fn should_serialize_buffer(&self) -> bool {
 3562        self.buffer_serialization.is_some()
 3563    }
 3564
 3565    pub fn toggle_edit_predictions(
 3566        &mut self,
 3567        _: &ToggleEditPrediction,
 3568        window: &mut Window,
 3569        cx: &mut Context<Self>,
 3570    ) {
 3571        if self.show_edit_predictions_override.is_some() {
 3572            self.set_show_edit_predictions(None, window, cx);
 3573        } else {
 3574            let show_edit_predictions = !self.edit_predictions_enabled();
 3575            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3576        }
 3577    }
 3578
 3579    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3580        self.show_completions_on_input_override = show_completions_on_input;
 3581    }
 3582
 3583    pub fn set_show_edit_predictions(
 3584        &mut self,
 3585        show_edit_predictions: Option<bool>,
 3586        window: &mut Window,
 3587        cx: &mut Context<Self>,
 3588    ) {
 3589        self.show_edit_predictions_override = show_edit_predictions;
 3590        self.update_edit_prediction_settings(cx);
 3591
 3592        if let Some(false) = show_edit_predictions {
 3593            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3594        } else {
 3595            self.refresh_edit_prediction(false, true, window, cx);
 3596        }
 3597    }
 3598
 3599    fn edit_predictions_disabled_in_scope(
 3600        &self,
 3601        buffer: &Entity<Buffer>,
 3602        buffer_position: language::Anchor,
 3603        cx: &App,
 3604    ) -> bool {
 3605        let snapshot = buffer.read(cx).snapshot();
 3606        let settings = snapshot.settings_at(buffer_position, cx);
 3607
 3608        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3609            return false;
 3610        };
 3611
 3612        scope.override_name().is_some_and(|scope_name| {
 3613            settings
 3614                .edit_predictions_disabled_in
 3615                .iter()
 3616                .any(|s| s == scope_name)
 3617        })
 3618    }
 3619
 3620    pub fn set_use_modal_editing(&mut self, to: bool) {
 3621        self.use_modal_editing = to;
 3622    }
 3623
 3624    pub fn use_modal_editing(&self) -> bool {
 3625        self.use_modal_editing
 3626    }
 3627
 3628    fn selections_did_change(
 3629        &mut self,
 3630        local: bool,
 3631        old_cursor_position: &Anchor,
 3632        effects: SelectionEffects,
 3633        window: &mut Window,
 3634        cx: &mut Context<Self>,
 3635    ) {
 3636        window.invalidate_character_coordinates();
 3637
 3638        // Copy selections to primary selection buffer
 3639        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3640        if local {
 3641            let selections = self
 3642                .selections
 3643                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3644            let buffer_handle = self.buffer.read(cx).read(cx);
 3645
 3646            let mut text = String::new();
 3647            for (index, selection) in selections.iter().enumerate() {
 3648                let text_for_selection = buffer_handle
 3649                    .text_for_range(selection.start..selection.end)
 3650                    .collect::<String>();
 3651
 3652                text.push_str(&text_for_selection);
 3653                if index != selections.len() - 1 {
 3654                    text.push('\n');
 3655                }
 3656            }
 3657
 3658            if !text.is_empty() {
 3659                cx.write_to_primary(ClipboardItem::new_string(text));
 3660            }
 3661        }
 3662
 3663        let selection_anchors = self.selections.disjoint_anchors_arc();
 3664
 3665        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3666            self.buffer.update(cx, |buffer, cx| {
 3667                buffer.set_active_selections(
 3668                    &selection_anchors,
 3669                    self.selections.line_mode(),
 3670                    self.cursor_shape,
 3671                    cx,
 3672                )
 3673            });
 3674        }
 3675        let display_map = self
 3676            .display_map
 3677            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3678        let buffer = display_map.buffer_snapshot();
 3679        if self.selections.count() == 1 {
 3680            self.add_selections_state = None;
 3681        }
 3682        self.select_next_state = None;
 3683        self.select_prev_state = None;
 3684        self.select_syntax_node_history.try_clear();
 3685        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3686        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3687        self.take_rename(false, window, cx);
 3688
 3689        let newest_selection = self.selections.newest_anchor();
 3690        let new_cursor_position = newest_selection.head();
 3691        let selection_start = newest_selection.start;
 3692
 3693        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3694            self.push_to_nav_history(
 3695                *old_cursor_position,
 3696                Some(new_cursor_position.to_point(buffer)),
 3697                false,
 3698                effects.nav_history == Some(true),
 3699                cx,
 3700            );
 3701        }
 3702
 3703        if local {
 3704            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3705                self.register_buffer(buffer_id, cx);
 3706            }
 3707
 3708            let mut context_menu = self.context_menu.borrow_mut();
 3709            let completion_menu = match context_menu.as_ref() {
 3710                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3711                Some(CodeContextMenu::CodeActions(_)) => {
 3712                    *context_menu = None;
 3713                    None
 3714                }
 3715                None => None,
 3716            };
 3717            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3718            drop(context_menu);
 3719
 3720            if effects.completions
 3721                && let Some(completion_position) = completion_position
 3722            {
 3723                let start_offset = selection_start.to_offset(buffer);
 3724                let position_matches = start_offset == completion_position.to_offset(buffer);
 3725                let continue_showing = if let Some((snap, ..)) =
 3726                    buffer.point_to_buffer_offset(completion_position)
 3727                    && !snap.capability.editable()
 3728                {
 3729                    false
 3730                } else if position_matches {
 3731                    if self.snippet_stack.is_empty() {
 3732                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3733                            == Some(CharKind::Word)
 3734                    } else {
 3735                        // Snippet choices can be shown even when the cursor is in whitespace.
 3736                        // Dismissing the menu with actions like backspace is handled by
 3737                        // invalidation regions.
 3738                        true
 3739                    }
 3740                } else {
 3741                    false
 3742                };
 3743
 3744                if continue_showing {
 3745                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3746                } else {
 3747                    self.hide_context_menu(window, cx);
 3748                }
 3749            }
 3750
 3751            hide_hover(self, cx);
 3752
 3753            if old_cursor_position.to_display_point(&display_map).row()
 3754                != new_cursor_position.to_display_point(&display_map).row()
 3755            {
 3756                self.available_code_actions.take();
 3757            }
 3758            self.refresh_code_actions(window, cx);
 3759            self.refresh_document_highlights(cx);
 3760            refresh_linked_ranges(self, window, cx);
 3761
 3762            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3763            self.refresh_matching_bracket_highlights(&display_map, cx);
 3764            self.refresh_outline_symbols_at_cursor(cx);
 3765            self.update_visible_edit_prediction(window, cx);
 3766            self.inline_blame_popover.take();
 3767            if self.git_blame_inline_enabled {
 3768                self.start_inline_blame_timer(window, cx);
 3769            }
 3770        }
 3771
 3772        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3773
 3774        if local && !self.suppress_selection_callback {
 3775            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3776                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3777                callback(cursor_position, window, cx);
 3778            }
 3779        }
 3780
 3781        cx.emit(EditorEvent::SelectionsChanged { local });
 3782
 3783        let selections = &self.selections.disjoint_anchors_arc();
 3784        if selections.len() == 1 {
 3785            cx.emit(SearchEvent::ActiveMatchChanged)
 3786        }
 3787        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3788            let inmemory_selections = selections
 3789                .iter()
 3790                .map(|s| {
 3791                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3792                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3793                })
 3794                .collect();
 3795            self.update_restoration_data(cx, |data| {
 3796                data.selections = inmemory_selections;
 3797            });
 3798
 3799            if WorkspaceSettings::get(None, cx).restore_on_startup
 3800                != RestoreOnStartupBehavior::EmptyTab
 3801                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3802            {
 3803                let snapshot = self.buffer().read(cx).snapshot(cx);
 3804                let selections = selections.clone();
 3805                let background_executor = cx.background_executor().clone();
 3806                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3807                let db = EditorDb::global(cx);
 3808                self.serialize_selections = cx.background_spawn(async move {
 3809                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3810                    let db_selections = selections
 3811                        .iter()
 3812                        .map(|selection| {
 3813                            (
 3814                                selection.start.to_offset(&snapshot).0,
 3815                                selection.end.to_offset(&snapshot).0,
 3816                            )
 3817                        })
 3818                        .collect();
 3819
 3820                    db.save_editor_selections(editor_id, workspace_id, db_selections)
 3821                        .await
 3822                        .with_context(|| {
 3823                            format!(
 3824                                "persisting editor selections for editor {editor_id}, \
 3825                                workspace {workspace_id:?}"
 3826                            )
 3827                        })
 3828                        .log_err();
 3829                });
 3830            }
 3831        }
 3832
 3833        cx.notify();
 3834    }
 3835
 3836    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3837        use text::ToOffset as _;
 3838        use text::ToPoint as _;
 3839
 3840        if self.mode.is_minimap()
 3841            || WorkspaceSettings::get(None, cx).restore_on_startup
 3842                == RestoreOnStartupBehavior::EmptyTab
 3843        {
 3844            return;
 3845        }
 3846
 3847        if !self.buffer().read(cx).is_singleton() {
 3848            return;
 3849        }
 3850
 3851        let display_snapshot = self
 3852            .display_map
 3853            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3854        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3855            return;
 3856        };
 3857        let inmemory_folds = display_snapshot
 3858            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3859            .map(|fold| {
 3860                fold.range.start.text_anchor.to_point(&snapshot)
 3861                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3862            })
 3863            .collect();
 3864        self.update_restoration_data(cx, |data| {
 3865            data.folds = inmemory_folds;
 3866        });
 3867
 3868        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3869            return;
 3870        };
 3871
 3872        // Get file path for path-based fold storage (survives tab close)
 3873        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3874            project::File::from_dyn(buffer.read(cx).file())
 3875                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3876        }) else {
 3877            return;
 3878        };
 3879
 3880        let background_executor = cx.background_executor().clone();
 3881        const FINGERPRINT_LEN: usize = 32;
 3882        let db_folds = display_snapshot
 3883            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3884            .map(|fold| {
 3885                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3886                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3887
 3888                // Extract fingerprints - content at fold boundaries for validation on restore
 3889                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3890                // content that might change independently.
 3891                // start_fp: first min(32, fold_len) bytes of fold content
 3892                // end_fp: last min(32, fold_len) bytes of fold content
 3893                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3894                let fold_len = end - start;
 3895                let start_fp_end = snapshot
 3896                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3897                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3898                let end_fp_start = snapshot
 3899                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3900                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3901
 3902                (start, end, start_fp, end_fp)
 3903            })
 3904            .collect::<Vec<_>>();
 3905        let db = EditorDb::global(cx);
 3906        self.serialize_folds = cx.background_spawn(async move {
 3907            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3908            if db_folds.is_empty() {
 3909                // No folds - delete any persisted folds for this file
 3910                db.delete_file_folds(workspace_id, file_path)
 3911                    .await
 3912                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3913                    .log_err();
 3914            } else {
 3915                db.save_file_folds(workspace_id, file_path, db_folds)
 3916                    .await
 3917                    .with_context(|| {
 3918                        format!("persisting file folds for workspace {workspace_id:?}")
 3919                    })
 3920                    .log_err();
 3921            }
 3922        });
 3923    }
 3924
 3925    pub fn sync_selections(
 3926        &mut self,
 3927        other: Entity<Editor>,
 3928        cx: &mut Context<Self>,
 3929    ) -> gpui::Subscription {
 3930        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3931        if !other_selections.is_empty() {
 3932            self.selections
 3933                .change_with(&self.display_snapshot(cx), |selections| {
 3934                    selections.select_anchors(other_selections);
 3935                });
 3936        }
 3937
 3938        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3939            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3940                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3941                if other_selections.is_empty() {
 3942                    return;
 3943                }
 3944                let snapshot = this.display_snapshot(cx);
 3945                this.selections.change_with(&snapshot, |selections| {
 3946                    selections.select_anchors(other_selections);
 3947                });
 3948            }
 3949        });
 3950
 3951        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3952            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3953                let these_selections = this.selections.disjoint_anchors().to_vec();
 3954                if these_selections.is_empty() {
 3955                    return;
 3956                }
 3957                other.update(cx, |other_editor, cx| {
 3958                    let snapshot = other_editor.display_snapshot(cx);
 3959                    other_editor
 3960                        .selections
 3961                        .change_with(&snapshot, |selections| {
 3962                            selections.select_anchors(these_selections);
 3963                        })
 3964                });
 3965            }
 3966        });
 3967
 3968        Subscription::join(other_subscription, this_subscription)
 3969    }
 3970
 3971    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3972        if self.buffer().read(cx).is_singleton() {
 3973            return;
 3974        }
 3975        let snapshot = self.buffer.read(cx).snapshot(cx);
 3976        let buffer_ids: HashSet<BufferId> = self
 3977            .selections
 3978            .disjoint_anchor_ranges()
 3979            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3980            .collect();
 3981        for buffer_id in buffer_ids {
 3982            self.unfold_buffer(buffer_id, cx);
 3983        }
 3984    }
 3985
 3986    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3987    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3988    /// effects of selection change occur at the end of the transaction.
 3989    pub fn change_selections<R>(
 3990        &mut self,
 3991        effects: SelectionEffects,
 3992        window: &mut Window,
 3993        cx: &mut Context<Self>,
 3994        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3995    ) -> R {
 3996        let snapshot = self.display_snapshot(cx);
 3997        if let Some(state) = &mut self.deferred_selection_effects_state {
 3998            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3999            state.effects.completions = effects.completions;
 4000            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 4001            let (changed, result) = self.selections.change_with(&snapshot, change);
 4002            state.changed |= changed;
 4003            return result;
 4004        }
 4005        let mut state = DeferredSelectionEffectsState {
 4006            changed: false,
 4007            effects,
 4008            old_cursor_position: self.selections.newest_anchor().head(),
 4009            history_entry: SelectionHistoryEntry {
 4010                selections: self.selections.disjoint_anchors_arc(),
 4011                select_next_state: self.select_next_state.clone(),
 4012                select_prev_state: self.select_prev_state.clone(),
 4013                add_selections_state: self.add_selections_state.clone(),
 4014            },
 4015        };
 4016        let (changed, result) = self.selections.change_with(&snapshot, change);
 4017        state.changed = state.changed || changed;
 4018        if self.defer_selection_effects {
 4019            self.deferred_selection_effects_state = Some(state);
 4020        } else {
 4021            self.apply_selection_effects(state, window, cx);
 4022        }
 4023        result
 4024    }
 4025
 4026    /// Defers the effects of selection change, so that the effects of multiple calls to
 4027    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 4028    /// to selection history and the state of popovers based on selection position aren't
 4029    /// erroneously updated.
 4030    pub fn with_selection_effects_deferred<R>(
 4031        &mut self,
 4032        window: &mut Window,
 4033        cx: &mut Context<Self>,
 4034        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 4035    ) -> R {
 4036        let already_deferred = self.defer_selection_effects;
 4037        self.defer_selection_effects = true;
 4038        let result = update(self, window, cx);
 4039        if !already_deferred {
 4040            self.defer_selection_effects = false;
 4041            if let Some(state) = self.deferred_selection_effects_state.take() {
 4042                self.apply_selection_effects(state, window, cx);
 4043            }
 4044        }
 4045        result
 4046    }
 4047
 4048    fn apply_selection_effects(
 4049        &mut self,
 4050        state: DeferredSelectionEffectsState,
 4051        window: &mut Window,
 4052        cx: &mut Context<Self>,
 4053    ) {
 4054        if state.changed {
 4055            self.selection_history.push(state.history_entry);
 4056
 4057            if let Some(autoscroll) = state.effects.scroll {
 4058                self.request_autoscroll(autoscroll, cx);
 4059            }
 4060
 4061            let old_cursor_position = &state.old_cursor_position;
 4062
 4063            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 4064
 4065            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 4066                self.show_signature_help_auto(window, cx);
 4067            }
 4068        }
 4069    }
 4070
 4071    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4072    where
 4073        I: IntoIterator<Item = (Range<S>, T)>,
 4074        S: ToOffset,
 4075        T: Into<Arc<str>>,
 4076    {
 4077        if self.read_only(cx) {
 4078            return;
 4079        }
 4080
 4081        self.buffer
 4082            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 4083    }
 4084
 4085    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4086    where
 4087        I: IntoIterator<Item = (Range<S>, T)>,
 4088        S: ToOffset,
 4089        T: Into<Arc<str>>,
 4090    {
 4091        if self.read_only(cx) {
 4092            return;
 4093        }
 4094
 4095        self.buffer.update(cx, |buffer, cx| {
 4096            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 4097        });
 4098    }
 4099
 4100    pub fn edit_with_block_indent<I, S, T>(
 4101        &mut self,
 4102        edits: I,
 4103        original_indent_columns: Vec<Option<u32>>,
 4104        cx: &mut Context<Self>,
 4105    ) where
 4106        I: IntoIterator<Item = (Range<S>, T)>,
 4107        S: ToOffset,
 4108        T: Into<Arc<str>>,
 4109    {
 4110        if self.read_only(cx) {
 4111            return;
 4112        }
 4113
 4114        self.buffer.update(cx, |buffer, cx| {
 4115            buffer.edit(
 4116                edits,
 4117                Some(AutoindentMode::Block {
 4118                    original_indent_columns,
 4119                }),
 4120                cx,
 4121            )
 4122        });
 4123    }
 4124
 4125    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 4126        self.hide_context_menu(window, cx);
 4127
 4128        match phase {
 4129            SelectPhase::Begin {
 4130                position,
 4131                add,
 4132                click_count,
 4133            } => self.begin_selection(position, add, click_count, window, cx),
 4134            SelectPhase::BeginColumnar {
 4135                position,
 4136                goal_column,
 4137                reset,
 4138                mode,
 4139            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4140            SelectPhase::Extend {
 4141                position,
 4142                click_count,
 4143            } => self.extend_selection(position, click_count, window, cx),
 4144            SelectPhase::Update {
 4145                position,
 4146                goal_column,
 4147                scroll_delta,
 4148            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4149            SelectPhase::End => self.end_selection(window, cx),
 4150        }
 4151    }
 4152
 4153    fn extend_selection(
 4154        &mut self,
 4155        position: DisplayPoint,
 4156        click_count: usize,
 4157        window: &mut Window,
 4158        cx: &mut Context<Self>,
 4159    ) {
 4160        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4161        let tail = self
 4162            .selections
 4163            .newest::<MultiBufferOffset>(&display_map)
 4164            .tail();
 4165        let click_count = click_count.max(match self.selections.select_mode() {
 4166            SelectMode::Character => 1,
 4167            SelectMode::Word(_) => 2,
 4168            SelectMode::Line(_) => 3,
 4169            SelectMode::All => 4,
 4170        });
 4171        self.begin_selection(position, false, click_count, window, cx);
 4172
 4173        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4174
 4175        let current_selection = match self.selections.select_mode() {
 4176            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4177            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4178        };
 4179
 4180        let mut pending_selection = self
 4181            .selections
 4182            .pending_anchor()
 4183            .cloned()
 4184            .expect("extend_selection not called with pending selection");
 4185
 4186        if pending_selection
 4187            .start
 4188            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4189            == Ordering::Greater
 4190        {
 4191            pending_selection.start = current_selection.start;
 4192        }
 4193        if pending_selection
 4194            .end
 4195            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4196            == Ordering::Less
 4197        {
 4198            pending_selection.end = current_selection.end;
 4199            pending_selection.reversed = true;
 4200        }
 4201
 4202        let mut pending_mode = self.selections.pending_mode().unwrap();
 4203        match &mut pending_mode {
 4204            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4205            _ => {}
 4206        }
 4207
 4208        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4209            SelectionEffects::scroll(Autoscroll::fit())
 4210        } else {
 4211            SelectionEffects::no_scroll()
 4212        };
 4213
 4214        self.change_selections(effects, window, cx, |s| {
 4215            s.set_pending(pending_selection.clone(), pending_mode);
 4216            s.set_is_extending(true);
 4217        });
 4218    }
 4219
 4220    fn begin_selection(
 4221        &mut self,
 4222        position: DisplayPoint,
 4223        add: bool,
 4224        click_count: usize,
 4225        window: &mut Window,
 4226        cx: &mut Context<Self>,
 4227    ) {
 4228        if !self.focus_handle.is_focused(window) {
 4229            self.last_focused_descendant = None;
 4230            window.focus(&self.focus_handle, cx);
 4231        }
 4232
 4233        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4234        let buffer = display_map.buffer_snapshot();
 4235        let position = display_map.clip_point(position, Bias::Left);
 4236
 4237        let start;
 4238        let end;
 4239        let mode;
 4240        let mut auto_scroll;
 4241        match click_count {
 4242            1 => {
 4243                start = buffer.anchor_before(position.to_point(&display_map));
 4244                end = start;
 4245                mode = SelectMode::Character;
 4246                auto_scroll = true;
 4247            }
 4248            2 => {
 4249                let position = display_map
 4250                    .clip_point(position, Bias::Left)
 4251                    .to_offset(&display_map, Bias::Left);
 4252                let (range, _) = buffer.surrounding_word(position, None);
 4253                start = buffer.anchor_before(range.start);
 4254                end = buffer.anchor_before(range.end);
 4255                mode = SelectMode::Word(start..end);
 4256                auto_scroll = true;
 4257            }
 4258            3 => {
 4259                let position = display_map
 4260                    .clip_point(position, Bias::Left)
 4261                    .to_point(&display_map);
 4262                let line_start = display_map.prev_line_boundary(position).0;
 4263                let next_line_start = buffer.clip_point(
 4264                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4265                    Bias::Left,
 4266                );
 4267                start = buffer.anchor_before(line_start);
 4268                end = buffer.anchor_before(next_line_start);
 4269                mode = SelectMode::Line(start..end);
 4270                auto_scroll = true;
 4271            }
 4272            _ => {
 4273                start = buffer.anchor_before(MultiBufferOffset(0));
 4274                end = buffer.anchor_before(buffer.len());
 4275                mode = SelectMode::All;
 4276                auto_scroll = false;
 4277            }
 4278        }
 4279        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4280
 4281        let point_to_delete: Option<usize> = {
 4282            let selected_points: Vec<Selection<Point>> =
 4283                self.selections.disjoint_in_range(start..end, &display_map);
 4284
 4285            if !add || click_count > 1 {
 4286                None
 4287            } else if !selected_points.is_empty() {
 4288                Some(selected_points[0].id)
 4289            } else {
 4290                let clicked_point_already_selected =
 4291                    self.selections.disjoint_anchors().iter().find(|selection| {
 4292                        selection.start.to_point(buffer) == start.to_point(buffer)
 4293                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4294                    });
 4295
 4296                clicked_point_already_selected.map(|selection| selection.id)
 4297            }
 4298        };
 4299
 4300        let selections_count = self.selections.count();
 4301        let effects = if auto_scroll {
 4302            SelectionEffects::default()
 4303        } else {
 4304            SelectionEffects::no_scroll()
 4305        };
 4306
 4307        self.change_selections(effects, window, cx, |s| {
 4308            if let Some(point_to_delete) = point_to_delete {
 4309                s.delete(point_to_delete);
 4310
 4311                if selections_count == 1 {
 4312                    s.set_pending_anchor_range(start..end, mode);
 4313                }
 4314            } else {
 4315                if !add {
 4316                    s.clear_disjoint();
 4317                }
 4318
 4319                s.set_pending_anchor_range(start..end, mode);
 4320            }
 4321        });
 4322    }
 4323
 4324    fn begin_columnar_selection(
 4325        &mut self,
 4326        position: DisplayPoint,
 4327        goal_column: u32,
 4328        reset: bool,
 4329        mode: ColumnarMode,
 4330        window: &mut Window,
 4331        cx: &mut Context<Self>,
 4332    ) {
 4333        if !self.focus_handle.is_focused(window) {
 4334            self.last_focused_descendant = None;
 4335            window.focus(&self.focus_handle, cx);
 4336        }
 4337
 4338        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4339
 4340        if reset {
 4341            let pointer_position = display_map
 4342                .buffer_snapshot()
 4343                .anchor_before(position.to_point(&display_map));
 4344
 4345            self.change_selections(
 4346                SelectionEffects::scroll(Autoscroll::newest()),
 4347                window,
 4348                cx,
 4349                |s| {
 4350                    s.clear_disjoint();
 4351                    s.set_pending_anchor_range(
 4352                        pointer_position..pointer_position,
 4353                        SelectMode::Character,
 4354                    );
 4355                },
 4356            );
 4357        };
 4358
 4359        let tail = self.selections.newest::<Point>(&display_map).tail();
 4360        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4361        self.columnar_selection_state = match mode {
 4362            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4363                selection_tail: selection_anchor,
 4364                display_point: if reset {
 4365                    if position.column() != goal_column {
 4366                        Some(DisplayPoint::new(position.row(), goal_column))
 4367                    } else {
 4368                        None
 4369                    }
 4370                } else {
 4371                    None
 4372                },
 4373            }),
 4374            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4375                selection_tail: selection_anchor,
 4376            }),
 4377        };
 4378
 4379        if !reset {
 4380            self.select_columns(position, goal_column, &display_map, window, cx);
 4381        }
 4382    }
 4383
 4384    fn update_selection(
 4385        &mut self,
 4386        position: DisplayPoint,
 4387        goal_column: u32,
 4388        scroll_delta: gpui::Point<f32>,
 4389        window: &mut Window,
 4390        cx: &mut Context<Self>,
 4391    ) {
 4392        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4393
 4394        if self.columnar_selection_state.is_some() {
 4395            self.select_columns(position, goal_column, &display_map, window, cx);
 4396        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4397            let buffer = display_map.buffer_snapshot();
 4398            let head;
 4399            let tail;
 4400            let mode = self.selections.pending_mode().unwrap();
 4401            match &mode {
 4402                SelectMode::Character => {
 4403                    head = position.to_point(&display_map);
 4404                    tail = pending.tail().to_point(buffer);
 4405                }
 4406                SelectMode::Word(original_range) => {
 4407                    let offset = display_map
 4408                        .clip_point(position, Bias::Left)
 4409                        .to_offset(&display_map, Bias::Left);
 4410                    let original_range = original_range.to_offset(buffer);
 4411
 4412                    let head_offset = if buffer.is_inside_word(offset, None)
 4413                        || original_range.contains(&offset)
 4414                    {
 4415                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4416                        if word_range.start < original_range.start {
 4417                            word_range.start
 4418                        } else {
 4419                            word_range.end
 4420                        }
 4421                    } else {
 4422                        offset
 4423                    };
 4424
 4425                    head = head_offset.to_point(buffer);
 4426                    if head_offset <= original_range.start {
 4427                        tail = original_range.end.to_point(buffer);
 4428                    } else {
 4429                        tail = original_range.start.to_point(buffer);
 4430                    }
 4431                }
 4432                SelectMode::Line(original_range) => {
 4433                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4434
 4435                    let position = display_map
 4436                        .clip_point(position, Bias::Left)
 4437                        .to_point(&display_map);
 4438                    let line_start = display_map.prev_line_boundary(position).0;
 4439                    let next_line_start = buffer.clip_point(
 4440                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4441                        Bias::Left,
 4442                    );
 4443
 4444                    if line_start < original_range.start {
 4445                        head = line_start
 4446                    } else {
 4447                        head = next_line_start
 4448                    }
 4449
 4450                    if head <= original_range.start {
 4451                        tail = original_range.end;
 4452                    } else {
 4453                        tail = original_range.start;
 4454                    }
 4455                }
 4456                SelectMode::All => {
 4457                    return;
 4458                }
 4459            };
 4460
 4461            if head < tail {
 4462                pending.start = buffer.anchor_before(head);
 4463                pending.end = buffer.anchor_before(tail);
 4464                pending.reversed = true;
 4465            } else {
 4466                pending.start = buffer.anchor_before(tail);
 4467                pending.end = buffer.anchor_before(head);
 4468                pending.reversed = false;
 4469            }
 4470
 4471            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4472                s.set_pending(pending.clone(), mode);
 4473            });
 4474        } else {
 4475            log::error!("update_selection dispatched with no pending selection");
 4476            return;
 4477        }
 4478
 4479        self.apply_scroll_delta(scroll_delta, window, cx);
 4480        cx.notify();
 4481    }
 4482
 4483    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4484        self.columnar_selection_state.take();
 4485        if let Some(pending_mode) = self.selections.pending_mode() {
 4486            let selections = self
 4487                .selections
 4488                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4489            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4490                s.select(selections);
 4491                s.clear_pending();
 4492                if s.is_extending() {
 4493                    s.set_is_extending(false);
 4494                } else {
 4495                    s.set_select_mode(pending_mode);
 4496                }
 4497            });
 4498        }
 4499    }
 4500
 4501    fn select_columns(
 4502        &mut self,
 4503        head: DisplayPoint,
 4504        goal_column: u32,
 4505        display_map: &DisplaySnapshot,
 4506        window: &mut Window,
 4507        cx: &mut Context<Self>,
 4508    ) {
 4509        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4510            return;
 4511        };
 4512
 4513        let tail = match columnar_state {
 4514            ColumnarSelectionState::FromMouse {
 4515                selection_tail,
 4516                display_point,
 4517            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4518            ColumnarSelectionState::FromSelection { selection_tail } => {
 4519                selection_tail.to_display_point(display_map)
 4520            }
 4521        };
 4522
 4523        let start_row = cmp::min(tail.row(), head.row());
 4524        let end_row = cmp::max(tail.row(), head.row());
 4525        let start_column = cmp::min(tail.column(), goal_column);
 4526        let end_column = cmp::max(tail.column(), goal_column);
 4527        let reversed = start_column < tail.column();
 4528
 4529        let selection_ranges = (start_row.0..=end_row.0)
 4530            .map(DisplayRow)
 4531            .filter_map(|row| {
 4532                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4533                    || start_column <= display_map.line_len(row))
 4534                    && !display_map.is_block_line(row)
 4535                {
 4536                    let start = display_map
 4537                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4538                        .to_point(display_map);
 4539                    let end = display_map
 4540                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4541                        .to_point(display_map);
 4542                    if reversed {
 4543                        Some(end..start)
 4544                    } else {
 4545                        Some(start..end)
 4546                    }
 4547                } else {
 4548                    None
 4549                }
 4550            })
 4551            .collect::<Vec<_>>();
 4552        if selection_ranges.is_empty() {
 4553            return;
 4554        }
 4555
 4556        let ranges = match columnar_state {
 4557            ColumnarSelectionState::FromMouse { .. } => {
 4558                let mut non_empty_ranges = selection_ranges
 4559                    .iter()
 4560                    .filter(|selection_range| selection_range.start != selection_range.end)
 4561                    .peekable();
 4562                if non_empty_ranges.peek().is_some() {
 4563                    non_empty_ranges.cloned().collect()
 4564                } else {
 4565                    selection_ranges
 4566                }
 4567            }
 4568            _ => selection_ranges,
 4569        };
 4570
 4571        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4572            s.select_ranges(ranges);
 4573        });
 4574        cx.notify();
 4575    }
 4576
 4577    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4578        self.selections
 4579            .all_adjusted(snapshot)
 4580            .iter()
 4581            .any(|selection| !selection.is_empty())
 4582    }
 4583
 4584    pub fn has_pending_nonempty_selection(&self) -> bool {
 4585        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4586            Some(Selection { start, end, .. }) => start != end,
 4587            None => false,
 4588        };
 4589
 4590        pending_nonempty_selection
 4591            || (self.columnar_selection_state.is_some()
 4592                && self.selections.disjoint_anchors().len() > 1)
 4593    }
 4594
 4595    pub fn has_pending_selection(&self) -> bool {
 4596        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4597    }
 4598
 4599    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4600        self.selection_mark_mode = false;
 4601        self.selection_drag_state = SelectionDragState::None;
 4602
 4603        if self.dismiss_menus_and_popups(true, window, cx) {
 4604            cx.notify();
 4605            return;
 4606        }
 4607        if self.clear_expanded_diff_hunks(cx) {
 4608            cx.notify();
 4609            return;
 4610        }
 4611        if self.show_git_blame_gutter {
 4612            self.show_git_blame_gutter = false;
 4613            cx.notify();
 4614            return;
 4615        }
 4616
 4617        if self.mode.is_full()
 4618            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4619        {
 4620            cx.notify();
 4621            return;
 4622        }
 4623
 4624        cx.propagate();
 4625    }
 4626
 4627    pub fn dismiss_menus_and_popups(
 4628        &mut self,
 4629        is_user_requested: bool,
 4630        window: &mut Window,
 4631        cx: &mut Context<Self>,
 4632    ) -> bool {
 4633        let mut dismissed = false;
 4634
 4635        dismissed |= self.take_rename(false, window, cx).is_some();
 4636        dismissed |= self.hide_blame_popover(true, cx);
 4637        dismissed |= hide_hover(self, cx);
 4638        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4639        dismissed |= self.hide_context_menu(window, cx).is_some();
 4640        dismissed |= self.mouse_context_menu.take().is_some();
 4641        dismissed |= is_user_requested
 4642            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4643        dismissed |= self.snippet_stack.pop().is_some();
 4644        if self.diff_review_drag_state.is_some() {
 4645            self.cancel_diff_review_drag(cx);
 4646            dismissed = true;
 4647        }
 4648        if !self.diff_review_overlays.is_empty() {
 4649            self.dismiss_all_diff_review_overlays(cx);
 4650            dismissed = true;
 4651        }
 4652
 4653        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4654            self.dismiss_diagnostics(cx);
 4655            dismissed = true;
 4656        }
 4657
 4658        dismissed
 4659    }
 4660
 4661    fn linked_editing_ranges_for(
 4662        &self,
 4663        selection: Range<text::Anchor>,
 4664        cx: &App,
 4665    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4666        if self.linked_edit_ranges.is_empty() {
 4667            return None;
 4668        }
 4669        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4670            selection.end.buffer_id.and_then(|end_buffer_id| {
 4671                if selection.start.buffer_id != Some(end_buffer_id) {
 4672                    return None;
 4673                }
 4674                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4675                let snapshot = buffer.read(cx).snapshot();
 4676                self.linked_edit_ranges
 4677                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4678                    .map(|ranges| (ranges, snapshot, buffer))
 4679            })?;
 4680        use text::ToOffset as TO;
 4681        // find offset from the start of current range to current cursor position
 4682        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4683
 4684        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4685        let start_difference = start_offset - start_byte_offset;
 4686        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4687        let end_difference = end_offset - start_byte_offset;
 4688
 4689        // Current range has associated linked ranges.
 4690        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4691        for range in linked_ranges.iter() {
 4692            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4693            let end_offset = start_offset + end_difference;
 4694            let start_offset = start_offset + start_difference;
 4695            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4696                continue;
 4697            }
 4698            if self.selections.disjoint_anchor_ranges().any(|s| {
 4699                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4700                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4701                {
 4702                    return false;
 4703                }
 4704                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4705                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4706            }) {
 4707                continue;
 4708            }
 4709            let start = buffer_snapshot.anchor_after(start_offset);
 4710            let end = buffer_snapshot.anchor_after(end_offset);
 4711            linked_edits
 4712                .entry(buffer.clone())
 4713                .or_default()
 4714                .push(start..end);
 4715        }
 4716        Some(linked_edits)
 4717    }
 4718
 4719    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4720        let text: Arc<str> = text.into();
 4721
 4722        if self.read_only(cx) {
 4723            return;
 4724        }
 4725
 4726        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4727
 4728        self.unfold_buffers_with_selections(cx);
 4729
 4730        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4731        let mut bracket_inserted = false;
 4732        let mut edits = Vec::new();
 4733        let mut linked_edits = LinkedEdits::new();
 4734        let mut new_selections = Vec::with_capacity(selections.len());
 4735        let mut new_autoclose_regions = Vec::new();
 4736        let snapshot = self.buffer.read(cx).read(cx);
 4737        let mut clear_linked_edit_ranges = false;
 4738        let mut all_selections_read_only = true;
 4739        let mut has_adjacent_edits = false;
 4740        let mut in_adjacent_group = false;
 4741
 4742        let mut regions = self
 4743            .selections_with_autoclose_regions(selections, &snapshot)
 4744            .peekable();
 4745
 4746        while let Some((selection, autoclose_region)) = regions.next() {
 4747            if snapshot
 4748                .point_to_buffer_point(selection.head())
 4749                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4750            {
 4751                continue;
 4752            }
 4753            if snapshot
 4754                .point_to_buffer_point(selection.tail())
 4755                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4756            {
 4757                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4758                continue;
 4759            }
 4760            all_selections_read_only = false;
 4761
 4762            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4763                // Determine if the inserted text matches the opening or closing
 4764                // bracket of any of this language's bracket pairs.
 4765                let mut bracket_pair = None;
 4766                let mut is_bracket_pair_start = false;
 4767                let mut is_bracket_pair_end = false;
 4768                if !text.is_empty() {
 4769                    let mut bracket_pair_matching_end = None;
 4770                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4771                    //  and they are removing the character that triggered IME popup.
 4772                    for (pair, enabled) in scope.brackets() {
 4773                        if !pair.close && !pair.surround {
 4774                            continue;
 4775                        }
 4776
 4777                        if enabled && pair.start.ends_with(text.as_ref()) {
 4778                            let prefix_len = pair.start.len() - text.len();
 4779                            let preceding_text_matches_prefix = prefix_len == 0
 4780                                || (selection.start.column >= (prefix_len as u32)
 4781                                    && snapshot.contains_str_at(
 4782                                        Point::new(
 4783                                            selection.start.row,
 4784                                            selection.start.column - (prefix_len as u32),
 4785                                        ),
 4786                                        &pair.start[..prefix_len],
 4787                                    ));
 4788                            if preceding_text_matches_prefix {
 4789                                bracket_pair = Some(pair.clone());
 4790                                is_bracket_pair_start = true;
 4791                                break;
 4792                            }
 4793                        }
 4794                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4795                        {
 4796                            // take first bracket pair matching end, but don't break in case a later bracket
 4797                            // pair matches start
 4798                            bracket_pair_matching_end = Some(pair.clone());
 4799                        }
 4800                    }
 4801                    if let Some(end) = bracket_pair_matching_end
 4802                        && bracket_pair.is_none()
 4803                    {
 4804                        bracket_pair = Some(end);
 4805                        is_bracket_pair_end = true;
 4806                    }
 4807                }
 4808
 4809                if let Some(bracket_pair) = bracket_pair {
 4810                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4811                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4812                    let auto_surround =
 4813                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4814                    if selection.is_empty() {
 4815                        if is_bracket_pair_start {
 4816                            // If the inserted text is a suffix of an opening bracket and the
 4817                            // selection is preceded by the rest of the opening bracket, then
 4818                            // insert the closing bracket.
 4819                            let following_text_allows_autoclose = snapshot
 4820                                .chars_at(selection.start)
 4821                                .next()
 4822                                .is_none_or(|c| scope.should_autoclose_before(c));
 4823
 4824                            let preceding_text_allows_autoclose = selection.start.column == 0
 4825                                || snapshot
 4826                                    .reversed_chars_at(selection.start)
 4827                                    .next()
 4828                                    .is_none_or(|c| {
 4829                                        bracket_pair.start != bracket_pair.end
 4830                                            || !snapshot
 4831                                                .char_classifier_at(selection.start)
 4832                                                .is_word(c)
 4833                                    });
 4834
 4835                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4836                                && bracket_pair.start.len() == 1
 4837                            {
 4838                                let target = bracket_pair.start.chars().next().unwrap();
 4839                                let mut byte_offset = 0u32;
 4840                                let current_line_count = snapshot
 4841                                    .reversed_chars_at(selection.start)
 4842                                    .take_while(|&c| c != '\n')
 4843                                    .filter(|c| {
 4844                                        byte_offset += c.len_utf8() as u32;
 4845                                        if *c != target {
 4846                                            return false;
 4847                                        }
 4848
 4849                                        let point = Point::new(
 4850                                            selection.start.row,
 4851                                            selection.start.column.saturating_sub(byte_offset),
 4852                                        );
 4853
 4854                                        let is_enabled = snapshot
 4855                                            .language_scope_at(point)
 4856                                            .and_then(|scope| {
 4857                                                scope
 4858                                                    .brackets()
 4859                                                    .find(|(pair, _)| {
 4860                                                        pair.start == bracket_pair.start
 4861                                                    })
 4862                                                    .map(|(_, enabled)| enabled)
 4863                                            })
 4864                                            .unwrap_or(true);
 4865
 4866                                        let is_delimiter = snapshot
 4867                                            .language_scope_at(Point::new(
 4868                                                point.row,
 4869                                                point.column + 1,
 4870                                            ))
 4871                                            .and_then(|scope| {
 4872                                                scope
 4873                                                    .brackets()
 4874                                                    .find(|(pair, _)| {
 4875                                                        pair.start == bracket_pair.start
 4876                                                    })
 4877                                                    .map(|(_, enabled)| !enabled)
 4878                                            })
 4879                                            .unwrap_or(false);
 4880
 4881                                        is_enabled && !is_delimiter
 4882                                    })
 4883                                    .count();
 4884                                current_line_count % 2 == 1
 4885                            } else {
 4886                                false
 4887                            };
 4888
 4889                            if autoclose
 4890                                && bracket_pair.close
 4891                                && following_text_allows_autoclose
 4892                                && preceding_text_allows_autoclose
 4893                                && !is_closing_quote
 4894                            {
 4895                                let anchor = snapshot.anchor_before(selection.end);
 4896                                new_selections.push((selection.map(|_| anchor), text.len()));
 4897                                new_autoclose_regions.push((
 4898                                    anchor,
 4899                                    text.len(),
 4900                                    selection.id,
 4901                                    bracket_pair.clone(),
 4902                                ));
 4903                                edits.push((
 4904                                    selection.range(),
 4905                                    format!("{}{}", text, bracket_pair.end).into(),
 4906                                ));
 4907                                bracket_inserted = true;
 4908                                continue;
 4909                            }
 4910                        }
 4911
 4912                        if let Some(region) = autoclose_region {
 4913                            // If the selection is followed by an auto-inserted closing bracket,
 4914                            // then don't insert that closing bracket again; just move the selection
 4915                            // past the closing bracket.
 4916                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4917                                && text.as_ref() == region.pair.end.as_str()
 4918                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4919                            if should_skip {
 4920                                let anchor = snapshot.anchor_after(selection.end);
 4921                                new_selections
 4922                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4923                                continue;
 4924                            }
 4925                        }
 4926
 4927                        let always_treat_brackets_as_autoclosed = snapshot
 4928                            .language_settings_at(selection.start, cx)
 4929                            .always_treat_brackets_as_autoclosed;
 4930                        if always_treat_brackets_as_autoclosed
 4931                            && is_bracket_pair_end
 4932                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4933                        {
 4934                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4935                            // and the inserted text is a closing bracket and the selection is followed
 4936                            // by the closing bracket then move the selection past the closing bracket.
 4937                            let anchor = snapshot.anchor_after(selection.end);
 4938                            new_selections.push((selection.map(|_| anchor), text.len()));
 4939                            continue;
 4940                        }
 4941                    }
 4942                    // If an opening bracket is 1 character long and is typed while
 4943                    // text is selected, then surround that text with the bracket pair.
 4944                    else if auto_surround
 4945                        && bracket_pair.surround
 4946                        && is_bracket_pair_start
 4947                        && bracket_pair.start.chars().count() == 1
 4948                    {
 4949                        edits.push((selection.start..selection.start, text.clone()));
 4950                        edits.push((
 4951                            selection.end..selection.end,
 4952                            bracket_pair.end.as_str().into(),
 4953                        ));
 4954                        bracket_inserted = true;
 4955                        new_selections.push((
 4956                            Selection {
 4957                                id: selection.id,
 4958                                start: snapshot.anchor_after(selection.start),
 4959                                end: snapshot.anchor_before(selection.end),
 4960                                reversed: selection.reversed,
 4961                                goal: selection.goal,
 4962                            },
 4963                            0,
 4964                        ));
 4965                        continue;
 4966                    }
 4967                }
 4968            }
 4969
 4970            if self.auto_replace_emoji_shortcode
 4971                && selection.is_empty()
 4972                && text.as_ref().ends_with(':')
 4973                && let Some(possible_emoji_short_code) =
 4974                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4975                && !possible_emoji_short_code.is_empty()
 4976                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4977            {
 4978                let emoji_shortcode_start = Point::new(
 4979                    selection.start.row,
 4980                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4981                );
 4982
 4983                // Remove shortcode from buffer
 4984                edits.push((
 4985                    emoji_shortcode_start..selection.start,
 4986                    "".to_string().into(),
 4987                ));
 4988                new_selections.push((
 4989                    Selection {
 4990                        id: selection.id,
 4991                        start: snapshot.anchor_after(emoji_shortcode_start),
 4992                        end: snapshot.anchor_before(selection.start),
 4993                        reversed: selection.reversed,
 4994                        goal: selection.goal,
 4995                    },
 4996                    0,
 4997                ));
 4998
 4999                // Insert emoji
 5000                let selection_start_anchor = snapshot.anchor_after(selection.start);
 5001                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 5002                edits.push((selection.start..selection.end, emoji.to_string().into()));
 5003
 5004                continue;
 5005            }
 5006
 5007            let next_is_adjacent = regions
 5008                .peek()
 5009                .is_some_and(|(next, _)| selection.end == next.start);
 5010
 5011            // If not handling any auto-close operation, then just replace the selected
 5012            // text with the given input and move the selection to the end of the
 5013            // newly inserted text.
 5014            let anchor = if in_adjacent_group || next_is_adjacent {
 5015                // After edits the right bias would shift those anchor to the next visible fragment
 5016                // but we want to resolve to the previous one
 5017                snapshot.anchor_before(selection.end)
 5018            } else {
 5019                snapshot.anchor_after(selection.end)
 5020            };
 5021
 5022            if !self.linked_edit_ranges.is_empty() {
 5023                let start_anchor = snapshot.anchor_before(selection.start);
 5024
 5025                let is_word_char = text.chars().next().is_none_or(|char| {
 5026                    let classifier = snapshot
 5027                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 5028                        .scope_context(Some(CharScopeContext::LinkedEdit));
 5029                    classifier.is_word(char)
 5030                });
 5031                let is_dot = text.as_ref() == ".";
 5032                let should_apply_linked_edit = is_word_char || is_dot;
 5033
 5034                if should_apply_linked_edit {
 5035                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 5036                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 5037                } else {
 5038                    clear_linked_edit_ranges = true;
 5039                }
 5040            }
 5041
 5042            new_selections.push((selection.map(|_| anchor), 0));
 5043            edits.push((selection.start..selection.end, text.clone()));
 5044
 5045            has_adjacent_edits |= next_is_adjacent;
 5046            in_adjacent_group = next_is_adjacent;
 5047        }
 5048
 5049        if all_selections_read_only {
 5050            return;
 5051        }
 5052
 5053        drop(regions);
 5054        drop(snapshot);
 5055
 5056        self.transact(window, cx, |this, window, cx| {
 5057            if clear_linked_edit_ranges {
 5058                this.linked_edit_ranges.clear();
 5059            }
 5060            let initial_buffer_versions =
 5061                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 5062
 5063            this.buffer.update(cx, |buffer, cx| {
 5064                if has_adjacent_edits {
 5065                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 5066                } else {
 5067                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 5068                }
 5069            });
 5070            linked_edits.apply(cx);
 5071            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 5072            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 5073            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 5074            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 5075                new_anchor_selections,
 5076                &map,
 5077            )
 5078            .zip(new_selection_deltas)
 5079            .map(|(selection, delta)| Selection {
 5080                id: selection.id,
 5081                start: selection.start + delta,
 5082                end: selection.end + delta,
 5083                reversed: selection.reversed,
 5084                goal: SelectionGoal::None,
 5085            })
 5086            .collect::<Vec<_>>();
 5087
 5088            let mut i = 0;
 5089            for (position, delta, selection_id, pair) in new_autoclose_regions {
 5090                let position = position.to_offset(map.buffer_snapshot()) + delta;
 5091                let start = map.buffer_snapshot().anchor_before(position);
 5092                let end = map.buffer_snapshot().anchor_after(position);
 5093                while let Some(existing_state) = this.autoclose_regions.get(i) {
 5094                    match existing_state
 5095                        .range
 5096                        .start
 5097                        .cmp(&start, map.buffer_snapshot())
 5098                    {
 5099                        Ordering::Less => i += 1,
 5100                        Ordering::Greater => break,
 5101                        Ordering::Equal => {
 5102                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 5103                                Ordering::Less => i += 1,
 5104                                Ordering::Equal => break,
 5105                                Ordering::Greater => break,
 5106                            }
 5107                        }
 5108                    }
 5109                }
 5110                this.autoclose_regions.insert(
 5111                    i,
 5112                    AutocloseRegion {
 5113                        selection_id,
 5114                        range: start..end,
 5115                        pair,
 5116                    },
 5117                );
 5118            }
 5119
 5120            let had_active_edit_prediction = this.has_active_edit_prediction();
 5121            this.change_selections(
 5122                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 5123                window,
 5124                cx,
 5125                |s| s.select(new_selections),
 5126            );
 5127
 5128            if !bracket_inserted
 5129                && let Some(on_type_format_task) =
 5130                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5131            {
 5132                on_type_format_task.detach_and_log_err(cx);
 5133            }
 5134
 5135            let editor_settings = EditorSettings::get_global(cx);
 5136            if bracket_inserted
 5137                && (editor_settings.auto_signature_help
 5138                    || editor_settings.show_signature_help_after_edits)
 5139            {
 5140                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5141            }
 5142
 5143            let trigger_in_words =
 5144                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5145            if this.hard_wrap.is_some() {
 5146                let latest: Range<Point> = this.selections.newest(&map).range();
 5147                if latest.is_empty()
 5148                    && this
 5149                        .buffer()
 5150                        .read(cx)
 5151                        .snapshot(cx)
 5152                        .line_len(MultiBufferRow(latest.start.row))
 5153                        == latest.start.column
 5154                {
 5155                    this.rewrap_impl(
 5156                        RewrapOptions {
 5157                            override_language_settings: true,
 5158                            preserve_existing_whitespace: true,
 5159                            line_length: None,
 5160                        },
 5161                        cx,
 5162                    )
 5163                }
 5164            }
 5165            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5166            refresh_linked_ranges(this, window, cx);
 5167            this.refresh_edit_prediction(true, false, window, cx);
 5168            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5169        });
 5170    }
 5171
 5172    fn find_possible_emoji_shortcode_at_position(
 5173        snapshot: &MultiBufferSnapshot,
 5174        position: Point,
 5175    ) -> Option<String> {
 5176        let mut chars = Vec::new();
 5177        let mut found_colon = false;
 5178        for char in snapshot.reversed_chars_at(position).take(100) {
 5179            // Found a possible emoji shortcode in the middle of the buffer
 5180            if found_colon {
 5181                if char.is_whitespace() {
 5182                    chars.reverse();
 5183                    return Some(chars.iter().collect());
 5184                }
 5185                // If the previous character is not a whitespace, we are in the middle of a word
 5186                // and we only want to complete the shortcode if the word is made up of other emojis
 5187                let mut containing_word = String::new();
 5188                for ch in snapshot
 5189                    .reversed_chars_at(position)
 5190                    .skip(chars.len() + 1)
 5191                    .take(100)
 5192                {
 5193                    if ch.is_whitespace() {
 5194                        break;
 5195                    }
 5196                    containing_word.push(ch);
 5197                }
 5198                let containing_word = containing_word.chars().rev().collect::<String>();
 5199                if util::word_consists_of_emojis(containing_word.as_str()) {
 5200                    chars.reverse();
 5201                    return Some(chars.iter().collect());
 5202                }
 5203            }
 5204
 5205            if char.is_whitespace() || !char.is_ascii() {
 5206                return None;
 5207            }
 5208            if char == ':' {
 5209                found_colon = true;
 5210            } else {
 5211                chars.push(char);
 5212            }
 5213        }
 5214        // Found a possible emoji shortcode at the beginning of the buffer
 5215        chars.reverse();
 5216        Some(chars.iter().collect())
 5217    }
 5218
 5219    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5220        if self.read_only(cx) {
 5221            return;
 5222        }
 5223
 5224        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5225        self.transact(window, cx, |this, window, cx| {
 5226            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5227                let selections = this
 5228                    .selections
 5229                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5230                let multi_buffer = this.buffer.read(cx);
 5231                let buffer = multi_buffer.snapshot(cx);
 5232                selections
 5233                    .iter()
 5234                    .map(|selection| {
 5235                        let start_point = selection.start.to_point(&buffer);
 5236                        let mut existing_indent =
 5237                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5238                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5239                        let start = selection.start;
 5240                        let end = selection.end;
 5241                        let selection_is_empty = start == end;
 5242                        let language_scope = buffer.language_scope_at(start);
 5243                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5244                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5245                                &buffer,
 5246                                start..end,
 5247                                language,
 5248                            )
 5249                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5250                                    &buffer,
 5251                                    start..end,
 5252                                );
 5253
 5254                            let mut newline_config = NewlineConfig::Newline {
 5255                                additional_indent: IndentSize::spaces(0),
 5256                                extra_line_additional_indent: if needs_extra_newline {
 5257                                    Some(IndentSize::spaces(0))
 5258                                } else {
 5259                                    None
 5260                                },
 5261                                prevent_auto_indent: false,
 5262                            };
 5263
 5264                            let comment_delimiter = maybe!({
 5265                                if !selection_is_empty {
 5266                                    return None;
 5267                                }
 5268
 5269                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5270                                    return None;
 5271                                }
 5272
 5273                                return comment_delimiter_for_newline(
 5274                                    &start_point,
 5275                                    &buffer,
 5276                                    language,
 5277                                );
 5278                            });
 5279
 5280                            let doc_delimiter = maybe!({
 5281                                if !selection_is_empty {
 5282                                    return None;
 5283                                }
 5284
 5285                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5286                                    return None;
 5287                                }
 5288
 5289                                return documentation_delimiter_for_newline(
 5290                                    &start_point,
 5291                                    &buffer,
 5292                                    language,
 5293                                    &mut newline_config,
 5294                                );
 5295                            });
 5296
 5297                            let list_delimiter = maybe!({
 5298                                if !selection_is_empty {
 5299                                    return None;
 5300                                }
 5301
 5302                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5303                                    return None;
 5304                                }
 5305
 5306                                return list_delimiter_for_newline(
 5307                                    &start_point,
 5308                                    &buffer,
 5309                                    language,
 5310                                    &mut newline_config,
 5311                                );
 5312                            });
 5313
 5314                            (
 5315                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5316                                newline_config,
 5317                            )
 5318                        } else {
 5319                            (
 5320                                None,
 5321                                NewlineConfig::Newline {
 5322                                    additional_indent: IndentSize::spaces(0),
 5323                                    extra_line_additional_indent: None,
 5324                                    prevent_auto_indent: false,
 5325                                },
 5326                            )
 5327                        };
 5328
 5329                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5330                            NewlineConfig::ClearCurrentLine => {
 5331                                let row_start =
 5332                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5333                                (row_start, String::new(), false)
 5334                            }
 5335                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5336                                let row_start =
 5337                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5338                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5339                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5340                                let reduced_indent =
 5341                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5342                                let mut new_text = String::new();
 5343                                new_text.extend(reduced_indent.chars());
 5344                                new_text.push_str(continuation);
 5345                                (row_start, new_text, true)
 5346                            }
 5347                            NewlineConfig::Newline {
 5348                                additional_indent,
 5349                                extra_line_additional_indent,
 5350                                prevent_auto_indent,
 5351                            } => {
 5352                                let auto_indent_mode =
 5353                                    buffer.language_settings_at(start, cx).auto_indent;
 5354                                let preserve_indent =
 5355                                    auto_indent_mode != language::AutoIndentMode::None;
 5356                                let apply_syntax_indent =
 5357                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5358                                let capacity_for_delimiter =
 5359                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5360                                let existing_indent_len = if preserve_indent {
 5361                                    existing_indent.len as usize
 5362                                } else {
 5363                                    0
 5364                                };
 5365                                let extra_line_len = extra_line_additional_indent
 5366                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5367                                    .unwrap_or(0);
 5368                                let mut new_text = String::with_capacity(
 5369                                    1 + capacity_for_delimiter
 5370                                        + existing_indent_len
 5371                                        + additional_indent.len as usize
 5372                                        + extra_line_len,
 5373                                );
 5374                                new_text.push('\n');
 5375                                if preserve_indent {
 5376                                    new_text.extend(existing_indent.chars());
 5377                                }
 5378                                new_text.extend(additional_indent.chars());
 5379                                if let Some(delimiter) = &delimiter {
 5380                                    new_text.push_str(delimiter);
 5381                                }
 5382                                if let Some(extra_indent) = extra_line_additional_indent {
 5383                                    new_text.push('\n');
 5384                                    if preserve_indent {
 5385                                        new_text.extend(existing_indent.chars());
 5386                                    }
 5387                                    new_text.extend(extra_indent.chars());
 5388                                }
 5389                                (
 5390                                    start,
 5391                                    new_text,
 5392                                    *prevent_auto_indent || !apply_syntax_indent,
 5393                                )
 5394                            }
 5395                        };
 5396
 5397                        let anchor = buffer.anchor_after(end);
 5398                        let new_selection = selection.map(|_| anchor);
 5399                        (
 5400                            ((edit_start..end, new_text), prevent_auto_indent),
 5401                            (newline_config.has_extra_line(), new_selection),
 5402                        )
 5403                    })
 5404                    .unzip()
 5405            };
 5406
 5407            let mut auto_indent_edits = Vec::new();
 5408            let mut edits = Vec::new();
 5409            for (edit, prevent_auto_indent) in edits_with_flags {
 5410                if prevent_auto_indent {
 5411                    edits.push(edit);
 5412                } else {
 5413                    auto_indent_edits.push(edit);
 5414                }
 5415            }
 5416            if !edits.is_empty() {
 5417                this.edit(edits, cx);
 5418            }
 5419            if !auto_indent_edits.is_empty() {
 5420                this.edit_with_autoindent(auto_indent_edits, cx);
 5421            }
 5422
 5423            let buffer = this.buffer.read(cx).snapshot(cx);
 5424            let new_selections = selection_info
 5425                .into_iter()
 5426                .map(|(extra_newline_inserted, new_selection)| {
 5427                    let mut cursor = new_selection.end.to_point(&buffer);
 5428                    if extra_newline_inserted {
 5429                        cursor.row -= 1;
 5430                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5431                    }
 5432                    new_selection.map(|_| cursor)
 5433                })
 5434                .collect();
 5435
 5436            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5437            this.refresh_edit_prediction(true, false, window, cx);
 5438            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5439                task.detach_and_log_err(cx);
 5440            }
 5441        });
 5442    }
 5443
 5444    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5445        if self.read_only(cx) {
 5446            return;
 5447        }
 5448
 5449        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5450
 5451        let buffer = self.buffer.read(cx);
 5452        let snapshot = buffer.snapshot(cx);
 5453
 5454        let mut edits = Vec::new();
 5455        let mut rows = Vec::new();
 5456
 5457        for (rows_inserted, selection) in self
 5458            .selections
 5459            .all_adjusted(&self.display_snapshot(cx))
 5460            .into_iter()
 5461            .enumerate()
 5462        {
 5463            let cursor = selection.head();
 5464            let row = cursor.row;
 5465
 5466            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5467
 5468            let newline = "\n".to_string();
 5469            edits.push((start_of_line..start_of_line, newline));
 5470
 5471            rows.push(row + rows_inserted as u32);
 5472        }
 5473
 5474        self.transact(window, cx, |editor, window, cx| {
 5475            editor.edit(edits, cx);
 5476
 5477            editor.change_selections(Default::default(), window, cx, |s| {
 5478                let mut index = 0;
 5479                s.move_cursors_with(&mut |map, _, _| {
 5480                    let row = rows[index];
 5481                    index += 1;
 5482
 5483                    let point = Point::new(row, 0);
 5484                    let boundary = map.next_line_boundary(point).1;
 5485                    let clipped = map.clip_point(boundary, Bias::Left);
 5486
 5487                    (clipped, SelectionGoal::None)
 5488                });
 5489            });
 5490
 5491            let mut indent_edits = Vec::new();
 5492            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5493            for row in rows {
 5494                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5495                for (row, indent) in indents {
 5496                    if indent.len == 0 {
 5497                        continue;
 5498                    }
 5499
 5500                    let text = match indent.kind {
 5501                        IndentKind::Space => " ".repeat(indent.len as usize),
 5502                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5503                    };
 5504                    let point = Point::new(row.0, 0);
 5505                    indent_edits.push((point..point, text));
 5506                }
 5507            }
 5508            editor.edit(indent_edits, cx);
 5509            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5510                format.detach_and_log_err(cx);
 5511            }
 5512        });
 5513    }
 5514
 5515    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5516        if self.read_only(cx) {
 5517            return;
 5518        }
 5519
 5520        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5521
 5522        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5523        let mut rows = Vec::new();
 5524        let mut rows_inserted = 0;
 5525
 5526        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5527            let cursor = selection.head();
 5528            let row = cursor.row;
 5529
 5530            let point = Point::new(row, 0);
 5531            let Some((buffer_handle, buffer_point, _)) =
 5532                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5533            else {
 5534                continue;
 5535            };
 5536
 5537            buffer_edits
 5538                .entry(buffer_handle.entity_id())
 5539                .or_insert_with(|| (buffer_handle, Vec::new()))
 5540                .1
 5541                .push(buffer_point);
 5542
 5543            rows_inserted += 1;
 5544            rows.push(row + rows_inserted);
 5545        }
 5546
 5547        self.transact(window, cx, |editor, window, cx| {
 5548            for (_, (buffer_handle, points)) in &buffer_edits {
 5549                buffer_handle.update(cx, |buffer, cx| {
 5550                    let edits: Vec<_> = points
 5551                        .iter()
 5552                        .map(|point| {
 5553                            let target = Point::new(point.row + 1, 0);
 5554                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5555                            (start_of_line..start_of_line, "\n")
 5556                        })
 5557                        .collect();
 5558                    buffer.edit(edits, None, cx);
 5559                });
 5560            }
 5561
 5562            editor.change_selections(Default::default(), window, cx, |s| {
 5563                let mut index = 0;
 5564                s.move_cursors_with(&mut |map, _, _| {
 5565                    let row = rows[index];
 5566                    index += 1;
 5567
 5568                    let point = Point::new(row, 0);
 5569                    let boundary = map.next_line_boundary(point).1;
 5570                    let clipped = map.clip_point(boundary, Bias::Left);
 5571
 5572                    (clipped, SelectionGoal::None)
 5573                });
 5574            });
 5575
 5576            let mut indent_edits = Vec::new();
 5577            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5578            for row in rows {
 5579                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5580                for (row, indent) in indents {
 5581                    if indent.len == 0 {
 5582                        continue;
 5583                    }
 5584
 5585                    let text = match indent.kind {
 5586                        IndentKind::Space => " ".repeat(indent.len as usize),
 5587                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5588                    };
 5589                    let point = Point::new(row.0, 0);
 5590                    indent_edits.push((point..point, text));
 5591                }
 5592            }
 5593            editor.edit(indent_edits, cx);
 5594            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5595                format.detach_and_log_err(cx);
 5596            }
 5597        });
 5598    }
 5599
 5600    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5601        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5602            original_indent_columns: Vec::new(),
 5603        });
 5604        self.replace_selections(text, autoindent, window, cx, false);
 5605    }
 5606
 5607    /// Replaces the editor's selections with the provided `text`, applying the
 5608    /// given `autoindent_mode` (`None` will skip autoindentation).
 5609    ///
 5610    /// Early returns if the editor is in read-only mode, without applying any
 5611    /// edits.
 5612    fn replace_selections(
 5613        &mut self,
 5614        text: &str,
 5615        autoindent_mode: Option<AutoindentMode>,
 5616        window: &mut Window,
 5617        cx: &mut Context<Self>,
 5618        apply_linked_edits: bool,
 5619    ) {
 5620        if self.read_only(cx) {
 5621            return;
 5622        }
 5623
 5624        let text: Arc<str> = text.into();
 5625        self.transact(window, cx, |this, window, cx| {
 5626            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5627            let linked_edits = if apply_linked_edits {
 5628                this.linked_edits_for_selections(text.clone(), cx)
 5629            } else {
 5630                LinkedEdits::new()
 5631            };
 5632
 5633            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5634                let anchors = {
 5635                    let snapshot = buffer.read(cx);
 5636                    old_selections
 5637                        .iter()
 5638                        .map(|s| {
 5639                            let anchor = snapshot.anchor_after(s.head());
 5640                            s.map(|_| anchor)
 5641                        })
 5642                        .collect::<Vec<_>>()
 5643                };
 5644                buffer.edit(
 5645                    old_selections
 5646                        .iter()
 5647                        .map(|s| (s.start..s.end, text.clone())),
 5648                    autoindent_mode,
 5649                    cx,
 5650                );
 5651                anchors
 5652            });
 5653
 5654            linked_edits.apply(cx);
 5655
 5656            this.change_selections(Default::default(), window, cx, |s| {
 5657                s.select_anchors(selection_anchors);
 5658            });
 5659
 5660            if apply_linked_edits {
 5661                refresh_linked_ranges(this, window, cx);
 5662            }
 5663
 5664            cx.notify();
 5665        });
 5666    }
 5667
 5668    /// Collects linked edits for the current selections, pairing each linked
 5669    /// range with `text`.
 5670    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5671        let mut linked_edits = LinkedEdits::new();
 5672        if !self.linked_edit_ranges.is_empty() {
 5673            for selection in self.selections.disjoint_anchors() {
 5674                let start = selection.start.text_anchor;
 5675                let end = selection.end.text_anchor;
 5676                linked_edits.push(self, start..end, text.clone(), cx);
 5677            }
 5678        }
 5679        linked_edits
 5680    }
 5681
 5682    /// Deletes the content covered by the current selections and applies
 5683    /// linked edits.
 5684    pub fn delete_selections_with_linked_edits(
 5685        &mut self,
 5686        window: &mut Window,
 5687        cx: &mut Context<Self>,
 5688    ) {
 5689        self.replace_selections("", None, window, cx, true);
 5690    }
 5691
 5692    #[cfg(any(test, feature = "test-support"))]
 5693    pub fn set_linked_edit_ranges_for_testing(
 5694        &mut self,
 5695        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5696        cx: &mut Context<Self>,
 5697    ) -> Option<()> {
 5698        let Some((buffer, _)) = self
 5699            .buffer
 5700            .read(cx)
 5701            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5702        else {
 5703            return None;
 5704        };
 5705        let buffer = buffer.read(cx);
 5706        let buffer_id = buffer.remote_id();
 5707        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5708        for (base_range, linked_ranges_points) in ranges {
 5709            let base_anchor =
 5710                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5711            let linked_anchors = linked_ranges_points
 5712                .into_iter()
 5713                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5714                .collect();
 5715            linked_ranges.push((base_anchor, linked_anchors));
 5716        }
 5717        let mut map = HashMap::default();
 5718        map.insert(buffer_id, linked_ranges);
 5719        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5720        Some(())
 5721    }
 5722
 5723    fn trigger_completion_on_input(
 5724        &mut self,
 5725        text: &str,
 5726        trigger_in_words: bool,
 5727        window: &mut Window,
 5728        cx: &mut Context<Self>,
 5729    ) {
 5730        let completions_source = self
 5731            .context_menu
 5732            .borrow()
 5733            .as_ref()
 5734            .and_then(|menu| match menu {
 5735                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5736                CodeContextMenu::CodeActions(_) => None,
 5737            });
 5738
 5739        match completions_source {
 5740            Some(CompletionsMenuSource::Words { .. }) => {
 5741                self.open_or_update_completions_menu(
 5742                    Some(CompletionsMenuSource::Words {
 5743                        ignore_threshold: false,
 5744                    }),
 5745                    None,
 5746                    trigger_in_words,
 5747                    window,
 5748                    cx,
 5749                );
 5750            }
 5751            _ => self.open_or_update_completions_menu(
 5752                None,
 5753                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5754                true,
 5755                window,
 5756                cx,
 5757            ),
 5758        }
 5759    }
 5760
 5761    /// If any empty selections is touching the start of its innermost containing autoclose
 5762    /// region, expand it to select the brackets.
 5763    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5764        let selections = self
 5765            .selections
 5766            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5767        let buffer = self.buffer.read(cx).read(cx);
 5768        let new_selections = self
 5769            .selections_with_autoclose_regions(selections, &buffer)
 5770            .map(|(mut selection, region)| {
 5771                if !selection.is_empty() {
 5772                    return selection;
 5773                }
 5774
 5775                if let Some(region) = region {
 5776                    let mut range = region.range.to_offset(&buffer);
 5777                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5778                        range.start -= region.pair.start.len();
 5779                        if buffer.contains_str_at(range.start, &region.pair.start)
 5780                            && buffer.contains_str_at(range.end, &region.pair.end)
 5781                        {
 5782                            range.end += region.pair.end.len();
 5783                            selection.start = range.start;
 5784                            selection.end = range.end;
 5785
 5786                            return selection;
 5787                        }
 5788                    }
 5789                }
 5790
 5791                let always_treat_brackets_as_autoclosed = buffer
 5792                    .language_settings_at(selection.start, cx)
 5793                    .always_treat_brackets_as_autoclosed;
 5794
 5795                if !always_treat_brackets_as_autoclosed {
 5796                    return selection;
 5797                }
 5798
 5799                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5800                    for (pair, enabled) in scope.brackets() {
 5801                        if !enabled || !pair.close {
 5802                            continue;
 5803                        }
 5804
 5805                        if buffer.contains_str_at(selection.start, &pair.end) {
 5806                            let pair_start_len = pair.start.len();
 5807                            if buffer.contains_str_at(
 5808                                selection.start.saturating_sub_usize(pair_start_len),
 5809                                &pair.start,
 5810                            ) {
 5811                                selection.start -= pair_start_len;
 5812                                selection.end += pair.end.len();
 5813
 5814                                return selection;
 5815                            }
 5816                        }
 5817                    }
 5818                }
 5819
 5820                selection
 5821            })
 5822            .collect();
 5823
 5824        drop(buffer);
 5825        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5826            selections.select(new_selections)
 5827        });
 5828    }
 5829
 5830    /// Iterate the given selections, and for each one, find the smallest surrounding
 5831    /// autoclose region. This uses the ordering of the selections and the autoclose
 5832    /// regions to avoid repeated comparisons.
 5833    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5834        &'a self,
 5835        selections: impl IntoIterator<Item = Selection<D>>,
 5836        buffer: &'a MultiBufferSnapshot,
 5837    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5838        let mut i = 0;
 5839        let mut regions = self.autoclose_regions.as_slice();
 5840        selections.into_iter().map(move |selection| {
 5841            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5842
 5843            let mut enclosing = None;
 5844            while let Some(pair_state) = regions.get(i) {
 5845                if pair_state.range.end.to_offset(buffer) < range.start {
 5846                    regions = &regions[i + 1..];
 5847                    i = 0;
 5848                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5849                    break;
 5850                } else {
 5851                    if pair_state.selection_id == selection.id {
 5852                        enclosing = Some(pair_state);
 5853                    }
 5854                    i += 1;
 5855                }
 5856            }
 5857
 5858            (selection, enclosing)
 5859        })
 5860    }
 5861
 5862    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5863    fn invalidate_autoclose_regions(
 5864        &mut self,
 5865        mut selections: &[Selection<Anchor>],
 5866        buffer: &MultiBufferSnapshot,
 5867    ) {
 5868        self.autoclose_regions.retain(|state| {
 5869            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5870                return false;
 5871            }
 5872
 5873            let mut i = 0;
 5874            while let Some(selection) = selections.get(i) {
 5875                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5876                    selections = &selections[1..];
 5877                    continue;
 5878                }
 5879                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5880                    break;
 5881                }
 5882                if selection.id == state.selection_id {
 5883                    return true;
 5884                } else {
 5885                    i += 1;
 5886                }
 5887            }
 5888            false
 5889        });
 5890    }
 5891
 5892    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5893        let offset = position.to_offset(buffer);
 5894        let (word_range, kind) =
 5895            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5896        if offset > word_range.start && kind == Some(CharKind::Word) {
 5897            Some(
 5898                buffer
 5899                    .text_for_range(word_range.start..offset)
 5900                    .collect::<String>(),
 5901            )
 5902        } else {
 5903            None
 5904        }
 5905    }
 5906
 5907    pub fn visible_excerpts(
 5908        &self,
 5909        lsp_related_only: bool,
 5910        cx: &mut Context<Editor>,
 5911    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5912        let project = self.project().cloned();
 5913        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5914        let multi_buffer = self.buffer().read(cx);
 5915        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5916        multi_buffer_snapshot
 5917            .range_to_buffer_ranges(
 5918                self.multi_buffer_visible_range(&display_snapshot, cx)
 5919                    .to_inclusive(),
 5920            )
 5921            .into_iter()
 5922            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5923            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5924                if !lsp_related_only {
 5925                    return Some((
 5926                        excerpt_id,
 5927                        (
 5928                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5929                            buffer.version().clone(),
 5930                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5931                        ),
 5932                    ));
 5933                }
 5934
 5935                let project = project.as_ref()?.read(cx);
 5936                let buffer_file = project::File::from_dyn(buffer.file())?;
 5937                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5938                let worktree_entry = buffer_worktree
 5939                    .read(cx)
 5940                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5941                if worktree_entry.is_ignored {
 5942                    None
 5943                } else {
 5944                    Some((
 5945                        excerpt_id,
 5946                        (
 5947                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5948                            buffer.version().clone(),
 5949                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5950                        ),
 5951                    ))
 5952                }
 5953            })
 5954            .collect()
 5955    }
 5956
 5957    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5958        TextLayoutDetails {
 5959            text_system: window.text_system().clone(),
 5960            editor_style: self.style.clone().unwrap(),
 5961            rem_size: window.rem_size(),
 5962            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5963            visible_rows: self.visible_line_count(),
 5964            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5965        }
 5966    }
 5967
 5968    fn trigger_on_type_formatting(
 5969        &self,
 5970        input: String,
 5971        window: &mut Window,
 5972        cx: &mut Context<Self>,
 5973    ) -> Option<Task<Result<()>>> {
 5974        if input.chars().count() != 1 {
 5975            return None;
 5976        }
 5977
 5978        let project = self.project()?;
 5979        let position = self.selections.newest_anchor().head();
 5980        let (buffer, buffer_position) = self
 5981            .buffer
 5982            .read(cx)
 5983            .text_anchor_for_position(position, cx)?;
 5984
 5985        let settings = LanguageSettings::for_buffer_at(&buffer.read(cx), buffer_position, cx);
 5986        if !settings.use_on_type_format {
 5987            return None;
 5988        }
 5989
 5990        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5991        // hence we do LSP request & edit on host side only — add formats to host's history.
 5992        let push_to_lsp_host_history = true;
 5993        // If this is not the host, append its history with new edits.
 5994        let push_to_client_history = project.read(cx).is_via_collab();
 5995
 5996        let on_type_formatting = project.update(cx, |project, cx| {
 5997            project.on_type_format(
 5998                buffer.clone(),
 5999                buffer_position,
 6000                input,
 6001                push_to_lsp_host_history,
 6002                cx,
 6003            )
 6004        });
 6005        Some(cx.spawn_in(window, async move |editor, cx| {
 6006            if let Some(transaction) = on_type_formatting.await? {
 6007                if push_to_client_history {
 6008                    buffer.update(cx, |buffer, _| {
 6009                        buffer.push_transaction(transaction, Instant::now());
 6010                        buffer.finalize_last_transaction();
 6011                    });
 6012                }
 6013                editor.update(cx, |editor, cx| {
 6014                    editor.refresh_document_highlights(cx);
 6015                })?;
 6016            }
 6017            Ok(())
 6018        }))
 6019    }
 6020
 6021    pub fn show_word_completions(
 6022        &mut self,
 6023        _: &ShowWordCompletions,
 6024        window: &mut Window,
 6025        cx: &mut Context<Self>,
 6026    ) {
 6027        self.open_or_update_completions_menu(
 6028            Some(CompletionsMenuSource::Words {
 6029                ignore_threshold: true,
 6030            }),
 6031            None,
 6032            false,
 6033            window,
 6034            cx,
 6035        );
 6036    }
 6037
 6038    pub fn show_completions(
 6039        &mut self,
 6040        _: &ShowCompletions,
 6041        window: &mut Window,
 6042        cx: &mut Context<Self>,
 6043    ) {
 6044        self.open_or_update_completions_menu(None, None, false, window, cx);
 6045    }
 6046
 6047    fn open_or_update_completions_menu(
 6048        &mut self,
 6049        requested_source: Option<CompletionsMenuSource>,
 6050        trigger: Option<String>,
 6051        trigger_in_words: bool,
 6052        window: &mut Window,
 6053        cx: &mut Context<Self>,
 6054    ) {
 6055        if self.pending_rename.is_some() {
 6056            return;
 6057        }
 6058
 6059        let completions_source = self
 6060            .context_menu
 6061            .borrow()
 6062            .as_ref()
 6063            .and_then(|menu| match menu {
 6064                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 6065                CodeContextMenu::CodeActions(_) => None,
 6066            });
 6067
 6068        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 6069
 6070        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 6071        // inserted and selected. To handle that case, the start of the selection is used so that
 6072        // the menu starts with all choices.
 6073        let position = self
 6074            .selections
 6075            .newest_anchor()
 6076            .start
 6077            .bias_right(&multibuffer_snapshot);
 6078        if position.diff_base_anchor.is_some() {
 6079            return;
 6080        }
 6081        let buffer_position = multibuffer_snapshot.anchor_before(position);
 6082        let Some(buffer) = buffer_position
 6083            .text_anchor
 6084            .buffer_id
 6085            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 6086        else {
 6087            return;
 6088        };
 6089        let buffer_snapshot = buffer.read(cx).snapshot();
 6090
 6091        let menu_is_open = matches!(
 6092            self.context_menu.borrow().as_ref(),
 6093            Some(CodeContextMenu::Completions(_))
 6094        );
 6095
 6096        let language = buffer_snapshot
 6097            .language_at(buffer_position.text_anchor)
 6098            .map(|language| language.name());
 6099        let language_settings = multibuffer_snapshot.language_settings_at(buffer_position, cx);
 6100        let completion_settings = language_settings.completions.clone();
 6101
 6102        let show_completions_on_input = self
 6103            .show_completions_on_input_override
 6104            .unwrap_or(language_settings.show_completions_on_input);
 6105        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 6106            return;
 6107        }
 6108
 6109        let query: Option<Arc<String>> =
 6110            Self::completion_query(&multibuffer_snapshot, buffer_position)
 6111                .map(|query| query.into());
 6112
 6113        drop(multibuffer_snapshot);
 6114
 6115        // Hide the current completions menu when query is empty. Without this, cached
 6116        // completions from before the trigger char may be reused (#32774).
 6117        if query.is_none() && menu_is_open {
 6118            self.hide_context_menu(window, cx);
 6119        }
 6120
 6121        let mut ignore_word_threshold = false;
 6122        let provider = match requested_source {
 6123            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 6124            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 6125                ignore_word_threshold = ignore_threshold;
 6126                None
 6127            }
 6128            Some(CompletionsMenuSource::SnippetChoices)
 6129            | Some(CompletionsMenuSource::SnippetsOnly) => {
 6130                log::error!("bug: SnippetChoices requested_source is not handled");
 6131                None
 6132            }
 6133        };
 6134
 6135        let sort_completions = provider
 6136            .as_ref()
 6137            .is_some_and(|provider| provider.sort_completions());
 6138
 6139        let filter_completions = provider
 6140            .as_ref()
 6141            .is_none_or(|provider| provider.filter_completions());
 6142
 6143        let was_snippets_only = matches!(
 6144            completions_source,
 6145            Some(CompletionsMenuSource::SnippetsOnly)
 6146        );
 6147
 6148        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6149            if filter_completions {
 6150                menu.filter(
 6151                    query.clone().unwrap_or_default(),
 6152                    buffer_position.text_anchor,
 6153                    &buffer,
 6154                    provider.clone(),
 6155                    window,
 6156                    cx,
 6157                );
 6158            }
 6159            // When `is_incomplete` is false, no need to re-query completions when the current query
 6160            // is a suffix of the initial query.
 6161            let was_complete = !menu.is_incomplete;
 6162            if was_complete && !was_snippets_only {
 6163                // If the new query is a suffix of the old query (typing more characters) and
 6164                // the previous result was complete, the existing completions can be filtered.
 6165                //
 6166                // Note that snippet completions are always complete.
 6167                let query_matches = match (&menu.initial_query, &query) {
 6168                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6169                    (None, _) => true,
 6170                    _ => false,
 6171                };
 6172                if query_matches {
 6173                    let position_matches = if menu.initial_position == position {
 6174                        true
 6175                    } else {
 6176                        let snapshot = self.buffer.read(cx).read(cx);
 6177                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6178                    };
 6179                    if position_matches {
 6180                        return;
 6181                    }
 6182                }
 6183            }
 6184        };
 6185
 6186        let Anchor {
 6187            excerpt_id: buffer_excerpt_id,
 6188            text_anchor: buffer_position,
 6189            ..
 6190        } = buffer_position;
 6191
 6192        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6193            buffer_snapshot.surrounding_word(buffer_position, None)
 6194        {
 6195            let word_to_exclude = buffer_snapshot
 6196                .text_for_range(word_range.clone())
 6197                .collect::<String>();
 6198            (
 6199                buffer_snapshot.anchor_before(word_range.start)
 6200                    ..buffer_snapshot.anchor_after(buffer_position),
 6201                Some(word_to_exclude),
 6202            )
 6203        } else {
 6204            (buffer_position..buffer_position, None)
 6205        };
 6206
 6207        let show_completion_documentation = buffer_snapshot
 6208            .settings_at(buffer_position, cx)
 6209            .show_completion_documentation;
 6210
 6211        // The document can be large, so stay in reasonable bounds when searching for words,
 6212        // otherwise completion pop-up might be slow to appear.
 6213        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6214        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6215        let min_word_search = buffer_snapshot.clip_point(
 6216            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6217            Bias::Left,
 6218        );
 6219        let max_word_search = buffer_snapshot.clip_point(
 6220            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6221            Bias::Right,
 6222        );
 6223        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6224            ..buffer_snapshot.point_to_offset(max_word_search);
 6225
 6226        let skip_digits = query
 6227            .as_ref()
 6228            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6229
 6230        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6231            trigger.as_ref().is_none_or(|trigger| {
 6232                provider.is_completion_trigger(
 6233                    &buffer,
 6234                    position.text_anchor,
 6235                    trigger,
 6236                    trigger_in_words,
 6237                    cx,
 6238                )
 6239            })
 6240        });
 6241
 6242        let provider_responses = if let Some(provider) = &provider
 6243            && load_provider_completions
 6244        {
 6245            let trigger_character =
 6246                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6247            let completion_context = CompletionContext {
 6248                trigger_kind: match &trigger_character {
 6249                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6250                    None => CompletionTriggerKind::INVOKED,
 6251                },
 6252                trigger_character,
 6253            };
 6254
 6255            provider.completions(
 6256                buffer_excerpt_id,
 6257                &buffer,
 6258                buffer_position,
 6259                completion_context,
 6260                window,
 6261                cx,
 6262            )
 6263        } else {
 6264            Task::ready(Ok(Vec::new()))
 6265        };
 6266
 6267        let load_word_completions = if !self.word_completions_enabled {
 6268            false
 6269        } else if requested_source
 6270            == Some(CompletionsMenuSource::Words {
 6271                ignore_threshold: true,
 6272            })
 6273        {
 6274            true
 6275        } else {
 6276            load_provider_completions
 6277                && completion_settings.words != WordsCompletionMode::Disabled
 6278                && (ignore_word_threshold || {
 6279                    let words_min_length = completion_settings.words_min_length;
 6280                    // check whether word has at least `words_min_length` characters
 6281                    let query_chars = query.iter().flat_map(|q| q.chars());
 6282                    query_chars.take(words_min_length).count() == words_min_length
 6283                })
 6284        };
 6285
 6286        let mut words = if load_word_completions {
 6287            cx.background_spawn({
 6288                let buffer_snapshot = buffer_snapshot.clone();
 6289                async move {
 6290                    buffer_snapshot.words_in_range(WordsQuery {
 6291                        fuzzy_contents: None,
 6292                        range: word_search_range,
 6293                        skip_digits,
 6294                    })
 6295                }
 6296            })
 6297        } else {
 6298            Task::ready(BTreeMap::default())
 6299        };
 6300
 6301        let snippets = if let Some(provider) = &provider
 6302            && provider.show_snippets()
 6303            && let Some(project) = self.project()
 6304        {
 6305            let char_classifier = buffer_snapshot
 6306                .char_classifier_at(buffer_position)
 6307                .scope_context(Some(CharScopeContext::Completion));
 6308            project.update(cx, |project, cx| {
 6309                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6310            })
 6311        } else {
 6312            Task::ready(Ok(CompletionResponse {
 6313                completions: Vec::new(),
 6314                display_options: Default::default(),
 6315                is_incomplete: false,
 6316            }))
 6317        };
 6318
 6319        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6320
 6321        let id = post_inc(&mut self.next_completion_id);
 6322        let task = cx.spawn_in(window, async move |editor, cx| {
 6323            let Ok(()) = editor.update(cx, |this, _| {
 6324                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6325            }) else {
 6326                return;
 6327            };
 6328
 6329            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6330            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6331            let mut completions = Vec::new();
 6332            let mut is_incomplete = false;
 6333            let mut display_options: Option<CompletionDisplayOptions> = None;
 6334            if let Some(provider_responses) = provider_responses.await.log_err()
 6335                && !provider_responses.is_empty()
 6336            {
 6337                for response in provider_responses {
 6338                    completions.extend(response.completions);
 6339                    is_incomplete = is_incomplete || response.is_incomplete;
 6340                    match display_options.as_mut() {
 6341                        None => {
 6342                            display_options = Some(response.display_options);
 6343                        }
 6344                        Some(options) => options.merge(&response.display_options),
 6345                    }
 6346                }
 6347                if completion_settings.words == WordsCompletionMode::Fallback {
 6348                    words = Task::ready(BTreeMap::default());
 6349                }
 6350            }
 6351            let display_options = display_options.unwrap_or_default();
 6352
 6353            let mut words = words.await;
 6354            if let Some(word_to_exclude) = &word_to_exclude {
 6355                words.remove(word_to_exclude);
 6356            }
 6357            for lsp_completion in &completions {
 6358                words.remove(&lsp_completion.new_text);
 6359            }
 6360            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6361                replace_range: word_replace_range.clone(),
 6362                new_text: word.clone(),
 6363                label: CodeLabel::plain(word, None),
 6364                match_start: None,
 6365                snippet_deduplication_key: None,
 6366                icon_path: None,
 6367                documentation: None,
 6368                source: CompletionSource::BufferWord {
 6369                    word_range,
 6370                    resolved: false,
 6371                },
 6372                insert_text_mode: Some(InsertTextMode::AS_IS),
 6373                confirm: None,
 6374            }));
 6375
 6376            completions.extend(
 6377                snippets
 6378                    .await
 6379                    .into_iter()
 6380                    .flat_map(|response| response.completions),
 6381            );
 6382
 6383            let menu = if completions.is_empty() {
 6384                None
 6385            } else {
 6386                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6387                    let languages = editor
 6388                        .workspace
 6389                        .as_ref()
 6390                        .and_then(|(workspace, _)| workspace.upgrade())
 6391                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6392                    let menu = CompletionsMenu::new(
 6393                        id,
 6394                        requested_source.unwrap_or(if load_provider_completions {
 6395                            CompletionsMenuSource::Normal
 6396                        } else {
 6397                            CompletionsMenuSource::SnippetsOnly
 6398                        }),
 6399                        sort_completions,
 6400                        show_completion_documentation,
 6401                        position,
 6402                        query.clone(),
 6403                        is_incomplete,
 6404                        buffer.clone(),
 6405                        completions.into(),
 6406                        editor
 6407                            .context_menu()
 6408                            .borrow_mut()
 6409                            .as_ref()
 6410                            .map(|menu| menu.primary_scroll_handle()),
 6411                        display_options,
 6412                        snippet_sort_order,
 6413                        languages,
 6414                        language,
 6415                        cx,
 6416                    );
 6417
 6418                    let query = if filter_completions { query } else { None };
 6419                    let matches_task = menu.do_async_filtering(
 6420                        query.unwrap_or_default(),
 6421                        buffer_position,
 6422                        &buffer,
 6423                        cx,
 6424                    );
 6425                    (menu, matches_task)
 6426                }) else {
 6427                    return;
 6428                };
 6429
 6430                let matches = matches_task.await;
 6431
 6432                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6433                    // Newer menu already set, so exit.
 6434                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6435                        editor.context_menu.borrow().as_ref()
 6436                        && prev_menu.id > id
 6437                    {
 6438                        return;
 6439                    };
 6440
 6441                    // Only valid to take prev_menu because either the new menu is immediately set
 6442                    // below, or the menu is hidden.
 6443                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6444                        editor.context_menu.borrow_mut().take()
 6445                    {
 6446                        let position_matches =
 6447                            if prev_menu.initial_position == menu.initial_position {
 6448                                true
 6449                            } else {
 6450                                let snapshot = editor.buffer.read(cx).read(cx);
 6451                                prev_menu.initial_position.to_offset(&snapshot)
 6452                                    == menu.initial_position.to_offset(&snapshot)
 6453                            };
 6454                        if position_matches {
 6455                            // Preserve markdown cache before `set_filter_results` because it will
 6456                            // try to populate the documentation cache.
 6457                            menu.preserve_markdown_cache(prev_menu);
 6458                        }
 6459                    };
 6460
 6461                    menu.set_filter_results(matches, provider, window, cx);
 6462                }) else {
 6463                    return;
 6464                };
 6465
 6466                menu.visible().then_some(menu)
 6467            };
 6468
 6469            editor
 6470                .update_in(cx, |editor, window, cx| {
 6471                    if editor.focus_handle.is_focused(window)
 6472                        && let Some(menu) = menu
 6473                    {
 6474                        *editor.context_menu.borrow_mut() =
 6475                            Some(CodeContextMenu::Completions(menu));
 6476
 6477                        crate::hover_popover::hide_hover(editor, cx);
 6478                        if editor.show_edit_predictions_in_menu() {
 6479                            editor.update_visible_edit_prediction(window, cx);
 6480                        } else {
 6481                            editor
 6482                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6483                        }
 6484
 6485                        cx.notify();
 6486                        return;
 6487                    }
 6488
 6489                    if editor.completion_tasks.len() <= 1 {
 6490                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6491                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6492                        // If it was already hidden and we don't show edit predictions in the menu,
 6493                        // we should also show the edit prediction when available.
 6494                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6495                            editor.update_visible_edit_prediction(window, cx);
 6496                        }
 6497                    }
 6498                })
 6499                .ok();
 6500        });
 6501
 6502        self.completion_tasks.push((id, task));
 6503    }
 6504
 6505    #[cfg(any(test, feature = "test-support"))]
 6506    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6507        let menu = self.context_menu.borrow();
 6508        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6509            let completions = menu.completions.borrow();
 6510            Some(completions.to_vec())
 6511        } else {
 6512            None
 6513        }
 6514    }
 6515
 6516    pub fn with_completions_menu_matching_id<R>(
 6517        &self,
 6518        id: CompletionId,
 6519        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6520    ) -> R {
 6521        let mut context_menu = self.context_menu.borrow_mut();
 6522        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6523            return f(None);
 6524        };
 6525        if completions_menu.id != id {
 6526            return f(None);
 6527        }
 6528        f(Some(completions_menu))
 6529    }
 6530
 6531    pub fn confirm_completion(
 6532        &mut self,
 6533        action: &ConfirmCompletion,
 6534        window: &mut Window,
 6535        cx: &mut Context<Self>,
 6536    ) -> Option<Task<Result<()>>> {
 6537        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6538        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6539    }
 6540
 6541    pub fn confirm_completion_insert(
 6542        &mut self,
 6543        _: &ConfirmCompletionInsert,
 6544        window: &mut Window,
 6545        cx: &mut Context<Self>,
 6546    ) -> Option<Task<Result<()>>> {
 6547        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6548        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6549    }
 6550
 6551    pub fn confirm_completion_replace(
 6552        &mut self,
 6553        _: &ConfirmCompletionReplace,
 6554        window: &mut Window,
 6555        cx: &mut Context<Self>,
 6556    ) -> Option<Task<Result<()>>> {
 6557        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6558        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6559    }
 6560
 6561    pub fn compose_completion(
 6562        &mut self,
 6563        action: &ComposeCompletion,
 6564        window: &mut Window,
 6565        cx: &mut Context<Self>,
 6566    ) -> Option<Task<Result<()>>> {
 6567        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6568        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6569    }
 6570
 6571    fn do_completion(
 6572        &mut self,
 6573        item_ix: Option<usize>,
 6574        intent: CompletionIntent,
 6575        window: &mut Window,
 6576        cx: &mut Context<Editor>,
 6577    ) -> Option<Task<Result<()>>> {
 6578        use language::ToOffset as _;
 6579
 6580        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6581        else {
 6582            return None;
 6583        };
 6584
 6585        let candidate_id = {
 6586            let entries = completions_menu.entries.borrow();
 6587            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6588            if self.show_edit_predictions_in_menu() {
 6589                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6590            }
 6591            mat.candidate_id
 6592        };
 6593
 6594        let completion = completions_menu
 6595            .completions
 6596            .borrow()
 6597            .get(candidate_id)?
 6598            .clone();
 6599        cx.stop_propagation();
 6600
 6601        let buffer_handle = completions_menu.buffer.clone();
 6602
 6603        let CompletionEdit {
 6604            new_text,
 6605            snippet,
 6606            replace_range,
 6607        } = process_completion_for_edit(
 6608            &completion,
 6609            intent,
 6610            &buffer_handle,
 6611            &completions_menu.initial_position.text_anchor,
 6612            cx,
 6613        );
 6614
 6615        let buffer = buffer_handle.read(cx);
 6616        let snapshot = self.buffer.read(cx).snapshot(cx);
 6617        let newest_anchor = self.selections.newest_anchor();
 6618        let replace_range_multibuffer = {
 6619            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6620            excerpt.map_range_from_buffer(replace_range.clone())
 6621        };
 6622        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6623            return None;
 6624        }
 6625
 6626        let old_text = buffer
 6627            .text_for_range(replace_range.clone())
 6628            .collect::<String>();
 6629        let lookbehind = newest_anchor
 6630            .start
 6631            .text_anchor
 6632            .to_offset(buffer)
 6633            .saturating_sub(replace_range.start.0);
 6634        let lookahead = replace_range
 6635            .end
 6636            .0
 6637            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6638        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6639        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6640
 6641        let selections = self
 6642            .selections
 6643            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6644        let mut ranges = Vec::new();
 6645        let mut all_commit_ranges = Vec::new();
 6646        let mut linked_edits = LinkedEdits::new();
 6647
 6648        let text: Arc<str> = new_text.clone().into();
 6649        for selection in &selections {
 6650            let range = if selection.id == newest_anchor.id {
 6651                replace_range_multibuffer.clone()
 6652            } else {
 6653                let mut range = selection.range();
 6654
 6655                // if prefix is present, don't duplicate it
 6656                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6657                    range.start = range.start.saturating_sub_usize(lookbehind);
 6658
 6659                    // if suffix is also present, mimic the newest cursor and replace it
 6660                    if selection.id != newest_anchor.id
 6661                        && snapshot.contains_str_at(range.end, suffix)
 6662                    {
 6663                        range.end += lookahead;
 6664                    }
 6665                }
 6666                range
 6667            };
 6668
 6669            ranges.push(range.clone());
 6670
 6671            let start_anchor = snapshot.anchor_before(range.start);
 6672            let end_anchor = snapshot.anchor_after(range.end);
 6673            let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6674            all_commit_ranges.push(anchor_range.clone());
 6675
 6676            if !self.linked_edit_ranges.is_empty() {
 6677                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6678            }
 6679        }
 6680
 6681        let common_prefix_len = old_text
 6682            .chars()
 6683            .zip(new_text.chars())
 6684            .take_while(|(a, b)| a == b)
 6685            .map(|(a, _)| a.len_utf8())
 6686            .sum::<usize>();
 6687
 6688        cx.emit(EditorEvent::InputHandled {
 6689            utf16_range_to_replace: None,
 6690            text: new_text[common_prefix_len..].into(),
 6691        });
 6692
 6693        self.transact(window, cx, |editor, window, cx| {
 6694            if let Some(mut snippet) = snippet {
 6695                snippet.text = new_text.to_string();
 6696                editor
 6697                    .insert_snippet(&ranges, snippet, window, cx)
 6698                    .log_err();
 6699            } else {
 6700                editor.buffer.update(cx, |multi_buffer, cx| {
 6701                    let auto_indent = match completion.insert_text_mode {
 6702                        Some(InsertTextMode::AS_IS) => None,
 6703                        _ => editor.autoindent_mode.clone(),
 6704                    };
 6705                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6706                    multi_buffer.edit(edits, auto_indent, cx);
 6707                });
 6708            }
 6709            linked_edits.apply(cx);
 6710            editor.refresh_edit_prediction(true, false, window, cx);
 6711        });
 6712        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6713
 6714        let show_new_completions_on_confirm = completion
 6715            .confirm
 6716            .as_ref()
 6717            .is_some_and(|confirm| confirm(intent, window, cx));
 6718        if show_new_completions_on_confirm {
 6719            self.open_or_update_completions_menu(None, None, false, window, cx);
 6720        }
 6721
 6722        let provider = self.completion_provider.as_ref()?;
 6723
 6724        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6725        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6726            let CompletionSource::Lsp {
 6727                lsp_completion,
 6728                server_id,
 6729                ..
 6730            } = &completion.source
 6731            else {
 6732                return None;
 6733            };
 6734            let lsp_command = lsp_completion.command.as_ref()?;
 6735            let available_commands = lsp_store
 6736                .read(cx)
 6737                .lsp_server_capabilities
 6738                .get(server_id)
 6739                .and_then(|server_capabilities| {
 6740                    server_capabilities
 6741                        .execute_command_provider
 6742                        .as_ref()
 6743                        .map(|options| options.commands.as_slice())
 6744                })?;
 6745            if available_commands.contains(&lsp_command.command) {
 6746                Some(CodeAction {
 6747                    server_id: *server_id,
 6748                    range: language::Anchor::MIN..language::Anchor::MIN,
 6749                    lsp_action: LspAction::Command(lsp_command.clone()),
 6750                    resolved: false,
 6751                })
 6752            } else {
 6753                None
 6754            }
 6755        });
 6756
 6757        drop(completion);
 6758        let apply_edits = provider.apply_additional_edits_for_completion(
 6759            buffer_handle.clone(),
 6760            completions_menu.completions.clone(),
 6761            candidate_id,
 6762            true,
 6763            all_commit_ranges,
 6764            cx,
 6765        );
 6766
 6767        let editor_settings = EditorSettings::get_global(cx);
 6768        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6769            // After the code completion is finished, users often want to know what signatures are needed.
 6770            // so we should automatically call signature_help
 6771            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6772        }
 6773
 6774        Some(cx.spawn_in(window, async move |editor, cx| {
 6775            apply_edits.await?;
 6776
 6777            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6778                let title = command.lsp_action.title().to_owned();
 6779                let project_transaction = lsp_store
 6780                    .update(cx, |lsp_store, cx| {
 6781                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6782                    })
 6783                    .await
 6784                    .context("applying post-completion command")?;
 6785                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6786                    Self::open_project_transaction(
 6787                        &editor,
 6788                        workspace.downgrade(),
 6789                        project_transaction,
 6790                        title,
 6791                        cx,
 6792                    )
 6793                    .await?;
 6794                }
 6795            }
 6796
 6797            Ok(())
 6798        }))
 6799    }
 6800
 6801    pub fn toggle_code_actions(
 6802        &mut self,
 6803        action: &ToggleCodeActions,
 6804        window: &mut Window,
 6805        cx: &mut Context<Self>,
 6806    ) {
 6807        let quick_launch = action.quick_launch;
 6808        let mut context_menu = self.context_menu.borrow_mut();
 6809        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6810            if code_actions.deployed_from == action.deployed_from {
 6811                // Toggle if we're selecting the same one
 6812                *context_menu = None;
 6813                cx.notify();
 6814                return;
 6815            } else {
 6816                // Otherwise, clear it and start a new one
 6817                *context_menu = None;
 6818                cx.notify();
 6819            }
 6820        }
 6821        drop(context_menu);
 6822        let snapshot = self.snapshot(window, cx);
 6823        let deployed_from = action.deployed_from.clone();
 6824        let action = action.clone();
 6825        self.completion_tasks.clear();
 6826        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6827
 6828        let multibuffer_point = match &action.deployed_from {
 6829            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6830                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6831            }
 6832            _ => self
 6833                .selections
 6834                .newest::<Point>(&snapshot.display_snapshot)
 6835                .head(),
 6836        };
 6837        let Some((buffer, buffer_row)) = snapshot
 6838            .buffer_snapshot()
 6839            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6840            .and_then(|(buffer_snapshot, range)| {
 6841                self.buffer()
 6842                    .read(cx)
 6843                    .buffer(buffer_snapshot.remote_id())
 6844                    .map(|buffer| (buffer, range.start.row))
 6845            })
 6846        else {
 6847            return;
 6848        };
 6849        let buffer_id = buffer.read(cx).remote_id();
 6850        let tasks = self
 6851            .runnables
 6852            .runnables((buffer_id, buffer_row))
 6853            .map(|t| Arc::new(t.to_owned()));
 6854
 6855        if !self.focus_handle.is_focused(window) {
 6856            return;
 6857        }
 6858        let project = self.project.clone();
 6859
 6860        let code_actions_task = match deployed_from {
 6861            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6862            _ => self.code_actions(buffer_row, window, cx),
 6863        };
 6864
 6865        let runnable_task = match deployed_from {
 6866            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6867            _ => {
 6868                let mut task_context_task = Task::ready(None);
 6869                if let Some(tasks) = &tasks
 6870                    && let Some(project) = project
 6871                {
 6872                    task_context_task =
 6873                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6874                }
 6875
 6876                cx.spawn_in(window, {
 6877                    let buffer = buffer.clone();
 6878                    async move |editor, cx| {
 6879                        let task_context = task_context_task.await;
 6880
 6881                        let resolved_tasks =
 6882                            tasks
 6883                                .zip(task_context.clone())
 6884                                .map(|(tasks, task_context)| ResolvedTasks {
 6885                                    templates: tasks.resolve(&task_context).collect(),
 6886                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6887                                        multibuffer_point.row,
 6888                                        tasks.column,
 6889                                    )),
 6890                                });
 6891                        let debug_scenarios = editor
 6892                            .update(cx, |editor, cx| {
 6893                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6894                            })?
 6895                            .await;
 6896                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6897                    }
 6898                })
 6899            }
 6900        };
 6901
 6902        cx.spawn_in(window, async move |editor, cx| {
 6903            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6904            let code_actions = code_actions_task.await;
 6905            let spawn_straight_away = quick_launch
 6906                && resolved_tasks
 6907                    .as_ref()
 6908                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6909                && code_actions
 6910                    .as_ref()
 6911                    .is_none_or(|actions| actions.is_empty())
 6912                && debug_scenarios.is_empty();
 6913
 6914            editor.update_in(cx, |editor, window, cx| {
 6915                crate::hover_popover::hide_hover(editor, cx);
 6916                let actions = CodeActionContents::new(
 6917                    resolved_tasks,
 6918                    code_actions,
 6919                    debug_scenarios,
 6920                    task_context.unwrap_or_default(),
 6921                );
 6922
 6923                // Don't show the menu if there are no actions available
 6924                if actions.is_empty() {
 6925                    cx.notify();
 6926                    return Task::ready(Ok(()));
 6927                }
 6928
 6929                *editor.context_menu.borrow_mut() =
 6930                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6931                        buffer,
 6932                        actions,
 6933                        selected_item: Default::default(),
 6934                        scroll_handle: UniformListScrollHandle::default(),
 6935                        deployed_from,
 6936                    }));
 6937                cx.notify();
 6938                if spawn_straight_away
 6939                    && let Some(task) = editor.confirm_code_action(
 6940                        &ConfirmCodeAction { item_ix: Some(0) },
 6941                        window,
 6942                        cx,
 6943                    )
 6944                {
 6945                    return task;
 6946                }
 6947
 6948                Task::ready(Ok(()))
 6949            })
 6950        })
 6951        .detach_and_log_err(cx);
 6952    }
 6953
 6954    fn debug_scenarios(
 6955        &mut self,
 6956        resolved_tasks: &Option<ResolvedTasks>,
 6957        buffer: &Entity<Buffer>,
 6958        cx: &mut App,
 6959    ) -> Task<Vec<task::DebugScenario>> {
 6960        maybe!({
 6961            let project = self.project()?;
 6962            let dap_store = project.read(cx).dap_store();
 6963            let mut scenarios = vec![];
 6964            let resolved_tasks = resolved_tasks.as_ref()?;
 6965            let buffer = buffer.read(cx);
 6966            let language = buffer.language()?;
 6967            let debug_adapter = LanguageSettings::for_buffer(&buffer, cx)
 6968                .debuggers
 6969                .first()
 6970                .map(SharedString::from)
 6971                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6972
 6973            dap_store.update(cx, |dap_store, cx| {
 6974                for (_, task) in &resolved_tasks.templates {
 6975                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6976                        task.original_task().clone(),
 6977                        debug_adapter.clone().into(),
 6978                        task.display_label().to_owned().into(),
 6979                        cx,
 6980                    );
 6981                    scenarios.push(maybe_scenario);
 6982                }
 6983            });
 6984            Some(cx.background_spawn(async move {
 6985                futures::future::join_all(scenarios)
 6986                    .await
 6987                    .into_iter()
 6988                    .flatten()
 6989                    .collect::<Vec<_>>()
 6990            }))
 6991        })
 6992        .unwrap_or_else(|| Task::ready(vec![]))
 6993    }
 6994
 6995    fn code_actions(
 6996        &mut self,
 6997        buffer_row: u32,
 6998        window: &mut Window,
 6999        cx: &mut Context<Self>,
 7000    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 7001        let mut task = self.code_actions_task.take();
 7002        cx.spawn_in(window, async move |editor, cx| {
 7003            while let Some(prev_task) = task {
 7004                prev_task.await.log_err();
 7005                task = editor
 7006                    .update(cx, |this, _| this.code_actions_task.take())
 7007                    .ok()?;
 7008            }
 7009
 7010            editor
 7011                .update(cx, |editor, cx| {
 7012                    editor
 7013                        .available_code_actions
 7014                        .clone()
 7015                        .and_then(|(location, code_actions)| {
 7016                            let snapshot = location.buffer.read(cx).snapshot();
 7017                            let point_range = location.range.to_point(&snapshot);
 7018                            let point_range = point_range.start.row..=point_range.end.row;
 7019                            if point_range.contains(&buffer_row) {
 7020                                Some(code_actions)
 7021                            } else {
 7022                                None
 7023                            }
 7024                        })
 7025                })
 7026                .ok()
 7027                .flatten()
 7028        })
 7029    }
 7030
 7031    pub fn confirm_code_action(
 7032        &mut self,
 7033        action: &ConfirmCodeAction,
 7034        window: &mut Window,
 7035        cx: &mut Context<Self>,
 7036    ) -> Option<Task<Result<()>>> {
 7037        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 7038
 7039        let actions_menu =
 7040            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 7041                menu
 7042            } else {
 7043                return None;
 7044            };
 7045
 7046        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 7047        let action = actions_menu.actions.get(action_ix)?;
 7048        let title = action.label();
 7049        let buffer = actions_menu.buffer;
 7050        let workspace = self.workspace()?;
 7051
 7052        match action {
 7053            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 7054                workspace.update(cx, |workspace, cx| {
 7055                    workspace.schedule_resolved_task(
 7056                        task_source_kind,
 7057                        resolved_task,
 7058                        false,
 7059                        window,
 7060                        cx,
 7061                    );
 7062
 7063                    Some(Task::ready(Ok(())))
 7064                })
 7065            }
 7066            CodeActionsItem::CodeAction {
 7067                excerpt_id,
 7068                action,
 7069                provider,
 7070            } => {
 7071                let apply_code_action =
 7072                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 7073                let workspace = workspace.downgrade();
 7074                Some(cx.spawn_in(window, async move |editor, cx| {
 7075                    let project_transaction = apply_code_action.await?;
 7076                    Self::open_project_transaction(
 7077                        &editor,
 7078                        workspace,
 7079                        project_transaction,
 7080                        title,
 7081                        cx,
 7082                    )
 7083                    .await
 7084                }))
 7085            }
 7086            CodeActionsItem::DebugScenario(scenario) => {
 7087                let context = actions_menu.actions.context.into();
 7088
 7089                workspace.update(cx, |workspace, cx| {
 7090                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 7091                    workspace.start_debug_session(
 7092                        scenario,
 7093                        context,
 7094                        Some(buffer),
 7095                        None,
 7096                        window,
 7097                        cx,
 7098                    );
 7099                });
 7100                Some(Task::ready(Ok(())))
 7101            }
 7102        }
 7103    }
 7104
 7105    fn open_transaction_for_hidden_buffers(
 7106        workspace: Entity<Workspace>,
 7107        transaction: ProjectTransaction,
 7108        title: String,
 7109        window: &mut Window,
 7110        cx: &mut Context<Self>,
 7111    ) {
 7112        if transaction.0.is_empty() {
 7113            return;
 7114        }
 7115
 7116        let edited_buffers_already_open = {
 7117            let other_editors: Vec<Entity<Editor>> = workspace
 7118                .read(cx)
 7119                .panes()
 7120                .iter()
 7121                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7122                .filter(|editor| editor.entity_id() != cx.entity_id())
 7123                .collect();
 7124
 7125            transaction.0.keys().all(|buffer| {
 7126                other_editors.iter().any(|editor| {
 7127                    let multi_buffer = editor.read(cx).buffer();
 7128                    multi_buffer.read(cx).is_singleton()
 7129                        && multi_buffer
 7130                            .read(cx)
 7131                            .as_singleton()
 7132                            .map_or(false, |singleton| {
 7133                                singleton.entity_id() == buffer.entity_id()
 7134                            })
 7135                })
 7136            })
 7137        };
 7138        if !edited_buffers_already_open {
 7139            let workspace = workspace.downgrade();
 7140            cx.defer_in(window, move |_, window, cx| {
 7141                cx.spawn_in(window, async move |editor, cx| {
 7142                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7143                        .await
 7144                        .ok()
 7145                })
 7146                .detach();
 7147            });
 7148        }
 7149    }
 7150
 7151    pub async fn open_project_transaction(
 7152        editor: &WeakEntity<Editor>,
 7153        workspace: WeakEntity<Workspace>,
 7154        transaction: ProjectTransaction,
 7155        title: String,
 7156        cx: &mut AsyncWindowContext,
 7157    ) -> Result<()> {
 7158        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7159        cx.update(|_, cx| {
 7160            entries.sort_unstable_by_key(|(buffer, _)| {
 7161                buffer.read(cx).file().map(|f| f.path().clone())
 7162            });
 7163        })?;
 7164        if entries.is_empty() {
 7165            return Ok(());
 7166        }
 7167
 7168        // If the project transaction's edits are all contained within this editor, then
 7169        // avoid opening a new editor to display them.
 7170
 7171        if let [(buffer, transaction)] = &*entries {
 7172            let excerpt = editor.update(cx, |editor, cx| {
 7173                editor
 7174                    .buffer()
 7175                    .read(cx)
 7176                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7177            })?;
 7178            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7179                && excerpted_buffer == *buffer
 7180            {
 7181                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7182                    let excerpt_range = excerpt_range.to_offset(buffer);
 7183                    buffer
 7184                        .edited_ranges_for_transaction::<usize>(transaction)
 7185                        .all(|range| {
 7186                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7187                        })
 7188                });
 7189
 7190                if all_edits_within_excerpt {
 7191                    return Ok(());
 7192                }
 7193            }
 7194        }
 7195
 7196        let mut ranges_to_highlight = Vec::new();
 7197        let excerpt_buffer = cx.new(|cx| {
 7198            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7199            for (buffer_handle, transaction) in &entries {
 7200                let edited_ranges = buffer_handle
 7201                    .read(cx)
 7202                    .edited_ranges_for_transaction::<Point>(transaction)
 7203                    .collect::<Vec<_>>();
 7204                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7205                    PathKey::for_buffer(buffer_handle, cx),
 7206                    buffer_handle.clone(),
 7207                    edited_ranges,
 7208                    multibuffer_context_lines(cx),
 7209                    cx,
 7210                );
 7211
 7212                ranges_to_highlight.extend(ranges);
 7213            }
 7214            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7215            multibuffer
 7216        });
 7217
 7218        workspace.update_in(cx, |workspace, window, cx| {
 7219            let project = workspace.project().clone();
 7220            let editor =
 7221                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7222            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7223            editor.update(cx, |editor, cx| {
 7224                editor.highlight_background(
 7225                    HighlightKey::Editor,
 7226                    &ranges_to_highlight,
 7227                    |_, theme| theme.colors().editor_highlighted_line_background,
 7228                    cx,
 7229                );
 7230            });
 7231        })?;
 7232
 7233        Ok(())
 7234    }
 7235
 7236    pub fn clear_code_action_providers(&mut self) {
 7237        self.code_action_providers.clear();
 7238        self.available_code_actions.take();
 7239    }
 7240
 7241    pub fn add_code_action_provider(
 7242        &mut self,
 7243        provider: Rc<dyn CodeActionProvider>,
 7244        window: &mut Window,
 7245        cx: &mut Context<Self>,
 7246    ) {
 7247        if self
 7248            .code_action_providers
 7249            .iter()
 7250            .any(|existing_provider| existing_provider.id() == provider.id())
 7251        {
 7252            return;
 7253        }
 7254
 7255        self.code_action_providers.push(provider);
 7256        self.refresh_code_actions(window, cx);
 7257    }
 7258
 7259    pub fn remove_code_action_provider(
 7260        &mut self,
 7261        id: Arc<str>,
 7262        window: &mut Window,
 7263        cx: &mut Context<Self>,
 7264    ) {
 7265        self.code_action_providers
 7266            .retain(|provider| provider.id() != id);
 7267        self.refresh_code_actions(window, cx);
 7268    }
 7269
 7270    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7271        !self.code_action_providers.is_empty()
 7272            && EditorSettings::get_global(cx).toolbar.code_actions
 7273    }
 7274
 7275    pub fn has_available_code_actions(&self) -> bool {
 7276        self.available_code_actions
 7277            .as_ref()
 7278            .is_some_and(|(_, actions)| !actions.is_empty())
 7279    }
 7280
 7281    fn render_inline_code_actions(
 7282        &self,
 7283        icon_size: ui::IconSize,
 7284        display_row: DisplayRow,
 7285        is_active: bool,
 7286        cx: &mut Context<Self>,
 7287    ) -> AnyElement {
 7288        let show_tooltip = !self.context_menu_visible();
 7289        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7290            .icon_size(icon_size)
 7291            .shape(ui::IconButtonShape::Square)
 7292            .icon_color(ui::Color::Hidden)
 7293            .toggle_state(is_active)
 7294            .when(show_tooltip, |this| {
 7295                this.tooltip({
 7296                    let focus_handle = self.focus_handle.clone();
 7297                    move |_window, cx| {
 7298                        Tooltip::for_action_in(
 7299                            "Toggle Code Actions",
 7300                            &ToggleCodeActions {
 7301                                deployed_from: None,
 7302                                quick_launch: false,
 7303                            },
 7304                            &focus_handle,
 7305                            cx,
 7306                        )
 7307                    }
 7308                })
 7309            })
 7310            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7311                window.focus(&editor.focus_handle(cx), cx);
 7312                editor.toggle_code_actions(
 7313                    &crate::actions::ToggleCodeActions {
 7314                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7315                            display_row,
 7316                        )),
 7317                        quick_launch: false,
 7318                    },
 7319                    window,
 7320                    cx,
 7321                );
 7322            }))
 7323            .into_any_element()
 7324    }
 7325
 7326    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7327        &self.context_menu
 7328    }
 7329
 7330    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7331        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7332            cx.background_executor()
 7333                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7334                .await;
 7335
 7336            let (start_buffer, start, _, end, newest_selection) = this
 7337                .update(cx, |this, cx| {
 7338                    let newest_selection = this.selections.newest_anchor().clone();
 7339                    if newest_selection.head().diff_base_anchor.is_some() {
 7340                        return None;
 7341                    }
 7342                    let display_snapshot = this.display_snapshot(cx);
 7343                    let newest_selection_adjusted =
 7344                        this.selections.newest_adjusted(&display_snapshot);
 7345                    let buffer = this.buffer.read(cx);
 7346
 7347                    let (start_buffer, start) =
 7348                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7349                    let (end_buffer, end) =
 7350                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7351
 7352                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7353                })?
 7354                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7355                .context(
 7356                    "Expected selection to lie in a single buffer when refreshing code actions",
 7357                )?;
 7358            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7359                let providers = this.code_action_providers.clone();
 7360                let tasks = this
 7361                    .code_action_providers
 7362                    .iter()
 7363                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7364                    .collect::<Vec<_>>();
 7365                (providers, tasks)
 7366            })?;
 7367
 7368            let mut actions = Vec::new();
 7369            for (provider, provider_actions) in
 7370                providers.into_iter().zip(future::join_all(tasks).await)
 7371            {
 7372                if let Some(provider_actions) = provider_actions.log_err() {
 7373                    actions.extend(provider_actions.into_iter().map(|action| {
 7374                        AvailableCodeAction {
 7375                            excerpt_id: newest_selection.start.excerpt_id,
 7376                            action,
 7377                            provider: provider.clone(),
 7378                        }
 7379                    }));
 7380                }
 7381            }
 7382
 7383            this.update(cx, |this, cx| {
 7384                this.available_code_actions = if actions.is_empty() {
 7385                    None
 7386                } else {
 7387                    Some((
 7388                        Location {
 7389                            buffer: start_buffer,
 7390                            range: start..end,
 7391                        },
 7392                        actions.into(),
 7393                    ))
 7394                };
 7395                cx.notify();
 7396            })
 7397        }));
 7398    }
 7399
 7400    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7401        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7402            self.show_git_blame_inline = false;
 7403
 7404            self.show_git_blame_inline_delay_task =
 7405                Some(cx.spawn_in(window, async move |this, cx| {
 7406                    cx.background_executor().timer(delay).await;
 7407
 7408                    this.update(cx, |this, cx| {
 7409                        this.show_git_blame_inline = true;
 7410                        cx.notify();
 7411                    })
 7412                    .log_err();
 7413                }));
 7414        }
 7415    }
 7416
 7417    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7418        let snapshot = self.snapshot(window, cx);
 7419        let cursor = self
 7420            .selections
 7421            .newest::<Point>(&snapshot.display_snapshot)
 7422            .head();
 7423        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7424        else {
 7425            return;
 7426        };
 7427
 7428        if self.blame.is_none() {
 7429            self.start_git_blame(true, window, cx);
 7430        }
 7431        let Some(blame) = self.blame.as_ref() else {
 7432            return;
 7433        };
 7434
 7435        let row_info = RowInfo {
 7436            buffer_id: Some(buffer.remote_id()),
 7437            buffer_row: Some(point.row),
 7438            ..Default::default()
 7439        };
 7440        let Some((buffer, blame_entry)) = blame
 7441            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7442            .flatten()
 7443        else {
 7444            return;
 7445        };
 7446
 7447        let anchor = self.selections.newest_anchor().head();
 7448        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7449        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7450            self.show_blame_popover(
 7451                buffer,
 7452                &blame_entry,
 7453                position + last_bounds.origin,
 7454                true,
 7455                cx,
 7456            );
 7457        };
 7458    }
 7459
 7460    fn show_blame_popover(
 7461        &mut self,
 7462        buffer: BufferId,
 7463        blame_entry: &BlameEntry,
 7464        position: gpui::Point<Pixels>,
 7465        ignore_timeout: bool,
 7466        cx: &mut Context<Self>,
 7467    ) {
 7468        if let Some(state) = &mut self.inline_blame_popover {
 7469            state.hide_task.take();
 7470        } else {
 7471            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7472            let blame_entry = blame_entry.clone();
 7473            let show_task = cx.spawn(async move |editor, cx| {
 7474                if !ignore_timeout {
 7475                    cx.background_executor()
 7476                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7477                        .await;
 7478                }
 7479                editor
 7480                    .update(cx, |editor, cx| {
 7481                        editor.inline_blame_popover_show_task.take();
 7482                        let Some(blame) = editor.blame.as_ref() else {
 7483                            return;
 7484                        };
 7485                        let blame = blame.read(cx);
 7486                        let details = blame.details_for_entry(buffer, &blame_entry);
 7487                        let markdown = cx.new(|cx| {
 7488                            Markdown::new(
 7489                                details
 7490                                    .as_ref()
 7491                                    .map(|message| message.message.clone())
 7492                                    .unwrap_or_default(),
 7493                                None,
 7494                                None,
 7495                                cx,
 7496                            )
 7497                        });
 7498                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7499                            position,
 7500                            hide_task: None,
 7501                            popover_bounds: None,
 7502                            popover_state: InlineBlamePopoverState {
 7503                                scroll_handle: ScrollHandle::new(),
 7504                                commit_message: details,
 7505                                markdown,
 7506                            },
 7507                            keyboard_grace: ignore_timeout,
 7508                        });
 7509                        cx.notify();
 7510                    })
 7511                    .ok();
 7512            });
 7513            self.inline_blame_popover_show_task = Some(show_task);
 7514        }
 7515    }
 7516
 7517    pub fn has_mouse_context_menu(&self) -> bool {
 7518        self.mouse_context_menu.is_some()
 7519    }
 7520
 7521    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7522        self.inline_blame_popover_show_task.take();
 7523        if let Some(state) = &mut self.inline_blame_popover {
 7524            let hide_task = cx.spawn(async move |editor, cx| {
 7525                if !ignore_timeout {
 7526                    cx.background_executor()
 7527                        .timer(std::time::Duration::from_millis(100))
 7528                        .await;
 7529                }
 7530                editor
 7531                    .update(cx, |editor, cx| {
 7532                        editor.inline_blame_popover.take();
 7533                        cx.notify();
 7534                    })
 7535                    .ok();
 7536            });
 7537            state.hide_task = Some(hide_task);
 7538            true
 7539        } else {
 7540            false
 7541        }
 7542    }
 7543
 7544    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7545        if self.pending_rename.is_some() {
 7546            return None;
 7547        }
 7548
 7549        let provider = self.semantics_provider.clone()?;
 7550        let buffer = self.buffer.read(cx);
 7551        let newest_selection = self.selections.newest_anchor().clone();
 7552        let cursor_position = newest_selection.head();
 7553        let (cursor_buffer, cursor_buffer_position) =
 7554            buffer.text_anchor_for_position(cursor_position, cx)?;
 7555        let (tail_buffer, tail_buffer_position) =
 7556            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7557        if cursor_buffer != tail_buffer {
 7558            return None;
 7559        }
 7560
 7561        let snapshot = cursor_buffer.read(cx).snapshot();
 7562        let word_ranges = cx.background_spawn(async move {
 7563            // this might look odd to put on the background thread, but
 7564            // `surrounding_word` can be quite expensive as it calls into
 7565            // tree-sitter language scopes
 7566            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7567            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7568            (start_word_range, end_word_range)
 7569        });
 7570
 7571        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7572        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7573            let (start_word_range, end_word_range) = word_ranges.await;
 7574            if start_word_range != end_word_range {
 7575                this.update(cx, |this, cx| {
 7576                    this.document_highlights_task.take();
 7577                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7578                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7579                })
 7580                .ok();
 7581                return;
 7582            }
 7583            cx.background_executor()
 7584                .timer(Duration::from_millis(debounce))
 7585                .await;
 7586
 7587            let highlights = if let Some(highlights) = cx.update(|cx| {
 7588                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7589            }) {
 7590                highlights.await.log_err()
 7591            } else {
 7592                None
 7593            };
 7594
 7595            if let Some(highlights) = highlights {
 7596                this.update(cx, |this, cx| {
 7597                    if this.pending_rename.is_some() {
 7598                        return;
 7599                    }
 7600
 7601                    let buffer = this.buffer.read(cx);
 7602                    if buffer
 7603                        .text_anchor_for_position(cursor_position, cx)
 7604                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7605                    {
 7606                        return;
 7607                    }
 7608
 7609                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7610                    let mut write_ranges = Vec::new();
 7611                    let mut read_ranges = Vec::new();
 7612                    for highlight in highlights {
 7613                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7614                        for (excerpt_id, _, excerpt_range) in
 7615                            buffer.excerpts_for_buffer(buffer_id, cx)
 7616                        {
 7617                            let start = highlight
 7618                                .range
 7619                                .start
 7620                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7621                            let end = highlight
 7622                                .range
 7623                                .end
 7624                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7625                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7626                                continue;
 7627                            }
 7628
 7629                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7630                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7631                                write_ranges.push(range);
 7632                            } else {
 7633                                read_ranges.push(range);
 7634                            }
 7635                        }
 7636                    }
 7637
 7638                    this.highlight_background(
 7639                        HighlightKey::DocumentHighlightRead,
 7640                        &read_ranges,
 7641                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7642                        cx,
 7643                    );
 7644                    this.highlight_background(
 7645                        HighlightKey::DocumentHighlightWrite,
 7646                        &write_ranges,
 7647                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7648                        cx,
 7649                    );
 7650                    cx.notify();
 7651                })
 7652                .log_err();
 7653            }
 7654        }));
 7655        None
 7656    }
 7657
 7658    fn prepare_highlight_query_from_selection(
 7659        &mut self,
 7660        snapshot: &DisplaySnapshot,
 7661        cx: &mut Context<Editor>,
 7662    ) -> Option<(String, Range<Anchor>)> {
 7663        if matches!(self.mode, EditorMode::SingleLine) {
 7664            return None;
 7665        }
 7666        if !EditorSettings::get_global(cx).selection_highlight {
 7667            return None;
 7668        }
 7669        if self.selections.count() != 1 || self.selections.line_mode() {
 7670            return None;
 7671        }
 7672        let selection = self.selections.newest::<Point>(&snapshot);
 7673        // If the selection spans multiple rows OR it is empty
 7674        if selection.start.row != selection.end.row
 7675            || selection.start.column == selection.end.column
 7676        {
 7677            return None;
 7678        }
 7679        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7680        let query = snapshot
 7681            .buffer_snapshot()
 7682            .text_for_range(selection_anchor_range.clone())
 7683            .collect::<String>();
 7684        if query.trim().is_empty() {
 7685            return None;
 7686        }
 7687        Some((query, selection_anchor_range))
 7688    }
 7689
 7690    #[ztracing::instrument(skip_all)]
 7691    fn update_selection_occurrence_highlights(
 7692        &mut self,
 7693        multi_buffer_snapshot: MultiBufferSnapshot,
 7694        query_text: String,
 7695        query_range: Range<Anchor>,
 7696        multi_buffer_range_to_query: Range<Point>,
 7697        use_debounce: bool,
 7698        window: &mut Window,
 7699        cx: &mut Context<Editor>,
 7700    ) -> Task<()> {
 7701        cx.spawn_in(window, async move |editor, cx| {
 7702            if use_debounce {
 7703                cx.background_executor()
 7704                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7705                    .await;
 7706            }
 7707            let match_task = cx.background_spawn(async move {
 7708                let buffer_ranges = multi_buffer_snapshot
 7709                    .range_to_buffer_ranges(
 7710                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7711                    )
 7712                    .into_iter()
 7713                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7714                let mut match_ranges = Vec::new();
 7715                let Ok(regex) = project::search::SearchQuery::text(
 7716                    query_text,
 7717                    false,
 7718                    false,
 7719                    false,
 7720                    Default::default(),
 7721                    Default::default(),
 7722                    false,
 7723                    None,
 7724                ) else {
 7725                    return Vec::default();
 7726                };
 7727                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7728                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7729                    match_ranges.extend(
 7730                        regex
 7731                            .search(
 7732                                buffer_snapshot,
 7733                                Some(search_range.start.0..search_range.end.0),
 7734                            )
 7735                            .await
 7736                            .into_iter()
 7737                            .filter_map(|match_range| {
 7738                                let match_start = buffer_snapshot
 7739                                    .anchor_after(search_range.start + match_range.start);
 7740                                let match_end = buffer_snapshot
 7741                                    .anchor_before(search_range.start + match_range.end);
 7742                                let match_anchor_range =
 7743                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7744                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7745                            }),
 7746                    );
 7747                }
 7748                match_ranges
 7749            });
 7750            let match_ranges = match_task.await;
 7751            editor
 7752                .update_in(cx, |editor, _, cx| {
 7753                    if use_debounce {
 7754                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7755                        editor.debounced_selection_highlight_complete = true;
 7756                    } else if editor.debounced_selection_highlight_complete {
 7757                        return;
 7758                    }
 7759                    if !match_ranges.is_empty() {
 7760                        editor.highlight_background(
 7761                            HighlightKey::SelectedTextHighlight,
 7762                            &match_ranges,
 7763                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7764                            cx,
 7765                        )
 7766                    }
 7767                })
 7768                .log_err();
 7769        })
 7770    }
 7771
 7772    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7773        struct NewlineFold;
 7774        let type_id = std::any::TypeId::of::<NewlineFold>();
 7775        if !self.mode.is_single_line() {
 7776            return;
 7777        }
 7778        let snapshot = self.snapshot(window, cx);
 7779        if snapshot.buffer_snapshot().max_point().row == 0 {
 7780            return;
 7781        }
 7782        let task = cx.background_spawn(async move {
 7783            let new_newlines = snapshot
 7784                .buffer_chars_at(MultiBufferOffset(0))
 7785                .filter_map(|(c, i)| {
 7786                    if c == '\n' {
 7787                        Some(
 7788                            snapshot.buffer_snapshot().anchor_after(i)
 7789                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7790                        )
 7791                    } else {
 7792                        None
 7793                    }
 7794                })
 7795                .collect::<Vec<_>>();
 7796            let existing_newlines = snapshot
 7797                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7798                .filter_map(|fold| {
 7799                    if fold.placeholder.type_tag == Some(type_id) {
 7800                        Some(fold.range.start..fold.range.end)
 7801                    } else {
 7802                        None
 7803                    }
 7804                })
 7805                .collect::<Vec<_>>();
 7806
 7807            (new_newlines, existing_newlines)
 7808        });
 7809        self.folding_newlines = cx.spawn(async move |this, cx| {
 7810            let (new_newlines, existing_newlines) = task.await;
 7811            if new_newlines == existing_newlines {
 7812                return;
 7813            }
 7814            let placeholder = FoldPlaceholder {
 7815                render: Arc::new(move |_, _, cx| {
 7816                    div()
 7817                        .bg(cx.theme().status().hint_background)
 7818                        .border_b_1()
 7819                        .size_full()
 7820                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7821                        .border_color(cx.theme().status().hint)
 7822                        .child("\\n")
 7823                        .into_any()
 7824                }),
 7825                constrain_width: false,
 7826                merge_adjacent: false,
 7827                type_tag: Some(type_id),
 7828                collapsed_text: None,
 7829            };
 7830            let creases = new_newlines
 7831                .into_iter()
 7832                .map(|range| Crease::simple(range, placeholder.clone()))
 7833                .collect();
 7834            this.update(cx, |this, cx| {
 7835                this.display_map.update(cx, |display_map, cx| {
 7836                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7837                    display_map.fold(creases, cx);
 7838                });
 7839            })
 7840            .ok();
 7841        });
 7842    }
 7843
 7844    #[ztracing::instrument(skip_all)]
 7845    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7846        if !self.lsp_data_enabled() {
 7847            return;
 7848        }
 7849        let cursor = self.selections.newest_anchor().head();
 7850        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7851
 7852        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7853            self.outline_symbols_at_cursor =
 7854                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7855            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7856            cx.notify();
 7857        } else {
 7858            let syntax = cx.theme().syntax().clone();
 7859            let background_task = cx.background_spawn(async move {
 7860                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7861            });
 7862            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7863                cx.spawn(async move |this, cx| {
 7864                    let symbols = background_task.await;
 7865                    this.update(cx, |this, cx| {
 7866                        this.outline_symbols_at_cursor = symbols;
 7867                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7868                        cx.notify();
 7869                    })
 7870                    .ok();
 7871                });
 7872        }
 7873    }
 7874
 7875    #[ztracing::instrument(skip_all)]
 7876    fn refresh_selected_text_highlights(
 7877        &mut self,
 7878        snapshot: &DisplaySnapshot,
 7879        on_buffer_edit: bool,
 7880        window: &mut Window,
 7881        cx: &mut Context<Editor>,
 7882    ) {
 7883        let Some((query_text, query_range)) =
 7884            self.prepare_highlight_query_from_selection(snapshot, cx)
 7885        else {
 7886            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7887            self.quick_selection_highlight_task.take();
 7888            self.debounced_selection_highlight_task.take();
 7889            self.debounced_selection_highlight_complete = false;
 7890            return;
 7891        };
 7892        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7893        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7894        let query_changed = self
 7895            .quick_selection_highlight_task
 7896            .as_ref()
 7897            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7898        if query_changed {
 7899            self.debounced_selection_highlight_complete = false;
 7900        }
 7901        if on_buffer_edit || query_changed {
 7902            self.quick_selection_highlight_task = Some((
 7903                query_range.clone(),
 7904                self.update_selection_occurrence_highlights(
 7905                    snapshot.buffer.clone(),
 7906                    query_text.clone(),
 7907                    query_range.clone(),
 7908                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7909                    false,
 7910                    window,
 7911                    cx,
 7912                ),
 7913            ));
 7914        }
 7915        if on_buffer_edit
 7916            || self
 7917                .debounced_selection_highlight_task
 7918                .as_ref()
 7919                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7920        {
 7921            let multi_buffer_start = multi_buffer_snapshot
 7922                .anchor_before(MultiBufferOffset(0))
 7923                .to_point(&multi_buffer_snapshot);
 7924            let multi_buffer_end = multi_buffer_snapshot
 7925                .anchor_after(multi_buffer_snapshot.len())
 7926                .to_point(&multi_buffer_snapshot);
 7927            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7928            self.debounced_selection_highlight_task = Some((
 7929                query_range.clone(),
 7930                self.update_selection_occurrence_highlights(
 7931                    snapshot.buffer.clone(),
 7932                    query_text,
 7933                    query_range,
 7934                    multi_buffer_full_range,
 7935                    true,
 7936                    window,
 7937                    cx,
 7938                ),
 7939            ));
 7940        }
 7941    }
 7942
 7943    pub fn multi_buffer_visible_range(
 7944        &self,
 7945        display_snapshot: &DisplaySnapshot,
 7946        cx: &App,
 7947    ) -> Range<Point> {
 7948        let visible_start = self
 7949            .scroll_manager
 7950            .native_anchor(display_snapshot, cx)
 7951            .anchor
 7952            .to_point(display_snapshot.buffer_snapshot())
 7953            .to_display_point(display_snapshot);
 7954
 7955        let mut target_end = visible_start;
 7956        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7957
 7958        visible_start.to_point(display_snapshot)
 7959            ..display_snapshot
 7960                .clip_point(target_end, Bias::Right)
 7961                .to_point(display_snapshot)
 7962    }
 7963
 7964    pub fn refresh_edit_prediction(
 7965        &mut self,
 7966        debounce: bool,
 7967        user_requested: bool,
 7968        window: &mut Window,
 7969        cx: &mut Context<Self>,
 7970    ) -> Option<()> {
 7971        if self.leader_id.is_some() {
 7972            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7973            return None;
 7974        }
 7975
 7976        let cursor = self.selections.newest_anchor().head();
 7977        let (buffer, cursor_buffer_position) =
 7978            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7979
 7980        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7981            return None;
 7982        }
 7983
 7984        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7985            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7986            return None;
 7987        }
 7988
 7989        self.update_visible_edit_prediction(window, cx);
 7990
 7991        if !user_requested
 7992            && (!self.should_show_edit_predictions()
 7993                || !self.is_focused(window)
 7994                || buffer.read(cx).is_empty())
 7995        {
 7996            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7997            return None;
 7998        }
 7999
 8000        self.edit_prediction_provider()?
 8001            .refresh(buffer, cursor_buffer_position, debounce, cx);
 8002        Some(())
 8003    }
 8004
 8005    fn show_edit_predictions_in_menu(&self) -> bool {
 8006        match self.edit_prediction_settings {
 8007            EditPredictionSettings::Disabled => false,
 8008            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 8009        }
 8010    }
 8011
 8012    pub fn edit_predictions_enabled(&self) -> bool {
 8013        match self.edit_prediction_settings {
 8014            EditPredictionSettings::Disabled => false,
 8015            EditPredictionSettings::Enabled { .. } => true,
 8016        }
 8017    }
 8018
 8019    fn edit_prediction_requires_modifier(&self) -> bool {
 8020        match self.edit_prediction_settings {
 8021            EditPredictionSettings::Disabled => false,
 8022            EditPredictionSettings::Enabled {
 8023                preview_requires_modifier,
 8024                ..
 8025            } => preview_requires_modifier,
 8026        }
 8027    }
 8028
 8029    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 8030        if self.edit_prediction_provider.is_none() {
 8031            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8032            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8033            return;
 8034        }
 8035
 8036        let selection = self.selections.newest_anchor();
 8037        let cursor = selection.head();
 8038
 8039        if let Some((buffer, cursor_buffer_position)) =
 8040            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8041        {
 8042            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8043                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8044                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8045                return;
 8046            }
 8047            self.edit_prediction_settings =
 8048                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8049        }
 8050    }
 8051
 8052    fn edit_prediction_settings_at_position(
 8053        &self,
 8054        buffer: &Entity<Buffer>,
 8055        buffer_position: language::Anchor,
 8056        cx: &App,
 8057    ) -> EditPredictionSettings {
 8058        if !self.mode.is_full()
 8059            || !self.show_edit_predictions_override.unwrap_or(true)
 8060            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 8061        {
 8062            return EditPredictionSettings::Disabled;
 8063        }
 8064
 8065        if !LanguageSettings::for_buffer(&buffer.read(cx), cx).show_edit_predictions {
 8066            return EditPredictionSettings::Disabled;
 8067        };
 8068
 8069        let by_provider = matches!(
 8070            self.menu_edit_predictions_policy,
 8071            MenuEditPredictionsPolicy::ByProvider
 8072        );
 8073
 8074        let show_in_menu = by_provider
 8075            && self
 8076                .edit_prediction_provider
 8077                .as_ref()
 8078                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 8079
 8080        let file = buffer.read(cx).file();
 8081        let preview_requires_modifier =
 8082            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 8083
 8084        EditPredictionSettings::Enabled {
 8085            show_in_menu,
 8086            preview_requires_modifier,
 8087        }
 8088    }
 8089
 8090    fn should_show_edit_predictions(&self) -> bool {
 8091        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 8092    }
 8093
 8094    pub fn edit_prediction_preview_is_active(&self) -> bool {
 8095        matches!(
 8096            self.edit_prediction_preview,
 8097            EditPredictionPreview::Active { .. }
 8098        )
 8099    }
 8100
 8101    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 8102        let cursor = self.selections.newest_anchor().head();
 8103        if let Some((buffer, cursor_position)) =
 8104            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8105        {
 8106            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 8107        } else {
 8108            false
 8109        }
 8110    }
 8111
 8112    pub fn supports_minimap(&self, cx: &App) -> bool {
 8113        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 8114    }
 8115
 8116    fn edit_predictions_enabled_in_buffer(
 8117        &self,
 8118        buffer: &Entity<Buffer>,
 8119        buffer_position: language::Anchor,
 8120        cx: &App,
 8121    ) -> bool {
 8122        maybe!({
 8123            if self.read_only(cx) || self.leader_id.is_some() {
 8124                return Some(false);
 8125            }
 8126            let provider = self.edit_prediction_provider()?;
 8127            if !provider.is_enabled(buffer, buffer_position, cx) {
 8128                return Some(false);
 8129            }
 8130            let buffer = buffer.read(cx);
 8131            let Some(file) = buffer.file() else {
 8132                return Some(true);
 8133            };
 8134            let settings = all_language_settings(Some(file), cx);
 8135            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8136        })
 8137        .unwrap_or(false)
 8138    }
 8139
 8140    pub fn show_edit_prediction(
 8141        &mut self,
 8142        _: &ShowEditPrediction,
 8143        window: &mut Window,
 8144        cx: &mut Context<Self>,
 8145    ) {
 8146        if !self.has_active_edit_prediction() {
 8147            self.refresh_edit_prediction(false, true, window, cx);
 8148            return;
 8149        }
 8150
 8151        self.update_visible_edit_prediction(window, cx);
 8152    }
 8153
 8154    pub fn display_cursor_names(
 8155        &mut self,
 8156        _: &DisplayCursorNames,
 8157        window: &mut Window,
 8158        cx: &mut Context<Self>,
 8159    ) {
 8160        self.show_cursor_names(window, cx);
 8161    }
 8162
 8163    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8164        self.show_cursor_names = true;
 8165        cx.notify();
 8166        cx.spawn_in(window, async move |this, cx| {
 8167            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8168            this.update(cx, |this, cx| {
 8169                this.show_cursor_names = false;
 8170                cx.notify()
 8171            })
 8172            .ok()
 8173        })
 8174        .detach();
 8175    }
 8176
 8177    pub fn accept_partial_edit_prediction(
 8178        &mut self,
 8179        granularity: EditPredictionGranularity,
 8180        window: &mut Window,
 8181        cx: &mut Context<Self>,
 8182    ) {
 8183        if self.show_edit_predictions_in_menu() {
 8184            self.hide_context_menu(window, cx);
 8185        }
 8186
 8187        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8188            return;
 8189        };
 8190
 8191        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8192            return;
 8193        }
 8194
 8195        match &active_edit_prediction.completion {
 8196            EditPrediction::MoveWithin { target, .. } => {
 8197                let target = *target;
 8198
 8199                if matches!(granularity, EditPredictionGranularity::Full) {
 8200                    if let Some(position_map) = &self.last_position_map {
 8201                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8202                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8203
 8204                        if is_visible || !self.edit_prediction_requires_modifier() {
 8205                            self.unfold_ranges(&[target..target], true, false, cx);
 8206                            self.change_selections(
 8207                                SelectionEffects::scroll(Autoscroll::newest()),
 8208                                window,
 8209                                cx,
 8210                                |selections| {
 8211                                    selections.select_anchor_ranges([target..target]);
 8212                                },
 8213                            );
 8214                            self.clear_row_highlights::<EditPredictionPreview>();
 8215                            self.edit_prediction_preview
 8216                                .set_previous_scroll_position(None);
 8217                        } else {
 8218                            // Highlight and request scroll
 8219                            self.edit_prediction_preview
 8220                                .set_previous_scroll_position(Some(
 8221                                    position_map.snapshot.scroll_anchor,
 8222                                ));
 8223                            self.highlight_rows::<EditPredictionPreview>(
 8224                                target..target,
 8225                                cx.theme().colors().editor_highlighted_line_background,
 8226                                RowHighlightOptions {
 8227                                    autoscroll: true,
 8228                                    ..Default::default()
 8229                                },
 8230                                cx,
 8231                            );
 8232                            self.request_autoscroll(Autoscroll::fit(), cx);
 8233                        }
 8234                    }
 8235                } else {
 8236                    self.change_selections(
 8237                        SelectionEffects::scroll(Autoscroll::newest()),
 8238                        window,
 8239                        cx,
 8240                        |selections| {
 8241                            selections.select_anchor_ranges([target..target]);
 8242                        },
 8243                    );
 8244                }
 8245            }
 8246            EditPrediction::MoveOutside { snapshot, target } => {
 8247                if let Some(workspace) = self.workspace() {
 8248                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8249                        .detach_and_log_err(cx);
 8250                }
 8251            }
 8252            EditPrediction::Edit {
 8253                edits,
 8254                cursor_position,
 8255                ..
 8256            } => {
 8257                self.report_edit_prediction_event(
 8258                    active_edit_prediction.completion_id.clone(),
 8259                    true,
 8260                    cx,
 8261                );
 8262
 8263                match granularity {
 8264                    EditPredictionGranularity::Full => {
 8265                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8266
 8267                        // Compute fallback cursor position BEFORE applying the edit,
 8268                        // so the anchor tracks through the edit correctly
 8269                        let fallback_cursor_target = {
 8270                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8271                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8272                        };
 8273
 8274                        self.buffer.update(cx, |buffer, cx| {
 8275                            buffer.edit(edits.iter().cloned(), None, cx)
 8276                        });
 8277
 8278                        if let Some(provider) = self.edit_prediction_provider() {
 8279                            provider.accept(cx);
 8280                        }
 8281
 8282                        // Resolve cursor position after the edit is applied
 8283                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8284                            // The anchor tracks through the edit, then we add the offset
 8285                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8286                            let base_offset = anchor.to_offset(&snapshot).0;
 8287                            let target_offset =
 8288                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8289                            snapshot.anchor_after(target_offset)
 8290                        } else {
 8291                            fallback_cursor_target
 8292                        };
 8293
 8294                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8295                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8296                        });
 8297
 8298                        let selections = self.selections.disjoint_anchors_arc();
 8299                        if let Some(transaction_id_now) =
 8300                            self.buffer.read(cx).last_transaction_id(cx)
 8301                        {
 8302                            if transaction_id_prev != Some(transaction_id_now) {
 8303                                self.selection_history
 8304                                    .insert_transaction(transaction_id_now, selections);
 8305                            }
 8306                        }
 8307
 8308                        self.update_visible_edit_prediction(window, cx);
 8309                        if self.active_edit_prediction.is_none() {
 8310                            self.refresh_edit_prediction(true, true, window, cx);
 8311                        }
 8312                        cx.notify();
 8313                    }
 8314                    _ => {
 8315                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8316                        let cursor_offset = self
 8317                            .selections
 8318                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8319                            .head();
 8320
 8321                        let insertion = edits.iter().find_map(|(range, text)| {
 8322                            let range = range.to_offset(&snapshot);
 8323                            if range.is_empty() && range.start == cursor_offset {
 8324                                Some(text)
 8325                            } else {
 8326                                None
 8327                            }
 8328                        });
 8329
 8330                        if let Some(text) = insertion {
 8331                            let text_to_insert = match granularity {
 8332                                EditPredictionGranularity::Word => {
 8333                                    let mut partial = text
 8334                                        .chars()
 8335                                        .by_ref()
 8336                                        .take_while(|c| c.is_alphabetic())
 8337                                        .collect::<String>();
 8338                                    if partial.is_empty() {
 8339                                        partial = text
 8340                                            .chars()
 8341                                            .by_ref()
 8342                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8343                                            .collect::<String>();
 8344                                    }
 8345                                    partial
 8346                                }
 8347                                EditPredictionGranularity::Line => {
 8348                                    if let Some(line) = text.split_inclusive('\n').next() {
 8349                                        line.to_string()
 8350                                    } else {
 8351                                        text.to_string()
 8352                                    }
 8353                                }
 8354                                EditPredictionGranularity::Full => unreachable!(),
 8355                            };
 8356
 8357                            cx.emit(EditorEvent::InputHandled {
 8358                                utf16_range_to_replace: None,
 8359                                text: text_to_insert.clone().into(),
 8360                            });
 8361
 8362                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8363                            self.refresh_edit_prediction(true, true, window, cx);
 8364                            cx.notify();
 8365                        } else {
 8366                            self.accept_partial_edit_prediction(
 8367                                EditPredictionGranularity::Full,
 8368                                window,
 8369                                cx,
 8370                            );
 8371                        }
 8372                    }
 8373                }
 8374            }
 8375        }
 8376    }
 8377
 8378    pub fn accept_next_word_edit_prediction(
 8379        &mut self,
 8380        _: &AcceptNextWordEditPrediction,
 8381        window: &mut Window,
 8382        cx: &mut Context<Self>,
 8383    ) {
 8384        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8385    }
 8386
 8387    pub fn accept_next_line_edit_prediction(
 8388        &mut self,
 8389        _: &AcceptNextLineEditPrediction,
 8390        window: &mut Window,
 8391        cx: &mut Context<Self>,
 8392    ) {
 8393        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8394    }
 8395
 8396    pub fn accept_edit_prediction(
 8397        &mut self,
 8398        _: &AcceptEditPrediction,
 8399        window: &mut Window,
 8400        cx: &mut Context<Self>,
 8401    ) {
 8402        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8403    }
 8404
 8405    fn discard_edit_prediction(
 8406        &mut self,
 8407        reason: EditPredictionDiscardReason,
 8408        cx: &mut Context<Self>,
 8409    ) -> bool {
 8410        if reason == EditPredictionDiscardReason::Rejected {
 8411            let completion_id = self
 8412                .active_edit_prediction
 8413                .as_ref()
 8414                .and_then(|active_completion| active_completion.completion_id.clone());
 8415
 8416            self.report_edit_prediction_event(completion_id, false, cx);
 8417        }
 8418
 8419        if let Some(provider) = self.edit_prediction_provider() {
 8420            provider.discard(reason, cx);
 8421        }
 8422
 8423        self.take_active_edit_prediction(reason == EditPredictionDiscardReason::Ignored, cx)
 8424    }
 8425
 8426    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8427        let Some(provider) = self.edit_prediction_provider() else {
 8428            return;
 8429        };
 8430
 8431        let Some((_, buffer, _)) = self
 8432            .buffer
 8433            .read(cx)
 8434            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8435        else {
 8436            return;
 8437        };
 8438
 8439        let extension = buffer
 8440            .read(cx)
 8441            .file()
 8442            .and_then(|file| Some(file.path().extension()?.to_string()));
 8443
 8444        let event_type = match accepted {
 8445            true => "Edit Prediction Accepted",
 8446            false => "Edit Prediction Discarded",
 8447        };
 8448        telemetry::event!(
 8449            event_type,
 8450            provider = provider.name(),
 8451            prediction_id = id,
 8452            suggestion_accepted = accepted,
 8453            file_extension = extension,
 8454        );
 8455    }
 8456
 8457    fn open_editor_at_anchor(
 8458        snapshot: &language::BufferSnapshot,
 8459        target: language::Anchor,
 8460        workspace: &Entity<Workspace>,
 8461        window: &mut Window,
 8462        cx: &mut App,
 8463    ) -> Task<Result<()>> {
 8464        workspace.update(cx, |workspace, cx| {
 8465            let path = snapshot.file().map(|file| file.full_path(cx));
 8466            let Some(path) =
 8467                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8468            else {
 8469                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8470            };
 8471            let target = text::ToPoint::to_point(&target, snapshot);
 8472            let item = workspace.open_path(path, None, true, window, cx);
 8473            window.spawn(cx, async move |cx| {
 8474                let Some(editor) = item.await?.downcast::<Editor>() else {
 8475                    return Ok(());
 8476                };
 8477                editor
 8478                    .update_in(cx, |editor, window, cx| {
 8479                        editor.go_to_singleton_buffer_point(target, window, cx);
 8480                    })
 8481                    .ok();
 8482                anyhow::Ok(())
 8483            })
 8484        })
 8485    }
 8486
 8487    pub fn has_active_edit_prediction(&self) -> bool {
 8488        self.active_edit_prediction.is_some()
 8489    }
 8490
 8491    fn take_active_edit_prediction(
 8492        &mut self,
 8493        preserve_stale_in_menu: bool,
 8494        cx: &mut Context<Self>,
 8495    ) -> bool {
 8496        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8497            if !preserve_stale_in_menu {
 8498                self.stale_edit_prediction_in_menu = None;
 8499            }
 8500            return false;
 8501        };
 8502
 8503        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8504        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8505        self.stale_edit_prediction_in_menu =
 8506            preserve_stale_in_menu.then_some(active_edit_prediction);
 8507        true
 8508    }
 8509
 8510    /// Returns true when we're displaying the edit prediction popover below the cursor
 8511    /// like we are not previewing and the LSP autocomplete menu is visible
 8512    /// or we are in `when_holding_modifier` mode.
 8513    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8514        if self.edit_prediction_preview_is_active()
 8515            || !self.show_edit_predictions_in_menu()
 8516            || !self.edit_predictions_enabled()
 8517        {
 8518            return false;
 8519        }
 8520
 8521        if self.has_visible_completions_menu() {
 8522            return true;
 8523        }
 8524
 8525        has_completion && self.edit_prediction_requires_modifier()
 8526    }
 8527
 8528    fn handle_modifiers_changed(
 8529        &mut self,
 8530        modifiers: Modifiers,
 8531        position_map: &PositionMap,
 8532        window: &mut Window,
 8533        cx: &mut Context<Self>,
 8534    ) {
 8535        self.update_edit_prediction_settings(cx);
 8536
 8537        // Ensure that the edit prediction preview is updated, even when not
 8538        // enabled, if there's an active edit prediction preview.
 8539        if self.show_edit_predictions_in_menu()
 8540            || self.edit_prediction_requires_modifier()
 8541            || matches!(
 8542                self.edit_prediction_preview,
 8543                EditPredictionPreview::Active { .. }
 8544            )
 8545        {
 8546            self.update_edit_prediction_preview(&modifiers, window, cx);
 8547        }
 8548
 8549        self.update_selection_mode(&modifiers, position_map, window, cx);
 8550
 8551        let mouse_position = window.mouse_position();
 8552        if !position_map.text_hitbox.is_hovered(window) {
 8553            return;
 8554        }
 8555
 8556        self.update_hovered_link(
 8557            position_map.point_for_position(mouse_position),
 8558            Some(mouse_position),
 8559            &position_map.snapshot,
 8560            modifiers,
 8561            window,
 8562            cx,
 8563        )
 8564    }
 8565
 8566    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8567        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8568            MultiCursorModifier::Alt => modifiers.secondary(),
 8569            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8570        }
 8571    }
 8572
 8573    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8574        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8575            MultiCursorModifier::Alt => modifiers.alt,
 8576            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8577        }
 8578    }
 8579
 8580    fn columnar_selection_mode(
 8581        modifiers: &Modifiers,
 8582        cx: &mut Context<Self>,
 8583    ) -> Option<ColumnarMode> {
 8584        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8585            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8586                Some(ColumnarMode::FromMouse)
 8587            } else if Self::is_alt_pressed(modifiers, cx) {
 8588                Some(ColumnarMode::FromSelection)
 8589            } else {
 8590                None
 8591            }
 8592        } else {
 8593            None
 8594        }
 8595    }
 8596
 8597    fn update_selection_mode(
 8598        &mut self,
 8599        modifiers: &Modifiers,
 8600        position_map: &PositionMap,
 8601        window: &mut Window,
 8602        cx: &mut Context<Self>,
 8603    ) {
 8604        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8605            return;
 8606        };
 8607        if self.selections.pending_anchor().is_none() {
 8608            return;
 8609        }
 8610
 8611        let mouse_position = window.mouse_position();
 8612        let point_for_position = position_map.point_for_position(mouse_position);
 8613        let position = point_for_position.previous_valid;
 8614
 8615        self.select(
 8616            SelectPhase::BeginColumnar {
 8617                position,
 8618                reset: false,
 8619                mode,
 8620                goal_column: point_for_position.exact_unclipped.column(),
 8621            },
 8622            window,
 8623            cx,
 8624        );
 8625    }
 8626
 8627    fn update_edit_prediction_preview(
 8628        &mut self,
 8629        modifiers: &Modifiers,
 8630        window: &mut Window,
 8631        cx: &mut Context<Self>,
 8632    ) {
 8633        let modifiers_held = self.edit_prediction_preview_modifiers_held(modifiers, window, cx);
 8634
 8635        if modifiers_held {
 8636            if matches!(
 8637                self.edit_prediction_preview,
 8638                EditPredictionPreview::Inactive { .. }
 8639            ) {
 8640                self.edit_prediction_preview = EditPredictionPreview::Active {
 8641                    previous_scroll_position: None,
 8642                    since: Instant::now(),
 8643                };
 8644
 8645                self.update_visible_edit_prediction(window, cx);
 8646                cx.notify();
 8647            }
 8648        } else if let EditPredictionPreview::Active {
 8649            previous_scroll_position,
 8650            since,
 8651        } = self.edit_prediction_preview
 8652        {
 8653            if let (Some(previous_scroll_position), Some(position_map)) =
 8654                (previous_scroll_position, self.last_position_map.as_ref())
 8655            {
 8656                self.set_scroll_position(
 8657                    previous_scroll_position
 8658                        .scroll_position(&position_map.snapshot.display_snapshot),
 8659                    window,
 8660                    cx,
 8661                );
 8662            }
 8663
 8664            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8665                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8666            };
 8667            self.clear_row_highlights::<EditPredictionPreview>();
 8668            self.update_visible_edit_prediction(window, cx);
 8669            cx.notify();
 8670        }
 8671    }
 8672
 8673    fn update_visible_edit_prediction(
 8674        &mut self,
 8675        _window: &mut Window,
 8676        cx: &mut Context<Self>,
 8677    ) -> Option<()> {
 8678        if self.ime_transaction.is_some() {
 8679            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8680            return None;
 8681        }
 8682
 8683        let selection = self.selections.newest_anchor();
 8684        let cursor = selection.head();
 8685        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8686
 8687        // Check project-level disable_ai setting for the current buffer
 8688        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8689            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8690                return None;
 8691            }
 8692        }
 8693        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8694        let excerpt_id = cursor.excerpt_id;
 8695
 8696        let show_in_menu = self.show_edit_predictions_in_menu();
 8697        let completions_menu_has_precedence = !show_in_menu
 8698            && (self.context_menu.borrow().is_some()
 8699                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8700
 8701        if completions_menu_has_precedence
 8702            || !offset_selection.is_empty()
 8703            || self
 8704                .active_edit_prediction
 8705                .as_ref()
 8706                .is_some_and(|completion| {
 8707                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8708                        return false;
 8709                    };
 8710                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8711                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8712                    !invalidation_range.contains(&offset_selection.head())
 8713                })
 8714        {
 8715            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8716            return None;
 8717        }
 8718
 8719        self.take_active_edit_prediction(true, cx);
 8720        let Some(provider) = self.edit_prediction_provider() else {
 8721            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8722            return None;
 8723        };
 8724
 8725        let (buffer, cursor_buffer_position) =
 8726            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8727
 8728        self.edit_prediction_settings =
 8729            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8730
 8731        self.in_leading_whitespace = multibuffer.is_line_whitespace_upto(cursor);
 8732
 8733        if self.in_leading_whitespace {
 8734            let cursor_point = cursor.to_point(&multibuffer);
 8735            let mut suggested_indent = None;
 8736            multibuffer.suggested_indents_callback(
 8737                cursor_point.row..cursor_point.row + 1,
 8738                &mut |_, indent| {
 8739                    suggested_indent = Some(indent);
 8740                    ControlFlow::Break(())
 8741                },
 8742                cx,
 8743            );
 8744
 8745            if let Some(indent) = suggested_indent
 8746                && indent.len == cursor_point.column
 8747            {
 8748                self.in_leading_whitespace = false;
 8749            }
 8750        }
 8751
 8752        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8753
 8754        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8755        {
 8756            edit_prediction_types::EditPrediction::Local {
 8757                id,
 8758                edits,
 8759                cursor_position,
 8760                edit_preview,
 8761            } => (id, edits, cursor_position, edit_preview),
 8762            edit_prediction_types::EditPrediction::Jump {
 8763                id,
 8764                snapshot,
 8765                target,
 8766            } => {
 8767                if let Some(provider) = &self.edit_prediction_provider {
 8768                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8769                }
 8770                self.stale_edit_prediction_in_menu = None;
 8771                self.active_edit_prediction = Some(EditPredictionState {
 8772                    inlay_ids: vec![],
 8773                    completion: EditPrediction::MoveOutside { snapshot, target },
 8774                    completion_id: id,
 8775                    invalidation_range: None,
 8776                });
 8777                cx.notify();
 8778                return Some(());
 8779            }
 8780        };
 8781
 8782        let edits = edits
 8783            .into_iter()
 8784            .flat_map(|(range, new_text)| {
 8785                Some((
 8786                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8787                    new_text,
 8788                ))
 8789            })
 8790            .collect::<Vec<_>>();
 8791        if edits.is_empty() {
 8792            return None;
 8793        }
 8794
 8795        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8796            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8797            Some((anchor, predicted.offset))
 8798        });
 8799
 8800        let first_edit_start = edits.first().unwrap().0.start;
 8801        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8802        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8803
 8804        let last_edit_end = edits.last().unwrap().0.end;
 8805        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8806        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8807
 8808        let cursor_row = cursor.to_point(&multibuffer).row;
 8809
 8810        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8811
 8812        let mut inlay_ids = Vec::new();
 8813        let invalidation_row_range;
 8814        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8815            Some(cursor_row..edit_end_row)
 8816        } else if cursor_row > edit_end_row {
 8817            Some(edit_start_row..cursor_row)
 8818        } else {
 8819            None
 8820        };
 8821        let supports_jump = self
 8822            .edit_prediction_provider
 8823            .as_ref()
 8824            .map(|provider| provider.provider.supports_jump_to_edit())
 8825            .unwrap_or(true);
 8826
 8827        let is_move = supports_jump
 8828            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8829        let completion = if is_move {
 8830            if let Some(provider) = &self.edit_prediction_provider {
 8831                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8832            }
 8833            invalidation_row_range =
 8834                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8835            let target = first_edit_start;
 8836            EditPrediction::MoveWithin { target, snapshot }
 8837        } else {
 8838            let show_completions_in_menu = self.has_visible_completions_menu();
 8839            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8840                && !self.edit_predictions_hidden_for_vim_mode;
 8841
 8842            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8843                if provider.show_tab_accept_marker() {
 8844                    EditDisplayMode::TabAccept
 8845                } else {
 8846                    EditDisplayMode::Inline
 8847                }
 8848            } else {
 8849                EditDisplayMode::DiffPopover
 8850            };
 8851
 8852            let report_shown = match display_mode {
 8853                EditDisplayMode::DiffPopover | EditDisplayMode::Inline => {
 8854                    show_completions_in_buffer || show_completions_in_menu
 8855                }
 8856                EditDisplayMode::TabAccept => {
 8857                    show_completions_in_menu || self.edit_prediction_preview_is_active()
 8858                }
 8859            };
 8860
 8861            if report_shown && let Some(provider) = &self.edit_prediction_provider {
 8862                let suggestion_display_type = match display_mode {
 8863                    EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8864                    EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8865                        SuggestionDisplayType::GhostText
 8866                    }
 8867                };
 8868                provider.provider.did_show(suggestion_display_type, cx);
 8869            }
 8870
 8871            if show_completions_in_buffer {
 8872                if edits
 8873                    .iter()
 8874                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8875                {
 8876                    let mut inlays = Vec::new();
 8877                    for (range, new_text) in &edits {
 8878                        let inlay = Inlay::edit_prediction(
 8879                            post_inc(&mut self.next_inlay_id),
 8880                            range.start,
 8881                            new_text.as_ref(),
 8882                        );
 8883                        inlay_ids.push(inlay.id);
 8884                        inlays.push(inlay);
 8885                    }
 8886
 8887                    self.splice_inlays(&[], inlays, cx);
 8888                } else {
 8889                    let background_color = cx.theme().status().deleted_background;
 8890                    self.highlight_text(
 8891                        HighlightKey::EditPredictionHighlight,
 8892                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8893                        HighlightStyle {
 8894                            background_color: Some(background_color),
 8895                            ..Default::default()
 8896                        },
 8897                        cx,
 8898                    );
 8899                }
 8900            }
 8901
 8902            invalidation_row_range = edit_start_row..edit_end_row;
 8903
 8904            EditPrediction::Edit {
 8905                edits,
 8906                cursor_position,
 8907                edit_preview,
 8908                display_mode,
 8909                snapshot,
 8910            }
 8911        };
 8912
 8913        let invalidation_range = multibuffer
 8914            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8915            ..multibuffer.anchor_after(Point::new(
 8916                invalidation_row_range.end,
 8917                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8918            ));
 8919
 8920        self.stale_edit_prediction_in_menu = None;
 8921        self.active_edit_prediction = Some(EditPredictionState {
 8922            inlay_ids,
 8923            completion,
 8924            completion_id,
 8925            invalidation_range: Some(invalidation_range),
 8926        });
 8927
 8928        cx.notify();
 8929
 8930        Some(())
 8931    }
 8932
 8933    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8934        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8935    }
 8936
 8937    /// Get all display points of breakpoints that will be rendered within editor
 8938    ///
 8939    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8940    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8941    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8942    fn active_breakpoints(
 8943        &self,
 8944        range: Range<DisplayRow>,
 8945        window: &mut Window,
 8946        cx: &mut Context<Self>,
 8947    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8948        let mut breakpoint_display_points = HashMap::default();
 8949
 8950        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8951            return breakpoint_display_points;
 8952        };
 8953
 8954        let snapshot = self.snapshot(window, cx);
 8955
 8956        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8957        let Some(project) = self.project() else {
 8958            return breakpoint_display_points;
 8959        };
 8960
 8961        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8962            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8963
 8964        for (buffer_snapshot, range, excerpt_id) in
 8965            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8966        {
 8967            let Some(buffer) = project
 8968                .read(cx)
 8969                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8970            else {
 8971                continue;
 8972            };
 8973            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8974                &buffer,
 8975                Some(
 8976                    buffer_snapshot.anchor_before(range.start)
 8977                        ..buffer_snapshot.anchor_after(range.end),
 8978                ),
 8979                buffer_snapshot,
 8980                cx,
 8981            );
 8982            for (breakpoint, state) in breakpoints {
 8983                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8984                let position = multi_buffer_anchor
 8985                    .to_point(&multi_buffer_snapshot)
 8986                    .to_display_point(&snapshot);
 8987
 8988                breakpoint_display_points.insert(
 8989                    position.row(),
 8990                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8991                );
 8992            }
 8993        }
 8994
 8995        breakpoint_display_points
 8996    }
 8997
 8998    fn breakpoint_context_menu(
 8999        &self,
 9000        anchor: Anchor,
 9001        window: &mut Window,
 9002        cx: &mut Context<Self>,
 9003    ) -> Entity<ui::ContextMenu> {
 9004        let weak_editor = cx.weak_entity();
 9005        let focus_handle = self.focus_handle(cx);
 9006
 9007        let row = self
 9008            .buffer
 9009            .read(cx)
 9010            .snapshot(cx)
 9011            .summary_for_anchor::<Point>(&anchor)
 9012            .row;
 9013
 9014        let breakpoint = self
 9015            .breakpoint_at_row(row, window, cx)
 9016            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 9017
 9018        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 9019            "Edit Log Breakpoint"
 9020        } else {
 9021            "Set Log Breakpoint"
 9022        };
 9023
 9024        let condition_breakpoint_msg = if breakpoint
 9025            .as_ref()
 9026            .is_some_and(|bp| bp.1.condition.is_some())
 9027        {
 9028            "Edit Condition Breakpoint"
 9029        } else {
 9030            "Set Condition Breakpoint"
 9031        };
 9032
 9033        let hit_condition_breakpoint_msg = if breakpoint
 9034            .as_ref()
 9035            .is_some_and(|bp| bp.1.hit_condition.is_some())
 9036        {
 9037            "Edit Hit Condition Breakpoint"
 9038        } else {
 9039            "Set Hit Condition Breakpoint"
 9040        };
 9041
 9042        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 9043            "Unset Breakpoint"
 9044        } else {
 9045            "Set Breakpoint"
 9046        };
 9047
 9048        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 9049
 9050        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 9051            BreakpointState::Enabled => Some("Disable"),
 9052            BreakpointState::Disabled => Some("Enable"),
 9053        });
 9054
 9055        let (anchor, breakpoint) =
 9056            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 9057
 9058        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 9059            menu.on_blur_subscription(Subscription::new(|| {}))
 9060                .context(focus_handle)
 9061                .when(run_to_cursor, |this| {
 9062                    let weak_editor = weak_editor.clone();
 9063                    this.entry("Run to Cursor", None, move |window, cx| {
 9064                        weak_editor
 9065                            .update(cx, |editor, cx| {
 9066                                editor.change_selections(
 9067                                    SelectionEffects::no_scroll(),
 9068                                    window,
 9069                                    cx,
 9070                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 9071                                );
 9072                            })
 9073                            .ok();
 9074
 9075                        window.dispatch_action(Box::new(RunToCursor), cx);
 9076                    })
 9077                    .separator()
 9078                })
 9079                .when_some(toggle_state_msg, |this, msg| {
 9080                    this.entry(msg, None, {
 9081                        let weak_editor = weak_editor.clone();
 9082                        let breakpoint = breakpoint.clone();
 9083                        move |_window, cx| {
 9084                            weak_editor
 9085                                .update(cx, |this, cx| {
 9086                                    this.edit_breakpoint_at_anchor(
 9087                                        anchor,
 9088                                        breakpoint.as_ref().clone(),
 9089                                        BreakpointEditAction::InvertState,
 9090                                        cx,
 9091                                    );
 9092                                })
 9093                                .log_err();
 9094                        }
 9095                    })
 9096                })
 9097                .entry(set_breakpoint_msg, None, {
 9098                    let weak_editor = weak_editor.clone();
 9099                    let breakpoint = breakpoint.clone();
 9100                    move |_window, cx| {
 9101                        weak_editor
 9102                            .update(cx, |this, cx| {
 9103                                this.edit_breakpoint_at_anchor(
 9104                                    anchor,
 9105                                    breakpoint.as_ref().clone(),
 9106                                    BreakpointEditAction::Toggle,
 9107                                    cx,
 9108                                );
 9109                            })
 9110                            .log_err();
 9111                    }
 9112                })
 9113                .entry(log_breakpoint_msg, None, {
 9114                    let breakpoint = breakpoint.clone();
 9115                    let weak_editor = weak_editor.clone();
 9116                    move |window, cx| {
 9117                        weak_editor
 9118                            .update(cx, |this, cx| {
 9119                                this.add_edit_breakpoint_block(
 9120                                    anchor,
 9121                                    breakpoint.as_ref(),
 9122                                    BreakpointPromptEditAction::Log,
 9123                                    window,
 9124                                    cx,
 9125                                );
 9126                            })
 9127                            .log_err();
 9128                    }
 9129                })
 9130                .entry(condition_breakpoint_msg, None, {
 9131                    let breakpoint = breakpoint.clone();
 9132                    let weak_editor = weak_editor.clone();
 9133                    move |window, cx| {
 9134                        weak_editor
 9135                            .update(cx, |this, cx| {
 9136                                this.add_edit_breakpoint_block(
 9137                                    anchor,
 9138                                    breakpoint.as_ref(),
 9139                                    BreakpointPromptEditAction::Condition,
 9140                                    window,
 9141                                    cx,
 9142                                );
 9143                            })
 9144                            .log_err();
 9145                    }
 9146                })
 9147                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9148                    weak_editor
 9149                        .update(cx, |this, cx| {
 9150                            this.add_edit_breakpoint_block(
 9151                                anchor,
 9152                                breakpoint.as_ref(),
 9153                                BreakpointPromptEditAction::HitCondition,
 9154                                window,
 9155                                cx,
 9156                            );
 9157                        })
 9158                        .log_err();
 9159                })
 9160        })
 9161    }
 9162
 9163    fn render_breakpoint(
 9164        &self,
 9165        position: Anchor,
 9166        row: DisplayRow,
 9167        breakpoint: &Breakpoint,
 9168        state: Option<BreakpointSessionState>,
 9169        cx: &mut Context<Self>,
 9170    ) -> IconButton {
 9171        let is_rejected = state.is_some_and(|s| !s.verified);
 9172        // Is it a breakpoint that shows up when hovering over gutter?
 9173        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9174            (false, false),
 9175            |PhantomBreakpointIndicator {
 9176                 is_active,
 9177                 display_row,
 9178                 collides_with_existing_breakpoint,
 9179             }| {
 9180                (
 9181                    is_active && display_row == row,
 9182                    collides_with_existing_breakpoint,
 9183                )
 9184            },
 9185        );
 9186
 9187        let (color, icon) = {
 9188            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9189                (false, false) => ui::IconName::DebugBreakpoint,
 9190                (true, false) => ui::IconName::DebugLogBreakpoint,
 9191                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9192                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9193            };
 9194
 9195            let theme_colors = cx.theme().colors();
 9196
 9197            let color = if is_phantom {
 9198                if collides_with_existing {
 9199                    Color::Custom(
 9200                        theme_colors
 9201                            .debugger_accent
 9202                            .blend(theme_colors.text.opacity(0.6)),
 9203                    )
 9204                } else {
 9205                    Color::Hint
 9206                }
 9207            } else if is_rejected {
 9208                Color::Disabled
 9209            } else {
 9210                Color::Debugger
 9211            };
 9212
 9213            (color, icon)
 9214        };
 9215
 9216        let breakpoint = Arc::from(breakpoint.clone());
 9217
 9218        let alt_as_text = gpui::Keystroke {
 9219            modifiers: Modifiers::secondary_key(),
 9220            ..Default::default()
 9221        };
 9222        let primary_action_text = if breakpoint.is_disabled() {
 9223            "Enable breakpoint"
 9224        } else if is_phantom && !collides_with_existing {
 9225            "Set breakpoint"
 9226        } else {
 9227            "Unset breakpoint"
 9228        };
 9229        let focus_handle = self.focus_handle.clone();
 9230
 9231        let meta = if is_rejected {
 9232            SharedString::from("No executable code is associated with this line.")
 9233        } else if collides_with_existing && !breakpoint.is_disabled() {
 9234            SharedString::from(format!(
 9235                "{alt_as_text}-click to disable,\nright-click for more options."
 9236            ))
 9237        } else {
 9238            SharedString::from("Right-click for more options.")
 9239        };
 9240        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9241            .icon_size(IconSize::XSmall)
 9242            .size(ui::ButtonSize::None)
 9243            .when(is_rejected, |this| {
 9244                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9245            })
 9246            .icon_color(color)
 9247            .style(ButtonStyle::Transparent)
 9248            .on_click(cx.listener({
 9249                move |editor, event: &ClickEvent, window, cx| {
 9250                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9251                        BreakpointEditAction::InvertState
 9252                    } else {
 9253                        BreakpointEditAction::Toggle
 9254                    };
 9255
 9256                    window.focus(&editor.focus_handle(cx), cx);
 9257                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9258                    editor.edit_breakpoint_at_anchor(
 9259                        position,
 9260                        breakpoint.as_ref().clone(),
 9261                        edit_action,
 9262                        cx,
 9263                    );
 9264                }
 9265            }))
 9266            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9267                editor.set_breakpoint_context_menu(
 9268                    row,
 9269                    Some(position),
 9270                    event.position(),
 9271                    window,
 9272                    cx,
 9273                );
 9274            }))
 9275            .tooltip(move |_window, cx| {
 9276                Tooltip::with_meta_in(
 9277                    primary_action_text,
 9278                    Some(&ToggleBreakpoint),
 9279                    meta.clone(),
 9280                    &focus_handle,
 9281                    cx,
 9282                )
 9283            })
 9284    }
 9285
 9286    fn build_tasks_context(
 9287        project: &Entity<Project>,
 9288        buffer: &Entity<Buffer>,
 9289        buffer_row: u32,
 9290        tasks: &Arc<RunnableTasks>,
 9291        cx: &mut Context<Self>,
 9292    ) -> Task<Option<task::TaskContext>> {
 9293        let position = Point::new(buffer_row, tasks.column);
 9294        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9295        let location = Location {
 9296            buffer: buffer.clone(),
 9297            range: range_start..range_start,
 9298        };
 9299        // Fill in the environmental variables from the tree-sitter captures
 9300        let mut captured_task_variables = TaskVariables::default();
 9301        for (capture_name, value) in tasks.extra_variables.clone() {
 9302            captured_task_variables.insert(
 9303                task::VariableName::Custom(capture_name.into()),
 9304                value.clone(),
 9305            );
 9306        }
 9307        project.update(cx, |project, cx| {
 9308            project.task_store().update(cx, |task_store, cx| {
 9309                task_store.task_context_for_location(captured_task_variables, location, cx)
 9310            })
 9311        })
 9312    }
 9313
 9314    pub fn context_menu_visible(&self) -> bool {
 9315        !self.edit_prediction_preview_is_active()
 9316            && self
 9317                .context_menu
 9318                .borrow()
 9319                .as_ref()
 9320                .is_some_and(|menu| menu.visible())
 9321    }
 9322
 9323    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9324        self.context_menu
 9325            .borrow()
 9326            .as_ref()
 9327            .map(|menu| menu.origin())
 9328    }
 9329
 9330    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9331        self.context_menu_options = Some(options);
 9332    }
 9333
 9334    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9335    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9336
 9337    fn render_edit_prediction_popover(
 9338        &mut self,
 9339        text_bounds: &Bounds<Pixels>,
 9340        content_origin: gpui::Point<Pixels>,
 9341        right_margin: Pixels,
 9342        editor_snapshot: &EditorSnapshot,
 9343        visible_row_range: Range<DisplayRow>,
 9344        scroll_top: ScrollOffset,
 9345        scroll_bottom: ScrollOffset,
 9346        line_layouts: &[LineWithInvisibles],
 9347        line_height: Pixels,
 9348        scroll_position: gpui::Point<ScrollOffset>,
 9349        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9350        newest_selection_head: Option<DisplayPoint>,
 9351        editor_width: Pixels,
 9352        style: &EditorStyle,
 9353        window: &mut Window,
 9354        cx: &mut App,
 9355    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9356        if self.mode().is_minimap() {
 9357            return None;
 9358        }
 9359        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9360
 9361        if self.edit_prediction_visible_in_cursor_popover(true) {
 9362            return None;
 9363        }
 9364
 9365        match &active_edit_prediction.completion {
 9366            EditPrediction::MoveWithin { target, .. } => {
 9367                let target_display_point = target.to_display_point(editor_snapshot);
 9368
 9369                if self.edit_prediction_requires_modifier() {
 9370                    if !self.edit_prediction_preview_is_active() {
 9371                        return None;
 9372                    }
 9373
 9374                    self.render_edit_prediction_modifier_jump_popover(
 9375                        text_bounds,
 9376                        content_origin,
 9377                        visible_row_range,
 9378                        line_layouts,
 9379                        line_height,
 9380                        scroll_pixel_position,
 9381                        newest_selection_head,
 9382                        target_display_point,
 9383                        window,
 9384                        cx,
 9385                    )
 9386                } else {
 9387                    self.render_edit_prediction_eager_jump_popover(
 9388                        text_bounds,
 9389                        content_origin,
 9390                        editor_snapshot,
 9391                        visible_row_range,
 9392                        scroll_top,
 9393                        scroll_bottom,
 9394                        line_height,
 9395                        scroll_pixel_position,
 9396                        target_display_point,
 9397                        editor_width,
 9398                        window,
 9399                        cx,
 9400                    )
 9401                }
 9402            }
 9403            EditPrediction::Edit {
 9404                display_mode: EditDisplayMode::Inline,
 9405                ..
 9406            } => None,
 9407            EditPrediction::Edit {
 9408                display_mode: EditDisplayMode::TabAccept,
 9409                edits,
 9410                ..
 9411            } => {
 9412                let range = &edits.first()?.0;
 9413                let target_display_point = range.end.to_display_point(editor_snapshot);
 9414
 9415                self.render_edit_prediction_end_of_line_popover(
 9416                    "Accept",
 9417                    editor_snapshot,
 9418                    visible_row_range,
 9419                    target_display_point,
 9420                    line_height,
 9421                    scroll_pixel_position,
 9422                    content_origin,
 9423                    editor_width,
 9424                    window,
 9425                    cx,
 9426                )
 9427            }
 9428            EditPrediction::Edit {
 9429                edits,
 9430                edit_preview,
 9431                display_mode: EditDisplayMode::DiffPopover,
 9432                snapshot,
 9433                ..
 9434            } => self.render_edit_prediction_diff_popover(
 9435                text_bounds,
 9436                content_origin,
 9437                right_margin,
 9438                editor_snapshot,
 9439                visible_row_range,
 9440                line_layouts,
 9441                line_height,
 9442                scroll_position,
 9443                scroll_pixel_position,
 9444                newest_selection_head,
 9445                editor_width,
 9446                style,
 9447                edits,
 9448                edit_preview,
 9449                snapshot,
 9450                window,
 9451                cx,
 9452            ),
 9453            EditPrediction::MoveOutside { snapshot, .. } => {
 9454                let mut element = self
 9455                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9456                    .into_any();
 9457
 9458                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9459                let origin_x = text_bounds.size.width - size.width - px(30.);
 9460                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9461                element.prepaint_at(origin, window, cx);
 9462
 9463                Some((element, origin))
 9464            }
 9465        }
 9466    }
 9467
 9468    fn render_edit_prediction_modifier_jump_popover(
 9469        &mut self,
 9470        text_bounds: &Bounds<Pixels>,
 9471        content_origin: gpui::Point<Pixels>,
 9472        visible_row_range: Range<DisplayRow>,
 9473        line_layouts: &[LineWithInvisibles],
 9474        line_height: Pixels,
 9475        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9476        newest_selection_head: Option<DisplayPoint>,
 9477        target_display_point: DisplayPoint,
 9478        window: &mut Window,
 9479        cx: &mut App,
 9480    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9481        let scrolled_content_origin =
 9482            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9483
 9484        const SCROLL_PADDING_Y: Pixels = px(12.);
 9485
 9486        if target_display_point.row() < visible_row_range.start {
 9487            return self.render_edit_prediction_scroll_popover(
 9488                &|_| SCROLL_PADDING_Y,
 9489                IconName::ArrowUp,
 9490                visible_row_range,
 9491                line_layouts,
 9492                newest_selection_head,
 9493                scrolled_content_origin,
 9494                window,
 9495                cx,
 9496            );
 9497        } else if target_display_point.row() >= visible_row_range.end {
 9498            return self.render_edit_prediction_scroll_popover(
 9499                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9500                IconName::ArrowDown,
 9501                visible_row_range,
 9502                line_layouts,
 9503                newest_selection_head,
 9504                scrolled_content_origin,
 9505                window,
 9506                cx,
 9507            );
 9508        }
 9509
 9510        const POLE_WIDTH: Pixels = px(2.);
 9511
 9512        let line_layout =
 9513            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9514        let target_column = target_display_point.column() as usize;
 9515
 9516        let target_x = line_layout.x_for_index(target_column);
 9517        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9518            - scroll_pixel_position.y;
 9519
 9520        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9521
 9522        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9523        border_color.l += 0.001;
 9524
 9525        let mut element = v_flex()
 9526            .items_end()
 9527            .when(flag_on_right, |el| el.items_start())
 9528            .child(if flag_on_right {
 9529                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9530                    .rounded_bl(px(0.))
 9531                    .rounded_tl(px(0.))
 9532                    .border_l_2()
 9533                    .border_color(border_color)
 9534            } else {
 9535                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9536                    .rounded_br(px(0.))
 9537                    .rounded_tr(px(0.))
 9538                    .border_r_2()
 9539                    .border_color(border_color)
 9540            })
 9541            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9542            .into_any();
 9543
 9544        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9545
 9546        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9547            - point(
 9548                if flag_on_right {
 9549                    POLE_WIDTH
 9550                } else {
 9551                    size.width - POLE_WIDTH
 9552                },
 9553                size.height - line_height,
 9554            );
 9555
 9556        origin.x = origin.x.max(content_origin.x);
 9557
 9558        element.prepaint_at(origin, window, cx);
 9559
 9560        Some((element, origin))
 9561    }
 9562
 9563    fn render_edit_prediction_scroll_popover(
 9564        &mut self,
 9565        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9566        scroll_icon: IconName,
 9567        visible_row_range: Range<DisplayRow>,
 9568        line_layouts: &[LineWithInvisibles],
 9569        newest_selection_head: Option<DisplayPoint>,
 9570        scrolled_content_origin: gpui::Point<Pixels>,
 9571        window: &mut Window,
 9572        cx: &mut App,
 9573    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9574        let mut element = self
 9575            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9576            .into_any();
 9577
 9578        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9579
 9580        let cursor = newest_selection_head?;
 9581        let cursor_row_layout =
 9582            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9583        let cursor_column = cursor.column() as usize;
 9584
 9585        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9586
 9587        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9588
 9589        element.prepaint_at(origin, window, cx);
 9590        Some((element, origin))
 9591    }
 9592
 9593    fn render_edit_prediction_eager_jump_popover(
 9594        &mut self,
 9595        text_bounds: &Bounds<Pixels>,
 9596        content_origin: gpui::Point<Pixels>,
 9597        editor_snapshot: &EditorSnapshot,
 9598        visible_row_range: Range<DisplayRow>,
 9599        scroll_top: ScrollOffset,
 9600        scroll_bottom: ScrollOffset,
 9601        line_height: Pixels,
 9602        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9603        target_display_point: DisplayPoint,
 9604        editor_width: Pixels,
 9605        window: &mut Window,
 9606        cx: &mut App,
 9607    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9608        if target_display_point.row().as_f64() < scroll_top {
 9609            let mut element = self
 9610                .render_edit_prediction_line_popover(
 9611                    "Jump to Edit",
 9612                    Some(IconName::ArrowUp),
 9613                    window,
 9614                    cx,
 9615                )
 9616                .into_any();
 9617
 9618            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9619            let offset = point(
 9620                (text_bounds.size.width - size.width) / 2.,
 9621                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9622            );
 9623
 9624            let origin = text_bounds.origin + offset;
 9625            element.prepaint_at(origin, window, cx);
 9626            Some((element, origin))
 9627        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9628            let mut element = self
 9629                .render_edit_prediction_line_popover(
 9630                    "Jump to Edit",
 9631                    Some(IconName::ArrowDown),
 9632                    window,
 9633                    cx,
 9634                )
 9635                .into_any();
 9636
 9637            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9638            let offset = point(
 9639                (text_bounds.size.width - size.width) / 2.,
 9640                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9641            );
 9642
 9643            let origin = text_bounds.origin + offset;
 9644            element.prepaint_at(origin, window, cx);
 9645            Some((element, origin))
 9646        } else {
 9647            self.render_edit_prediction_end_of_line_popover(
 9648                "Jump to Edit",
 9649                editor_snapshot,
 9650                visible_row_range,
 9651                target_display_point,
 9652                line_height,
 9653                scroll_pixel_position,
 9654                content_origin,
 9655                editor_width,
 9656                window,
 9657                cx,
 9658            )
 9659        }
 9660    }
 9661
 9662    fn render_edit_prediction_end_of_line_popover(
 9663        self: &mut Editor,
 9664        label: &'static str,
 9665        editor_snapshot: &EditorSnapshot,
 9666        visible_row_range: Range<DisplayRow>,
 9667        target_display_point: DisplayPoint,
 9668        line_height: Pixels,
 9669        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9670        content_origin: gpui::Point<Pixels>,
 9671        editor_width: Pixels,
 9672        window: &mut Window,
 9673        cx: &mut App,
 9674    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9675        let target_line_end = DisplayPoint::new(
 9676            target_display_point.row(),
 9677            editor_snapshot.line_len(target_display_point.row()),
 9678        );
 9679
 9680        let mut element = self
 9681            .render_edit_prediction_line_popover(label, None, window, cx)
 9682            .into_any();
 9683
 9684        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9685
 9686        let line_origin =
 9687            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9688
 9689        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9690        let mut origin = start_point
 9691            + line_origin
 9692            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9693        origin.x = origin.x.max(content_origin.x);
 9694
 9695        let max_x = content_origin.x + editor_width - size.width;
 9696
 9697        if origin.x > max_x {
 9698            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9699
 9700            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9701                origin.y += offset;
 9702                IconName::ArrowUp
 9703            } else {
 9704                origin.y -= offset;
 9705                IconName::ArrowDown
 9706            };
 9707
 9708            element = self
 9709                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9710                .into_any();
 9711
 9712            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9713
 9714            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9715        }
 9716
 9717        element.prepaint_at(origin, window, cx);
 9718        Some((element, origin))
 9719    }
 9720
 9721    fn render_edit_prediction_diff_popover(
 9722        self: &Editor,
 9723        text_bounds: &Bounds<Pixels>,
 9724        content_origin: gpui::Point<Pixels>,
 9725        right_margin: Pixels,
 9726        editor_snapshot: &EditorSnapshot,
 9727        visible_row_range: Range<DisplayRow>,
 9728        line_layouts: &[LineWithInvisibles],
 9729        line_height: Pixels,
 9730        scroll_position: gpui::Point<ScrollOffset>,
 9731        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9732        newest_selection_head: Option<DisplayPoint>,
 9733        editor_width: Pixels,
 9734        style: &EditorStyle,
 9735        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9736        edit_preview: &Option<language::EditPreview>,
 9737        snapshot: &language::BufferSnapshot,
 9738        window: &mut Window,
 9739        cx: &mut App,
 9740    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9741        let edit_start = edits
 9742            .first()
 9743            .unwrap()
 9744            .0
 9745            .start
 9746            .to_display_point(editor_snapshot);
 9747        let edit_end = edits
 9748            .last()
 9749            .unwrap()
 9750            .0
 9751            .end
 9752            .to_display_point(editor_snapshot);
 9753
 9754        let is_visible = visible_row_range.contains(&edit_start.row())
 9755            || visible_row_range.contains(&edit_end.row());
 9756        if !is_visible {
 9757            return None;
 9758        }
 9759
 9760        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9761            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9762        } else {
 9763            // Fallback for providers without edit_preview
 9764            crate::edit_prediction_fallback_text(edits, cx)
 9765        };
 9766
 9767        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9768        let line_count = highlighted_edits.text.lines().count();
 9769
 9770        const BORDER_WIDTH: Pixels = px(1.);
 9771
 9772        let keybind = self.render_edit_prediction_keybind(window, cx);
 9773        let has_keybind = keybind.is_some();
 9774
 9775        let mut element = h_flex()
 9776            .items_start()
 9777            .child(
 9778                h_flex()
 9779                    .bg(cx.theme().colors().editor_background)
 9780                    .border(BORDER_WIDTH)
 9781                    .shadow_xs()
 9782                    .border_color(cx.theme().colors().border)
 9783                    .rounded_l_lg()
 9784                    .when(line_count > 1, |el| el.rounded_br_lg())
 9785                    .pr_1()
 9786                    .child(styled_text),
 9787            )
 9788            .child(
 9789                h_flex()
 9790                    .h(line_height + BORDER_WIDTH * 2.)
 9791                    .px_1p5()
 9792                    .gap_1()
 9793                    // Workaround: For some reason, there's a gap if we don't do this
 9794                    .ml(-BORDER_WIDTH)
 9795                    .shadow(vec![gpui::BoxShadow {
 9796                        color: gpui::black().opacity(0.05),
 9797                        offset: point(px(1.), px(1.)),
 9798                        blur_radius: px(2.),
 9799                        spread_radius: px(0.),
 9800                    }])
 9801                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9802                    .border(BORDER_WIDTH)
 9803                    .border_color(cx.theme().colors().border)
 9804                    .rounded_r_lg()
 9805                    .id("edit_prediction_diff_popover_keybind")
 9806                    .when(!has_keybind, |el| {
 9807                        let status_colors = cx.theme().status();
 9808
 9809                        el.bg(status_colors.error_background)
 9810                            .border_color(status_colors.error.opacity(0.6))
 9811                            .child(Icon::new(IconName::Info).color(Color::Error))
 9812                            .cursor_default()
 9813                            .hoverable_tooltip(move |_window, cx| {
 9814                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9815                            })
 9816                    })
 9817                    .children(keybind),
 9818            )
 9819            .into_any();
 9820
 9821        let longest_row =
 9822            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9823        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9824            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9825        } else {
 9826            layout_line(
 9827                longest_row,
 9828                editor_snapshot,
 9829                style,
 9830                editor_width,
 9831                |_| false,
 9832                window,
 9833                cx,
 9834            )
 9835            .width
 9836        };
 9837
 9838        let viewport_bounds =
 9839            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9840                right: -right_margin,
 9841                ..Default::default()
 9842            });
 9843
 9844        let x_after_longest = Pixels::from(
 9845            ScrollPixelOffset::from(
 9846                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9847            ) - scroll_pixel_position.x,
 9848        );
 9849
 9850        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9851
 9852        // Fully visible if it can be displayed within the window (allow overlapping other
 9853        // panes). However, this is only allowed if the popover starts within text_bounds.
 9854        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9855            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9856
 9857        let mut origin = if can_position_to_the_right {
 9858            point(
 9859                x_after_longest,
 9860                text_bounds.origin.y
 9861                    + Pixels::from(
 9862                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9863                            - scroll_pixel_position.y,
 9864                    ),
 9865            )
 9866        } else {
 9867            let cursor_row = newest_selection_head.map(|head| head.row());
 9868            let above_edit = edit_start
 9869                .row()
 9870                .0
 9871                .checked_sub(line_count as u32)
 9872                .map(DisplayRow);
 9873            let below_edit = Some(edit_end.row() + 1);
 9874            let above_cursor =
 9875                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9876            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9877
 9878            // Place the edit popover adjacent to the edit if there is a location
 9879            // available that is onscreen and does not obscure the cursor. Otherwise,
 9880            // place it adjacent to the cursor.
 9881            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9882                .into_iter()
 9883                .flatten()
 9884                .find(|&start_row| {
 9885                    let end_row = start_row + line_count as u32;
 9886                    visible_row_range.contains(&start_row)
 9887                        && visible_row_range.contains(&end_row)
 9888                        && cursor_row
 9889                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9890                })?;
 9891
 9892            content_origin
 9893                + point(
 9894                    Pixels::from(-scroll_pixel_position.x),
 9895                    Pixels::from(
 9896                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9897                    ),
 9898                )
 9899        };
 9900
 9901        origin.x -= BORDER_WIDTH;
 9902
 9903        window.with_content_mask(
 9904            Some(gpui::ContentMask {
 9905                bounds: *text_bounds,
 9906            }),
 9907            |window| {
 9908                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9909            },
 9910        );
 9911
 9912        // Do not return an element, since it will already be drawn due to defer_draw.
 9913        None
 9914    }
 9915
 9916    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9917        px(30.)
 9918    }
 9919
 9920    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9921        if self.read_only(cx) {
 9922            cx.theme().players().read_only()
 9923        } else {
 9924            self.style.as_ref().unwrap().local_player
 9925        }
 9926    }
 9927
 9928    fn render_edit_prediction_inline_keystroke(
 9929        &self,
 9930        keystroke: &gpui::KeybindingKeystroke,
 9931        modifiers_color: Color,
 9932        cx: &App,
 9933    ) -> AnyElement {
 9934        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9935
 9936        h_flex()
 9937            .px_0p5()
 9938            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9939            .font(
 9940                theme_settings::ThemeSettings::get_global(cx)
 9941                    .buffer_font
 9942                    .clone(),
 9943            )
 9944            .text_size(TextSize::XSmall.rems(cx))
 9945            .child(h_flex().children(ui::render_modifiers(
 9946                keystroke.modifiers(),
 9947                PlatformStyle::platform(),
 9948                Some(modifiers_color),
 9949                Some(IconSize::XSmall.rems().into()),
 9950                true,
 9951            )))
 9952            .when(is_platform_style_mac, |parent| {
 9953                parent.child(keystroke.key().to_string())
 9954            })
 9955            .when(!is_platform_style_mac, |parent| {
 9956                parent.child(
 9957                    Key::new(ui::utils::capitalize(keystroke.key()), Some(Color::Default))
 9958                        .size(Some(IconSize::XSmall.rems().into())),
 9959                )
 9960            })
 9961            .into_any()
 9962    }
 9963
 9964    fn render_edit_prediction_popover_keystroke(
 9965        &self,
 9966        keystroke: &gpui::KeybindingKeystroke,
 9967        color: Color,
 9968        cx: &App,
 9969    ) -> AnyElement {
 9970        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9971
 9972        if keystroke.modifiers().modified() {
 9973            h_flex()
 9974                .font(
 9975                    theme_settings::ThemeSettings::get_global(cx)
 9976                        .buffer_font
 9977                        .clone(),
 9978                )
 9979                .when(is_platform_style_mac, |parent| parent.gap_1())
 9980                .child(h_flex().children(ui::render_modifiers(
 9981                    keystroke.modifiers(),
 9982                    PlatformStyle::platform(),
 9983                    Some(color),
 9984                    None,
 9985                    false,
 9986                )))
 9987                .into_any()
 9988        } else {
 9989            Key::new(ui::utils::capitalize(keystroke.key()), Some(color))
 9990                .size(Some(IconSize::XSmall.rems().into()))
 9991                .into_any_element()
 9992        }
 9993    }
 9994
 9995    fn render_edit_prediction_keybind(
 9996        &self,
 9997        window: &mut Window,
 9998        cx: &mut App,
 9999    ) -> Option<AnyElement> {
10000        let keybind_display =
10001            self.edit_prediction_keybind_display(EditPredictionKeybindSurface::Inline, window, cx);
10002        let keystroke = keybind_display.displayed_keystroke.as_ref()?;
10003
10004        let modifiers_color = if *keystroke.modifiers() == window.modifiers() {
10005            Color::Accent
10006        } else {
10007            Color::Muted
10008        };
10009
10010        Some(self.render_edit_prediction_inline_keystroke(keystroke, modifiers_color, cx))
10011    }
10012
10013    fn render_edit_prediction_line_popover(
10014        &self,
10015        label: impl Into<SharedString>,
10016        icon: Option<IconName>,
10017        window: &mut Window,
10018        cx: &mut App,
10019    ) -> Stateful<Div> {
10020        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
10021
10022        let keybind = self.render_edit_prediction_keybind(window, cx);
10023        let has_keybind = keybind.is_some();
10024        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10025
10026        h_flex()
10027            .id("ep-line-popover")
10028            .py_0p5()
10029            .pl_1()
10030            .pr(padding_right)
10031            .gap_1()
10032            .rounded_md()
10033            .border_1()
10034            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10035            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10036            .shadow_xs()
10037            .when(!has_keybind, |el| {
10038                let status_colors = cx.theme().status();
10039
10040                el.bg(status_colors.error_background)
10041                    .border_color(status_colors.error.opacity(0.6))
10042                    .pl_2()
10043                    .child(Icon::new(icons.error).color(Color::Error))
10044                    .cursor_default()
10045                    .hoverable_tooltip(move |_window, cx| {
10046                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10047                    })
10048            })
10049            .children(keybind)
10050            .child(
10051                Label::new(label)
10052                    .size(LabelSize::Small)
10053                    .when(!has_keybind, |el| {
10054                        el.color(cx.theme().status().error.into()).strikethrough()
10055                    }),
10056            )
10057            .when(!has_keybind, |el| {
10058                el.child(
10059                    h_flex().ml_1().child(
10060                        Icon::new(IconName::Info)
10061                            .size(IconSize::Small)
10062                            .color(cx.theme().status().error.into()),
10063                    ),
10064                )
10065            })
10066            .when_some(icon, |element, icon| {
10067                element.child(
10068                    div()
10069                        .mt(px(1.5))
10070                        .child(Icon::new(icon).size(IconSize::Small)),
10071                )
10072            })
10073    }
10074
10075    fn render_edit_prediction_jump_outside_popover(
10076        &self,
10077        snapshot: &BufferSnapshot,
10078        window: &mut Window,
10079        cx: &mut App,
10080    ) -> Stateful<Div> {
10081        let keybind = self.render_edit_prediction_keybind(window, cx);
10082        let has_keybind = keybind.is_some();
10083        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10084
10085        let file_name = snapshot
10086            .file()
10087            .map(|file| SharedString::new(file.file_name(cx)))
10088            .unwrap_or(SharedString::new_static("untitled"));
10089
10090        h_flex()
10091            .id("ep-jump-outside-popover")
10092            .py_1()
10093            .px_2()
10094            .gap_1()
10095            .rounded_md()
10096            .border_1()
10097            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10098            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10099            .shadow_xs()
10100            .when(!has_keybind, |el| {
10101                let status_colors = cx.theme().status();
10102
10103                el.bg(status_colors.error_background)
10104                    .border_color(status_colors.error.opacity(0.6))
10105                    .pl_2()
10106                    .child(Icon::new(icons.error).color(Color::Error))
10107                    .cursor_default()
10108                    .hoverable_tooltip(move |_window, cx| {
10109                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10110                    })
10111            })
10112            .children(keybind)
10113            .child(
10114                Label::new(file_name)
10115                    .size(LabelSize::Small)
10116                    .buffer_font(cx)
10117                    .when(!has_keybind, |el| {
10118                        el.color(cx.theme().status().error.into()).strikethrough()
10119                    }),
10120            )
10121            .when(!has_keybind, |el| {
10122                el.child(
10123                    h_flex().ml_1().child(
10124                        Icon::new(IconName::Info)
10125                            .size(IconSize::Small)
10126                            .color(cx.theme().status().error.into()),
10127                    ),
10128                )
10129            })
10130            .child(
10131                div()
10132                    .mt(px(1.5))
10133                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10134            )
10135    }
10136
10137    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10138        let accent_color = cx.theme().colors().text_accent;
10139        let editor_bg_color = cx.theme().colors().editor_background;
10140        editor_bg_color.blend(accent_color.opacity(0.1))
10141    }
10142
10143    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10144        let accent_color = cx.theme().colors().text_accent;
10145        let editor_bg_color = cx.theme().colors().editor_background;
10146        editor_bg_color.blend(accent_color.opacity(0.6))
10147    }
10148    fn get_prediction_provider_icons(
10149        provider: &Option<RegisteredEditPredictionDelegate>,
10150        cx: &App,
10151    ) -> edit_prediction_types::EditPredictionIconSet {
10152        match provider {
10153            Some(provider) => provider.provider.icons(cx),
10154            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10155        }
10156    }
10157
10158    fn render_edit_prediction_cursor_popover(
10159        &self,
10160        min_width: Pixels,
10161        max_width: Pixels,
10162        cursor_point: Point,
10163        style: &EditorStyle,
10164        window: &mut Window,
10165        cx: &mut Context<Editor>,
10166    ) -> Option<AnyElement> {
10167        let provider = self.edit_prediction_provider.as_ref()?;
10168        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10169
10170        let is_refreshing = provider.provider.is_refreshing(cx);
10171
10172        fn pending_completion_container(icon: IconName) -> Div {
10173            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10174        }
10175
10176        let completion = match &self.active_edit_prediction {
10177            Some(prediction) => {
10178                if !self.has_visible_completions_menu() {
10179                    const RADIUS: Pixels = px(6.);
10180                    const BORDER_WIDTH: Pixels = px(1.);
10181                    let keybind_display = self.edit_prediction_keybind_display(
10182                        EditPredictionKeybindSurface::CursorPopoverCompact,
10183                        window,
10184                        cx,
10185                    );
10186
10187                    return Some(
10188                        h_flex()
10189                            .elevation_2(cx)
10190                            .border(BORDER_WIDTH)
10191                            .border_color(cx.theme().colors().border)
10192                            .when(keybind_display.missing_accept_keystroke, |el| {
10193                                el.border_color(cx.theme().status().error)
10194                            })
10195                            .rounded(RADIUS)
10196                            .rounded_tl(px(0.))
10197                            .overflow_hidden()
10198                            .child(div().px_1p5().child(match &prediction.completion {
10199                                EditPrediction::MoveWithin { target, snapshot } => {
10200                                    use text::ToPoint as _;
10201                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10202                                    {
10203                                        Icon::new(icons.down)
10204                                    } else {
10205                                        Icon::new(icons.up)
10206                                    }
10207                                }
10208                                EditPrediction::MoveOutside { .. } => {
10209                                    // TODO [zeta2] custom icon for external jump?
10210                                    Icon::new(icons.base)
10211                                }
10212                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10213                            }))
10214                            .child(
10215                                h_flex()
10216                                    .gap_1()
10217                                    .py_1()
10218                                    .px_2()
10219                                    .rounded_r(RADIUS - BORDER_WIDTH)
10220                                    .border_l_1()
10221                                    .border_color(cx.theme().colors().border)
10222                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10223                                    .when(keybind_display.show_hold_label, |el| {
10224                                        el.child(
10225                                            Label::new("Hold")
10226                                                .size(LabelSize::Small)
10227                                                .when(
10228                                                    keybind_display.missing_accept_keystroke,
10229                                                    |el| el.strikethrough(),
10230                                                )
10231                                                .line_height_style(LineHeightStyle::UiLabel),
10232                                        )
10233                                    })
10234                                    .id("edit_prediction_cursor_popover_keybind")
10235                                    .when(keybind_display.missing_accept_keystroke, |el| {
10236                                        let status_colors = cx.theme().status();
10237
10238                                        el.bg(status_colors.error_background)
10239                                            .border_color(status_colors.error.opacity(0.6))
10240                                            .child(Icon::new(IconName::Info).color(Color::Error))
10241                                            .cursor_default()
10242                                            .hoverable_tooltip(move |_window, cx| {
10243                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10244                                                    .into()
10245                                            })
10246                                    })
10247                                    .when_some(
10248                                        keybind_display.displayed_keystroke.as_ref(),
10249                                        |el, compact_keystroke| {
10250                                            el.child(self.render_edit_prediction_popover_keystroke(
10251                                                compact_keystroke,
10252                                                Color::Default,
10253                                                cx,
10254                                            ))
10255                                        },
10256                                    ),
10257                            )
10258                            .into_any(),
10259                    );
10260                }
10261
10262                self.render_edit_prediction_cursor_popover_preview(
10263                    prediction,
10264                    cursor_point,
10265                    style,
10266                    cx,
10267                )?
10268            }
10269
10270            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10271                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10272                    stale_completion,
10273                    cursor_point,
10274                    style,
10275                    cx,
10276                )?,
10277
10278                None => pending_completion_container(icons.base)
10279                    .child(Label::new("...").size(LabelSize::Small)),
10280            },
10281
10282            None => pending_completion_container(icons.base)
10283                .child(Label::new("...").size(LabelSize::Small)),
10284        };
10285
10286        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10287            completion
10288                .with_animation(
10289                    "loading-completion",
10290                    Animation::new(Duration::from_secs(2))
10291                        .repeat()
10292                        .with_easing(pulsating_between(0.4, 0.8)),
10293                    |label, delta| label.opacity(delta),
10294                )
10295                .into_any_element()
10296        } else {
10297            completion.into_any_element()
10298        };
10299
10300        let has_completion = self.active_edit_prediction.is_some();
10301        let keybind_display = self.edit_prediction_keybind_display(
10302            EditPredictionKeybindSurface::CursorPopoverExpanded,
10303            window,
10304            cx,
10305        );
10306
10307        Some(
10308            h_flex()
10309                .min_w(min_width)
10310                .max_w(max_width)
10311                .flex_1()
10312                .elevation_2(cx)
10313                .border_color(cx.theme().colors().border)
10314                .child(
10315                    div()
10316                        .flex_1()
10317                        .py_1()
10318                        .px_2()
10319                        .overflow_hidden()
10320                        .child(completion),
10321                )
10322                .when_some(
10323                    keybind_display.displayed_keystroke.as_ref(),
10324                    |el, keystroke| {
10325                        let key_color = if !has_completion {
10326                            Color::Muted
10327                        } else {
10328                            Color::Default
10329                        };
10330
10331                        if keybind_display.action == EditPredictionKeybindAction::Preview {
10332                            el.child(
10333                                h_flex()
10334                                    .h_full()
10335                                    .border_l_1()
10336                                    .rounded_r_lg()
10337                                    .border_color(cx.theme().colors().border)
10338                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10339                                    .gap_1()
10340                                    .py_1()
10341                                    .px_2()
10342                                    .child(self.render_edit_prediction_popover_keystroke(
10343                                        keystroke, key_color, cx,
10344                                    ))
10345                                    .child(Label::new("Preview").into_any_element())
10346                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10347                            )
10348                        } else {
10349                            el.child(
10350                                h_flex()
10351                                    .h_full()
10352                                    .border_l_1()
10353                                    .rounded_r_lg()
10354                                    .border_color(cx.theme().colors().border)
10355                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10356                                    .gap_1()
10357                                    .py_1()
10358                                    .px_2()
10359                                    .child(self.render_edit_prediction_popover_keystroke(
10360                                        keystroke, key_color, cx,
10361                                    ))
10362                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10363                            )
10364                        }
10365                    },
10366                )
10367                .into_any(),
10368        )
10369    }
10370
10371    fn render_edit_prediction_cursor_popover_preview(
10372        &self,
10373        completion: &EditPredictionState,
10374        cursor_point: Point,
10375        style: &EditorStyle,
10376        cx: &mut Context<Editor>,
10377    ) -> Option<Div> {
10378        use text::ToPoint as _;
10379
10380        fn render_relative_row_jump(
10381            prefix: impl Into<String>,
10382            current_row: u32,
10383            target_row: u32,
10384        ) -> Div {
10385            let (row_diff, arrow) = if target_row < current_row {
10386                (current_row - target_row, IconName::ArrowUp)
10387            } else {
10388                (target_row - current_row, IconName::ArrowDown)
10389            };
10390
10391            h_flex()
10392                .child(
10393                    Label::new(format!("{}{}", prefix.into(), row_diff))
10394                        .color(Color::Muted)
10395                        .size(LabelSize::Small),
10396                )
10397                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10398        }
10399
10400        let supports_jump = self
10401            .edit_prediction_provider
10402            .as_ref()
10403            .map(|provider| provider.provider.supports_jump_to_edit())
10404            .unwrap_or(true);
10405
10406        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10407
10408        match &completion.completion {
10409            EditPrediction::MoveWithin {
10410                target, snapshot, ..
10411            } => {
10412                if !supports_jump {
10413                    return None;
10414                }
10415
10416                Some(
10417                    h_flex()
10418                        .px_2()
10419                        .gap_2()
10420                        .flex_1()
10421                        .child(
10422                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10423                                Icon::new(icons.down)
10424                            } else {
10425                                Icon::new(icons.up)
10426                            },
10427                        )
10428                        .child(Label::new("Jump to Edit")),
10429                )
10430            }
10431            EditPrediction::MoveOutside { snapshot, .. } => {
10432                let file_name = snapshot
10433                    .file()
10434                    .map(|file| file.file_name(cx))
10435                    .unwrap_or("untitled");
10436                Some(
10437                    h_flex()
10438                        .px_2()
10439                        .gap_2()
10440                        .flex_1()
10441                        .child(Icon::new(icons.base))
10442                        .child(Label::new(format!("Jump to {file_name}"))),
10443                )
10444            }
10445            EditPrediction::Edit {
10446                edits,
10447                edit_preview,
10448                snapshot,
10449                ..
10450            } => {
10451                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10452
10453                let (highlighted_edits, has_more_lines) =
10454                    if let Some(edit_preview) = edit_preview.as_ref() {
10455                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10456                            .first_line_preview()
10457                    } else {
10458                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10459                    };
10460
10461                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10462                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10463
10464                let preview = h_flex()
10465                    .gap_1()
10466                    .min_w_16()
10467                    .child(styled_text)
10468                    .when(has_more_lines, |parent| parent.child(""));
10469
10470                let left = if supports_jump && first_edit_row != cursor_point.row {
10471                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10472                        .into_any_element()
10473                } else {
10474                    Icon::new(icons.base).into_any_element()
10475                };
10476
10477                Some(
10478                    h_flex()
10479                        .h_full()
10480                        .flex_1()
10481                        .gap_2()
10482                        .pr_1()
10483                        .overflow_x_hidden()
10484                        .font(
10485                            theme_settings::ThemeSettings::get_global(cx)
10486                                .buffer_font
10487                                .clone(),
10488                        )
10489                        .child(left)
10490                        .child(preview),
10491                )
10492            }
10493        }
10494    }
10495
10496    pub fn render_context_menu(
10497        &mut self,
10498        max_height_in_lines: u32,
10499        window: &mut Window,
10500        cx: &mut Context<Editor>,
10501    ) -> Option<AnyElement> {
10502        let menu = self.context_menu.borrow();
10503        let menu = menu.as_ref()?;
10504        if !menu.visible() {
10505            return None;
10506        };
10507        self.style
10508            .as_ref()
10509            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10510    }
10511
10512    fn render_context_menu_aside(
10513        &mut self,
10514        max_size: Size<Pixels>,
10515        window: &mut Window,
10516        cx: &mut Context<Editor>,
10517    ) -> Option<AnyElement> {
10518        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10519            if menu.visible() {
10520                menu.render_aside(max_size, window, cx)
10521            } else {
10522                None
10523            }
10524        })
10525    }
10526
10527    fn hide_context_menu(
10528        &mut self,
10529        window: &mut Window,
10530        cx: &mut Context<Self>,
10531    ) -> Option<CodeContextMenu> {
10532        cx.notify();
10533        self.completion_tasks.clear();
10534        let context_menu = self.context_menu.borrow_mut().take();
10535        self.stale_edit_prediction_in_menu.take();
10536        self.update_visible_edit_prediction(window, cx);
10537        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10538            && let Some(completion_provider) = &self.completion_provider
10539        {
10540            completion_provider.selection_changed(None, window, cx);
10541        }
10542        context_menu
10543    }
10544
10545    fn show_snippet_choices(
10546        &mut self,
10547        choices: &Vec<String>,
10548        selection: Range<Anchor>,
10549        cx: &mut Context<Self>,
10550    ) {
10551        let Some((_, buffer, _)) = self
10552            .buffer()
10553            .read(cx)
10554            .excerpt_containing(selection.start, cx)
10555        else {
10556            return;
10557        };
10558        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10559        else {
10560            return;
10561        };
10562        if buffer != end_buffer {
10563            log::error!("expected anchor range to have matching buffer IDs");
10564            return;
10565        }
10566
10567        let id = post_inc(&mut self.next_completion_id);
10568        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10569        let mut context_menu = self.context_menu.borrow_mut();
10570        let old_menu = context_menu.take();
10571        *context_menu = Some(CodeContextMenu::Completions(
10572            CompletionsMenu::new_snippet_choices(
10573                id,
10574                true,
10575                choices,
10576                selection,
10577                buffer,
10578                old_menu.map(|menu| menu.primary_scroll_handle()),
10579                snippet_sort_order,
10580            ),
10581        ));
10582    }
10583
10584    pub fn insert_snippet(
10585        &mut self,
10586        insertion_ranges: &[Range<MultiBufferOffset>],
10587        snippet: Snippet,
10588        window: &mut Window,
10589        cx: &mut Context<Self>,
10590    ) -> Result<()> {
10591        struct Tabstop<T> {
10592            is_end_tabstop: bool,
10593            ranges: Vec<Range<T>>,
10594            choices: Option<Vec<String>>,
10595        }
10596
10597        let tabstops = self.buffer.update(cx, |buffer, cx| {
10598            let snippet_text: Arc<str> = snippet.text.clone().into();
10599            let edits = insertion_ranges
10600                .iter()
10601                .cloned()
10602                .map(|range| (range, snippet_text.clone()));
10603            let autoindent_mode = AutoindentMode::Block {
10604                original_indent_columns: Vec::new(),
10605            };
10606            buffer.edit(edits, Some(autoindent_mode), cx);
10607
10608            let snapshot = &*buffer.read(cx);
10609            let snippet = &snippet;
10610            snippet
10611                .tabstops
10612                .iter()
10613                .map(|tabstop| {
10614                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10615                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10616                    });
10617                    let mut tabstop_ranges = tabstop
10618                        .ranges
10619                        .iter()
10620                        .flat_map(|tabstop_range| {
10621                            let mut delta = 0_isize;
10622                            insertion_ranges.iter().map(move |insertion_range| {
10623                                let insertion_start = insertion_range.start + delta;
10624                                delta += snippet.text.len() as isize
10625                                    - (insertion_range.end - insertion_range.start) as isize;
10626
10627                                let start =
10628                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10629                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10630                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10631                            })
10632                        })
10633                        .collect::<Vec<_>>();
10634                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10635
10636                    Tabstop {
10637                        is_end_tabstop,
10638                        ranges: tabstop_ranges,
10639                        choices: tabstop.choices.clone(),
10640                    }
10641                })
10642                .collect::<Vec<_>>()
10643        });
10644        if let Some(tabstop) = tabstops.first() {
10645            self.change_selections(Default::default(), window, cx, |s| {
10646                // Reverse order so that the first range is the newest created selection.
10647                // Completions will use it and autoscroll will prioritize it.
10648                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10649            });
10650
10651            if let Some(choices) = &tabstop.choices
10652                && let Some(selection) = tabstop.ranges.first()
10653            {
10654                self.show_snippet_choices(choices, selection.clone(), cx)
10655            }
10656
10657            // If we're already at the last tabstop and it's at the end of the snippet,
10658            // we're done, we don't need to keep the state around.
10659            if !tabstop.is_end_tabstop {
10660                let choices = tabstops
10661                    .iter()
10662                    .map(|tabstop| tabstop.choices.clone())
10663                    .collect();
10664
10665                let ranges = tabstops
10666                    .into_iter()
10667                    .map(|tabstop| tabstop.ranges)
10668                    .collect::<Vec<_>>();
10669
10670                self.snippet_stack.push(SnippetState {
10671                    active_index: 0,
10672                    ranges,
10673                    choices,
10674                });
10675            }
10676
10677            // Check whether the just-entered snippet ends with an auto-closable bracket.
10678            if self.autoclose_regions.is_empty() {
10679                let snapshot = self.buffer.read(cx).snapshot(cx);
10680                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10681                    let selection_head = selection.head();
10682                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10683                        continue;
10684                    };
10685
10686                    let mut bracket_pair = None;
10687                    let max_lookup_length = scope
10688                        .brackets()
10689                        .map(|(pair, _)| {
10690                            pair.start
10691                                .as_str()
10692                                .chars()
10693                                .count()
10694                                .max(pair.end.as_str().chars().count())
10695                        })
10696                        .max();
10697                    if let Some(max_lookup_length) = max_lookup_length {
10698                        let next_text = snapshot
10699                            .chars_at(selection_head)
10700                            .take(max_lookup_length)
10701                            .collect::<String>();
10702                        let prev_text = snapshot
10703                            .reversed_chars_at(selection_head)
10704                            .take(max_lookup_length)
10705                            .collect::<String>();
10706
10707                        for (pair, enabled) in scope.brackets() {
10708                            if enabled
10709                                && pair.close
10710                                && prev_text.starts_with(pair.start.as_str())
10711                                && next_text.starts_with(pair.end.as_str())
10712                            {
10713                                bracket_pair = Some(pair.clone());
10714                                break;
10715                            }
10716                        }
10717                    }
10718
10719                    if let Some(pair) = bracket_pair {
10720                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10721                        let autoclose_enabled =
10722                            self.use_autoclose && snapshot_settings.use_autoclose;
10723                        if autoclose_enabled {
10724                            let start = snapshot.anchor_after(selection_head);
10725                            let end = snapshot.anchor_after(selection_head);
10726                            self.autoclose_regions.push(AutocloseRegion {
10727                                selection_id: selection.id,
10728                                range: start..end,
10729                                pair,
10730                            });
10731                        }
10732                    }
10733                }
10734            }
10735        }
10736        Ok(())
10737    }
10738
10739    pub fn move_to_next_snippet_tabstop(
10740        &mut self,
10741        window: &mut Window,
10742        cx: &mut Context<Self>,
10743    ) -> bool {
10744        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10745    }
10746
10747    pub fn move_to_prev_snippet_tabstop(
10748        &mut self,
10749        window: &mut Window,
10750        cx: &mut Context<Self>,
10751    ) -> bool {
10752        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10753    }
10754
10755    pub fn move_to_snippet_tabstop(
10756        &mut self,
10757        bias: Bias,
10758        window: &mut Window,
10759        cx: &mut Context<Self>,
10760    ) -> bool {
10761        if let Some(mut snippet) = self.snippet_stack.pop() {
10762            match bias {
10763                Bias::Left => {
10764                    if snippet.active_index > 0 {
10765                        snippet.active_index -= 1;
10766                    } else {
10767                        self.snippet_stack.push(snippet);
10768                        return false;
10769                    }
10770                }
10771                Bias::Right => {
10772                    if snippet.active_index + 1 < snippet.ranges.len() {
10773                        snippet.active_index += 1;
10774                    } else {
10775                        self.snippet_stack.push(snippet);
10776                        return false;
10777                    }
10778                }
10779            }
10780            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10781                self.change_selections(Default::default(), window, cx, |s| {
10782                    // Reverse order so that the first range is the newest created selection.
10783                    // Completions will use it and autoscroll will prioritize it.
10784                    s.select_ranges(current_ranges.iter().rev().cloned())
10785                });
10786
10787                if let Some(choices) = &snippet.choices[snippet.active_index]
10788                    && let Some(selection) = current_ranges.first()
10789                {
10790                    self.show_snippet_choices(choices, selection.clone(), cx);
10791                }
10792
10793                // If snippet state is not at the last tabstop, push it back on the stack
10794                if snippet.active_index + 1 < snippet.ranges.len() {
10795                    self.snippet_stack.push(snippet);
10796                }
10797                return true;
10798            }
10799        }
10800
10801        false
10802    }
10803
10804    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10805        self.transact(window, cx, |this, window, cx| {
10806            this.select_all(&SelectAll, window, cx);
10807            this.insert("", window, cx);
10808        });
10809    }
10810
10811    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10812        if self.read_only(cx) {
10813            return;
10814        }
10815        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10816        self.transact(window, cx, |this, window, cx| {
10817            this.select_autoclose_pair(window, cx);
10818
10819            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10820
10821            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10822            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10823            for selection in &mut selections {
10824                if selection.is_empty() {
10825                    let old_head = selection.head();
10826                    let mut new_head =
10827                        movement::left(&display_map, old_head.to_display_point(&display_map))
10828                            .to_point(&display_map);
10829                    if let Some((buffer, line_buffer_range)) = display_map
10830                        .buffer_snapshot()
10831                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10832                    {
10833                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10834                        let indent_len = match indent_size.kind {
10835                            IndentKind::Space => {
10836                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10837                            }
10838                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10839                        };
10840                        if old_head.column <= indent_size.len && old_head.column > 0 {
10841                            let indent_len = indent_len.get();
10842                            new_head = cmp::min(
10843                                new_head,
10844                                MultiBufferPoint::new(
10845                                    old_head.row,
10846                                    ((old_head.column - 1) / indent_len) * indent_len,
10847                                ),
10848                            );
10849                        }
10850                    }
10851
10852                    selection.set_head(new_head, SelectionGoal::None);
10853                }
10854            }
10855
10856            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10857            this.insert("", window, cx);
10858            linked_edits.apply_with_left_expansion(cx);
10859            this.refresh_edit_prediction(true, false, window, cx);
10860            refresh_linked_ranges(this, window, cx);
10861        });
10862    }
10863
10864    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10865        if self.read_only(cx) {
10866            return;
10867        }
10868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10869        self.transact(window, cx, |this, window, cx| {
10870            this.change_selections(Default::default(), window, cx, |s| {
10871                s.move_with(&mut |map, selection| {
10872                    if selection.is_empty() {
10873                        let cursor = movement::right(map, selection.head());
10874                        selection.end = cursor;
10875                        selection.reversed = true;
10876                        selection.goal = SelectionGoal::None;
10877                    }
10878                })
10879            });
10880            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10881            this.insert("", window, cx);
10882            linked_edits.apply(cx);
10883            this.refresh_edit_prediction(true, false, window, cx);
10884            refresh_linked_ranges(this, window, cx);
10885        });
10886    }
10887
10888    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10889        if self.mode.is_single_line() {
10890            cx.propagate();
10891            return;
10892        }
10893
10894        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10895        if self.move_to_prev_snippet_tabstop(window, cx) {
10896            return;
10897        }
10898        self.outdent(&Outdent, window, cx);
10899    }
10900
10901    pub fn next_snippet_tabstop(
10902        &mut self,
10903        _: &NextSnippetTabstop,
10904        window: &mut Window,
10905        cx: &mut Context<Self>,
10906    ) {
10907        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10908            cx.propagate();
10909            return;
10910        }
10911
10912        if self.move_to_next_snippet_tabstop(window, cx) {
10913            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10914            return;
10915        }
10916        cx.propagate();
10917    }
10918
10919    pub fn previous_snippet_tabstop(
10920        &mut self,
10921        _: &PreviousSnippetTabstop,
10922        window: &mut Window,
10923        cx: &mut Context<Self>,
10924    ) {
10925        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10926            cx.propagate();
10927            return;
10928        }
10929
10930        if self.move_to_prev_snippet_tabstop(window, cx) {
10931            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10932            return;
10933        }
10934        cx.propagate();
10935    }
10936
10937    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10938        if self.mode.is_single_line() {
10939            cx.propagate();
10940            return;
10941        }
10942
10943        if self.move_to_next_snippet_tabstop(window, cx) {
10944            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10945            return;
10946        }
10947        if self.read_only(cx) {
10948            return;
10949        }
10950        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10951        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10952        let buffer = self.buffer.read(cx);
10953        let snapshot = buffer.snapshot(cx);
10954        let rows_iter = selections.iter().map(|s| s.head().row);
10955        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10956
10957        let has_some_cursor_in_whitespace = selections
10958            .iter()
10959            .filter(|selection| selection.is_empty())
10960            .any(|selection| {
10961                let cursor = selection.head();
10962                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10963                cursor.column < current_indent.len
10964            });
10965
10966        let mut edits = Vec::new();
10967        let mut prev_edited_row = 0;
10968        let mut row_delta = 0;
10969        for selection in &mut selections {
10970            if selection.start.row != prev_edited_row {
10971                row_delta = 0;
10972            }
10973            prev_edited_row = selection.end.row;
10974
10975            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10976            if selection.is_empty() {
10977                let cursor = selection.head();
10978                let settings = buffer.language_settings_at(cursor, cx);
10979                if settings.indent_list_on_tab {
10980                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10981                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10982                            row_delta = Self::indent_selection(
10983                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10984                            );
10985                            continue;
10986                        }
10987                    }
10988                }
10989            }
10990
10991            // If the selection is non-empty, then increase the indentation of the selected lines.
10992            if !selection.is_empty() {
10993                row_delta =
10994                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10995                continue;
10996            }
10997
10998            let cursor = selection.head();
10999            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
11000            if let Some(suggested_indent) =
11001                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
11002            {
11003                // Don't do anything if already at suggested indent
11004                // and there is any other cursor which is not
11005                if has_some_cursor_in_whitespace
11006                    && cursor.column == current_indent.len
11007                    && current_indent.len == suggested_indent.len
11008                {
11009                    continue;
11010                }
11011
11012                // Adjust line and move cursor to suggested indent
11013                // if cursor is not at suggested indent
11014                if cursor.column < suggested_indent.len
11015                    && cursor.column <= current_indent.len
11016                    && current_indent.len <= suggested_indent.len
11017                {
11018                    selection.start = Point::new(cursor.row, suggested_indent.len);
11019                    selection.end = selection.start;
11020                    if row_delta == 0 {
11021                        edits.extend(Buffer::edit_for_indent_size_adjustment(
11022                            cursor.row,
11023                            current_indent,
11024                            suggested_indent,
11025                        ));
11026                        row_delta = suggested_indent.len - current_indent.len;
11027                    }
11028                    continue;
11029                }
11030
11031                // If current indent is more than suggested indent
11032                // only move cursor to current indent and skip indent
11033                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
11034                    selection.start = Point::new(cursor.row, current_indent.len);
11035                    selection.end = selection.start;
11036                    continue;
11037                }
11038            }
11039
11040            // Otherwise, insert a hard or soft tab.
11041            let settings = buffer.language_settings_at(cursor, cx);
11042            let tab_size = if settings.hard_tabs {
11043                IndentSize::tab()
11044            } else {
11045                let tab_size = settings.tab_size.get();
11046                let indent_remainder = snapshot
11047                    .text_for_range(Point::new(cursor.row, 0)..cursor)
11048                    .flat_map(str::chars)
11049                    .fold(row_delta % tab_size, |counter: u32, c| {
11050                        if c == '\t' {
11051                            0
11052                        } else {
11053                            (counter + 1) % tab_size
11054                        }
11055                    });
11056
11057                let chars_to_next_tab_stop = tab_size - indent_remainder;
11058                IndentSize::spaces(chars_to_next_tab_stop)
11059            };
11060            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11061            selection.end = selection.start;
11062            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11063            row_delta += tab_size.len;
11064        }
11065
11066        self.transact(window, cx, |this, window, cx| {
11067            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11068            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11069            this.refresh_edit_prediction(true, false, window, cx);
11070        });
11071    }
11072
11073    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11074        if self.read_only(cx) {
11075            return;
11076        }
11077        if self.mode.is_single_line() {
11078            cx.propagate();
11079            return;
11080        }
11081
11082        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11083        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11084        let mut prev_edited_row = 0;
11085        let mut row_delta = 0;
11086        let mut edits = Vec::new();
11087        let buffer = self.buffer.read(cx);
11088        let snapshot = buffer.snapshot(cx);
11089        for selection in &mut selections {
11090            if selection.start.row != prev_edited_row {
11091                row_delta = 0;
11092            }
11093            prev_edited_row = selection.end.row;
11094
11095            row_delta =
11096                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11097        }
11098
11099        self.transact(window, cx, |this, window, cx| {
11100            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11101            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11102        });
11103    }
11104
11105    fn indent_selection(
11106        buffer: &MultiBuffer,
11107        snapshot: &MultiBufferSnapshot,
11108        selection: &mut Selection<Point>,
11109        edits: &mut Vec<(Range<Point>, String)>,
11110        delta_for_start_row: u32,
11111        cx: &App,
11112    ) -> u32 {
11113        let settings = buffer.language_settings_at(selection.start, cx);
11114        let tab_size = settings.tab_size.get();
11115        let indent_kind = if settings.hard_tabs {
11116            IndentKind::Tab
11117        } else {
11118            IndentKind::Space
11119        };
11120        let mut start_row = selection.start.row;
11121        let mut end_row = selection.end.row + 1;
11122
11123        // If a selection ends at the beginning of a line, don't indent
11124        // that last line.
11125        if selection.end.column == 0 && selection.end.row > selection.start.row {
11126            end_row -= 1;
11127        }
11128
11129        // Avoid re-indenting a row that has already been indented by a
11130        // previous selection, but still update this selection's column
11131        // to reflect that indentation.
11132        if delta_for_start_row > 0 {
11133            start_row += 1;
11134            selection.start.column += delta_for_start_row;
11135            if selection.end.row == selection.start.row {
11136                selection.end.column += delta_for_start_row;
11137            }
11138        }
11139
11140        let mut delta_for_end_row = 0;
11141        let has_multiple_rows = start_row + 1 != end_row;
11142        for row in start_row..end_row {
11143            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11144            let indent_delta = match (current_indent.kind, indent_kind) {
11145                (IndentKind::Space, IndentKind::Space) => {
11146                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11147                    IndentSize::spaces(columns_to_next_tab_stop)
11148                }
11149                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11150                (_, IndentKind::Tab) => IndentSize::tab(),
11151            };
11152
11153            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11154                0
11155            } else {
11156                selection.start.column
11157            };
11158            let row_start = Point::new(row, start);
11159            edits.push((
11160                row_start..row_start,
11161                indent_delta.chars().collect::<String>(),
11162            ));
11163
11164            // Update this selection's endpoints to reflect the indentation.
11165            if row == selection.start.row {
11166                selection.start.column += indent_delta.len;
11167            }
11168            if row == selection.end.row {
11169                selection.end.column += indent_delta.len;
11170                delta_for_end_row = indent_delta.len;
11171            }
11172        }
11173
11174        if selection.start.row == selection.end.row {
11175            delta_for_start_row + delta_for_end_row
11176        } else {
11177            delta_for_end_row
11178        }
11179    }
11180
11181    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11182        if self.read_only(cx) {
11183            return;
11184        }
11185        if self.mode.is_single_line() {
11186            cx.propagate();
11187            return;
11188        }
11189
11190        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11191        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11192        let selections = self.selections.all::<Point>(&display_map);
11193        let mut deletion_ranges = Vec::new();
11194        let mut last_outdent = None;
11195        {
11196            let buffer = self.buffer.read(cx);
11197            let snapshot = buffer.snapshot(cx);
11198            for selection in &selections {
11199                let settings = buffer.language_settings_at(selection.start, cx);
11200                let tab_size = settings.tab_size.get();
11201                let mut rows = selection.spanned_rows(false, &display_map);
11202
11203                // Avoid re-outdenting a row that has already been outdented by a
11204                // previous selection.
11205                if let Some(last_row) = last_outdent
11206                    && last_row == rows.start
11207                {
11208                    rows.start = rows.start.next_row();
11209                }
11210                let has_multiple_rows = rows.len() > 1;
11211                for row in rows.iter_rows() {
11212                    let indent_size = snapshot.indent_size_for_line(row);
11213                    if indent_size.len > 0 {
11214                        let deletion_len = match indent_size.kind {
11215                            IndentKind::Space => {
11216                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11217                                if columns_to_prev_tab_stop == 0 {
11218                                    tab_size
11219                                } else {
11220                                    columns_to_prev_tab_stop
11221                                }
11222                            }
11223                            IndentKind::Tab => 1,
11224                        };
11225                        let start = if has_multiple_rows
11226                            || deletion_len > selection.start.column
11227                            || indent_size.len < selection.start.column
11228                        {
11229                            0
11230                        } else {
11231                            selection.start.column - deletion_len
11232                        };
11233                        deletion_ranges.push(
11234                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11235                        );
11236                        last_outdent = Some(row);
11237                    }
11238                }
11239            }
11240        }
11241
11242        self.transact(window, cx, |this, window, cx| {
11243            this.buffer.update(cx, |buffer, cx| {
11244                let empty_str: Arc<str> = Arc::default();
11245                buffer.edit(
11246                    deletion_ranges
11247                        .into_iter()
11248                        .map(|range| (range, empty_str.clone())),
11249                    None,
11250                    cx,
11251                );
11252            });
11253            let selections = this
11254                .selections
11255                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11256            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11257        });
11258    }
11259
11260    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11261        if self.read_only(cx) {
11262            return;
11263        }
11264        if self.mode.is_single_line() {
11265            cx.propagate();
11266            return;
11267        }
11268
11269        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11270        let selections = self
11271            .selections
11272            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11273            .into_iter()
11274            .map(|s| s.range());
11275
11276        self.transact(window, cx, |this, window, cx| {
11277            this.buffer.update(cx, |buffer, cx| {
11278                buffer.autoindent_ranges(selections, cx);
11279            });
11280            let selections = this
11281                .selections
11282                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11283            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11284        });
11285    }
11286
11287    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11288        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11289        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11290        let selections = self.selections.all::<Point>(&display_map);
11291
11292        let mut new_cursors = Vec::new();
11293        let mut edit_ranges = Vec::new();
11294        let mut selections = selections.iter().peekable();
11295        while let Some(selection) = selections.next() {
11296            let mut rows = selection.spanned_rows(false, &display_map);
11297
11298            // Accumulate contiguous regions of rows that we want to delete.
11299            while let Some(next_selection) = selections.peek() {
11300                let next_rows = next_selection.spanned_rows(false, &display_map);
11301                if next_rows.start <= rows.end {
11302                    rows.end = next_rows.end;
11303                    selections.next().unwrap();
11304                } else {
11305                    break;
11306                }
11307            }
11308
11309            let buffer = display_map.buffer_snapshot();
11310            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11311            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11312                // If there's a line after the range, delete the \n from the end of the row range
11313                (
11314                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11315                    rows.end,
11316                )
11317            } else {
11318                // If there isn't a line after the range, delete the \n from the line before the
11319                // start of the row range
11320                edit_start = edit_start.saturating_sub_usize(1);
11321                (buffer.len(), rows.start.previous_row())
11322            };
11323
11324            let text_layout_details = self.text_layout_details(window, cx);
11325            let x = display_map.x_for_display_point(
11326                selection.head().to_display_point(&display_map),
11327                &text_layout_details,
11328            );
11329            let row = Point::new(target_row.0, 0)
11330                .to_display_point(&display_map)
11331                .row();
11332            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11333
11334            new_cursors.push((
11335                selection.id,
11336                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11337                SelectionGoal::None,
11338            ));
11339            edit_ranges.push(edit_start..edit_end);
11340        }
11341
11342        self.transact(window, cx, |this, window, cx| {
11343            let buffer = this.buffer.update(cx, |buffer, cx| {
11344                let empty_str: Arc<str> = Arc::default();
11345                buffer.edit(
11346                    edit_ranges
11347                        .into_iter()
11348                        .map(|range| (range, empty_str.clone())),
11349                    None,
11350                    cx,
11351                );
11352                buffer.snapshot(cx)
11353            });
11354            let new_selections = new_cursors
11355                .into_iter()
11356                .map(|(id, cursor, goal)| {
11357                    let cursor = cursor.to_point(&buffer);
11358                    Selection {
11359                        id,
11360                        start: cursor,
11361                        end: cursor,
11362                        reversed: false,
11363                        goal,
11364                    }
11365                })
11366                .collect();
11367
11368            this.change_selections(Default::default(), window, cx, |s| {
11369                s.select(new_selections);
11370            });
11371        });
11372    }
11373
11374    pub fn join_lines_impl(
11375        &mut self,
11376        insert_whitespace: bool,
11377        window: &mut Window,
11378        cx: &mut Context<Self>,
11379    ) {
11380        if self.read_only(cx) {
11381            return;
11382        }
11383        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11384        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11385            let start = MultiBufferRow(selection.start.row);
11386            // Treat single line selections as if they include the next line. Otherwise this action
11387            // would do nothing for single line selections individual cursors.
11388            let end = if selection.start.row == selection.end.row {
11389                MultiBufferRow(selection.start.row + 1)
11390            } else if selection.end.column == 0 {
11391                // If the selection ends at the start of a line, it's logically at the end of the
11392                // previous line (plus its newline).
11393                // Don't include the end line unless there's only one line selected.
11394                if selection.start.row + 1 == selection.end.row {
11395                    MultiBufferRow(selection.end.row)
11396                } else {
11397                    MultiBufferRow(selection.end.row - 1)
11398                }
11399            } else {
11400                MultiBufferRow(selection.end.row)
11401            };
11402
11403            if let Some(last_row_range) = row_ranges.last_mut()
11404                && start <= last_row_range.end
11405            {
11406                last_row_range.end = end;
11407                continue;
11408            }
11409            row_ranges.push(start..end);
11410        }
11411
11412        let snapshot = self.buffer.read(cx).snapshot(cx);
11413        let mut cursor_positions = Vec::new();
11414        for row_range in &row_ranges {
11415            let anchor = snapshot.anchor_before(Point::new(
11416                row_range.end.previous_row().0,
11417                snapshot.line_len(row_range.end.previous_row()),
11418            ));
11419            cursor_positions.push(anchor..anchor);
11420        }
11421
11422        self.transact(window, cx, |this, window, cx| {
11423            for row_range in row_ranges.into_iter().rev() {
11424                for row in row_range.iter_rows().rev() {
11425                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11426                    let next_line_row = row.next_row();
11427                    let indent = snapshot.indent_size_for_line(next_line_row);
11428                    let mut join_start_column = indent.len;
11429
11430                    if let Some(language_scope) =
11431                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11432                    {
11433                        let line_end =
11434                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11435                        let line_text_after_indent = snapshot
11436                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11437                            .collect::<String>();
11438
11439                        if !line_text_after_indent.is_empty() {
11440                            let block_prefix = language_scope
11441                                .block_comment()
11442                                .map(|c| c.prefix.as_ref())
11443                                .filter(|p| !p.is_empty());
11444                            let doc_prefix = language_scope
11445                                .documentation_comment()
11446                                .map(|c| c.prefix.as_ref())
11447                                .filter(|p| !p.is_empty());
11448                            let all_prefixes = language_scope
11449                                .line_comment_prefixes()
11450                                .iter()
11451                                .map(|p| p.as_ref())
11452                                .chain(block_prefix)
11453                                .chain(doc_prefix)
11454                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11455
11456                            let mut longest_prefix_len = None;
11457                            for prefix in all_prefixes {
11458                                let trimmed = prefix.trim_end();
11459                                if line_text_after_indent.starts_with(trimmed) {
11460                                    let candidate_len =
11461                                        if line_text_after_indent.starts_with(prefix) {
11462                                            prefix.len()
11463                                        } else {
11464                                            trimmed.len()
11465                                        };
11466                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11467                                        longest_prefix_len = Some(candidate_len);
11468                                    }
11469                                }
11470                            }
11471
11472                            if let Some(prefix_len) = longest_prefix_len {
11473                                join_start_column =
11474                                    join_start_column.saturating_add(prefix_len as u32);
11475                            }
11476                        }
11477                    }
11478
11479                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11480
11481                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11482                        && insert_whitespace
11483                    {
11484                        " "
11485                    } else {
11486                        ""
11487                    };
11488
11489                    this.buffer.update(cx, |buffer, cx| {
11490                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11491                    });
11492                }
11493            }
11494
11495            this.change_selections(Default::default(), window, cx, |s| {
11496                s.select_anchor_ranges(cursor_positions)
11497            });
11498        });
11499    }
11500
11501    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11502        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11503        self.join_lines_impl(true, window, cx);
11504    }
11505
11506    pub fn sort_lines_case_sensitive(
11507        &mut self,
11508        _: &SortLinesCaseSensitive,
11509        window: &mut Window,
11510        cx: &mut Context<Self>,
11511    ) {
11512        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11513    }
11514
11515    pub fn sort_lines_by_length(
11516        &mut self,
11517        _: &SortLinesByLength,
11518        window: &mut Window,
11519        cx: &mut Context<Self>,
11520    ) {
11521        self.manipulate_immutable_lines(window, cx, |lines| {
11522            lines.sort_by_key(|&line| line.chars().count())
11523        })
11524    }
11525
11526    pub fn sort_lines_case_insensitive(
11527        &mut self,
11528        _: &SortLinesCaseInsensitive,
11529        window: &mut Window,
11530        cx: &mut Context<Self>,
11531    ) {
11532        self.manipulate_immutable_lines(window, cx, |lines| {
11533            lines.sort_by_key(|line| line.to_lowercase())
11534        })
11535    }
11536
11537    pub fn unique_lines_case_insensitive(
11538        &mut self,
11539        _: &UniqueLinesCaseInsensitive,
11540        window: &mut Window,
11541        cx: &mut Context<Self>,
11542    ) {
11543        self.manipulate_immutable_lines(window, cx, |lines| {
11544            let mut seen = HashSet::default();
11545            lines.retain(|line| seen.insert(line.to_lowercase()));
11546        })
11547    }
11548
11549    pub fn unique_lines_case_sensitive(
11550        &mut self,
11551        _: &UniqueLinesCaseSensitive,
11552        window: &mut Window,
11553        cx: &mut Context<Self>,
11554    ) {
11555        self.manipulate_immutable_lines(window, cx, |lines| {
11556            let mut seen = HashSet::default();
11557            lines.retain(|line| seen.insert(*line));
11558        })
11559    }
11560
11561    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11562        let snapshot = self.buffer.read(cx).snapshot(cx);
11563        for selection in self.selections.disjoint_anchors_arc().iter() {
11564            if snapshot
11565                .language_at(selection.start)
11566                .and_then(|lang| lang.config().wrap_characters.as_ref())
11567                .is_some()
11568            {
11569                return true;
11570            }
11571        }
11572        false
11573    }
11574
11575    fn wrap_selections_in_tag(
11576        &mut self,
11577        _: &WrapSelectionsInTag,
11578        window: &mut Window,
11579        cx: &mut Context<Self>,
11580    ) {
11581        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11582
11583        let snapshot = self.buffer.read(cx).snapshot(cx);
11584
11585        let mut edits = Vec::new();
11586        let mut boundaries = Vec::new();
11587
11588        for selection in self
11589            .selections
11590            .all_adjusted(&self.display_snapshot(cx))
11591            .iter()
11592        {
11593            let Some(wrap_config) = snapshot
11594                .language_at(selection.start)
11595                .and_then(|lang| lang.config().wrap_characters.clone())
11596            else {
11597                continue;
11598            };
11599
11600            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11601            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11602
11603            let start_before = snapshot.anchor_before(selection.start);
11604            let end_after = snapshot.anchor_after(selection.end);
11605
11606            edits.push((start_before..start_before, open_tag));
11607            edits.push((end_after..end_after, close_tag));
11608
11609            boundaries.push((
11610                start_before,
11611                end_after,
11612                wrap_config.start_prefix.len(),
11613                wrap_config.end_suffix.len(),
11614            ));
11615        }
11616
11617        if edits.is_empty() {
11618            return;
11619        }
11620
11621        self.transact(window, cx, |this, window, cx| {
11622            let buffer = this.buffer.update(cx, |buffer, cx| {
11623                buffer.edit(edits, None, cx);
11624                buffer.snapshot(cx)
11625            });
11626
11627            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11628            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11629                boundaries.into_iter()
11630            {
11631                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11632                let close_offset = end_after
11633                    .to_offset(&buffer)
11634                    .saturating_sub_usize(end_suffix_len);
11635                new_selections.push(open_offset..open_offset);
11636                new_selections.push(close_offset..close_offset);
11637            }
11638
11639            this.change_selections(Default::default(), window, cx, |s| {
11640                s.select_ranges(new_selections);
11641            });
11642
11643            this.request_autoscroll(Autoscroll::fit(), cx);
11644        });
11645    }
11646
11647    pub fn toggle_read_only(
11648        &mut self,
11649        _: &workspace::ToggleReadOnlyFile,
11650        _: &mut Window,
11651        cx: &mut Context<Self>,
11652    ) {
11653        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11654            buffer.update(cx, |buffer, cx| {
11655                buffer.set_capability(
11656                    match buffer.capability() {
11657                        Capability::ReadWrite => Capability::Read,
11658                        Capability::Read => Capability::ReadWrite,
11659                        Capability::ReadOnly => Capability::ReadOnly,
11660                    },
11661                    cx,
11662                );
11663            })
11664        }
11665    }
11666
11667    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11668        let Some(project) = self.project.clone() else {
11669            return;
11670        };
11671        let task = self.reload(project, window, cx);
11672        self.detach_and_notify_err(task, window, cx);
11673    }
11674
11675    pub fn restore_file(
11676        &mut self,
11677        _: &::git::RestoreFile,
11678        window: &mut Window,
11679        cx: &mut Context<Self>,
11680    ) {
11681        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11682        let mut buffer_ids = HashSet::default();
11683        let snapshot = self.buffer().read(cx).snapshot(cx);
11684        for selection in self
11685            .selections
11686            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11687        {
11688            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11689        }
11690
11691        let buffer = self.buffer().read(cx);
11692        let ranges = buffer_ids
11693            .into_iter()
11694            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11695            .collect::<Vec<_>>();
11696
11697        self.restore_hunks_in_ranges(ranges, window, cx);
11698    }
11699
11700    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11701        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11702        let selections = self
11703            .selections
11704            .all(&self.display_snapshot(cx))
11705            .into_iter()
11706            .map(|s| s.range())
11707            .collect();
11708        self.restore_hunks_in_ranges(selections, window, cx);
11709    }
11710
11711    /// Restores the diff hunks in the editor's selections and moves the cursor
11712    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11713    /// not all diff hunks are expanded.
11714    pub fn restore_and_next(
11715        &mut self,
11716        _: &::git::RestoreAndNext,
11717        window: &mut Window,
11718        cx: &mut Context<Self>,
11719    ) {
11720        let selections = self
11721            .selections
11722            .all(&self.display_snapshot(cx))
11723            .into_iter()
11724            .map(|selection| selection.range())
11725            .collect();
11726
11727        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11728        self.restore_hunks_in_ranges(selections, window, cx);
11729
11730        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11731        let wrap_around = !all_diff_hunks_expanded;
11732        let snapshot = self.snapshot(window, cx);
11733        let position = self
11734            .selections
11735            .newest::<Point>(&snapshot.display_snapshot)
11736            .head();
11737
11738        self.go_to_hunk_before_or_after_position(
11739            &snapshot,
11740            position,
11741            Direction::Next,
11742            wrap_around,
11743            window,
11744            cx,
11745        );
11746    }
11747
11748    pub fn restore_hunks_in_ranges(
11749        &mut self,
11750        ranges: Vec<Range<Point>>,
11751        window: &mut Window,
11752        cx: &mut Context<Editor>,
11753    ) {
11754        if self.delegate_stage_and_restore {
11755            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11756            if !hunks.is_empty() {
11757                cx.emit(EditorEvent::RestoreRequested { hunks });
11758            }
11759            return;
11760        }
11761        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11762        self.transact(window, cx, |editor, window, cx| {
11763            editor.restore_diff_hunks(hunks, cx);
11764            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11765                selections.refresh()
11766            });
11767        });
11768    }
11769
11770    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11771        let mut revert_changes = HashMap::default();
11772        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11773        for (buffer_id, hunks) in &chunk_by {
11774            let hunks = hunks.collect::<Vec<_>>();
11775            for hunk in &hunks {
11776                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11777            }
11778            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11779        }
11780        if !revert_changes.is_empty() {
11781            self.buffer().update(cx, |multi_buffer, cx| {
11782                for (buffer_id, changes) in revert_changes {
11783                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11784                        buffer.update(cx, |buffer, cx| {
11785                            buffer.edit(
11786                                changes
11787                                    .into_iter()
11788                                    .map(|(range, text)| (range, text.to_string())),
11789                                None,
11790                                cx,
11791                            );
11792                        });
11793                    }
11794                }
11795            });
11796        }
11797    }
11798
11799    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11800        if let Some(status) = self
11801            .addons
11802            .iter()
11803            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11804        {
11805            return Some(status);
11806        }
11807        self.project
11808            .as_ref()?
11809            .read(cx)
11810            .status_for_buffer_id(buffer_id, cx)
11811    }
11812
11813    pub fn open_active_item_in_terminal(
11814        &mut self,
11815        _: &OpenInTerminal,
11816        window: &mut Window,
11817        cx: &mut Context<Self>,
11818    ) {
11819        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11820            let project_path = buffer.read(cx).project_path(cx)?;
11821            let project = self.project()?.read(cx);
11822            let entry = project.entry_for_path(&project_path, cx)?;
11823            let parent = match &entry.canonical_path {
11824                Some(canonical_path) => canonical_path.to_path_buf(),
11825                None => project.absolute_path(&project_path, cx)?,
11826            }
11827            .parent()?
11828            .to_path_buf();
11829            Some(parent)
11830        }) {
11831            window.dispatch_action(
11832                OpenTerminal {
11833                    working_directory,
11834                    local: false,
11835                }
11836                .boxed_clone(),
11837                cx,
11838            );
11839        }
11840    }
11841
11842    fn set_breakpoint_context_menu(
11843        &mut self,
11844        display_row: DisplayRow,
11845        position: Option<Anchor>,
11846        clicked_point: gpui::Point<Pixels>,
11847        window: &mut Window,
11848        cx: &mut Context<Self>,
11849    ) {
11850        let source = self
11851            .buffer
11852            .read(cx)
11853            .snapshot(cx)
11854            .anchor_before(Point::new(display_row.0, 0u32));
11855
11856        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11857
11858        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11859            self,
11860            source,
11861            clicked_point,
11862            context_menu,
11863            window,
11864            cx,
11865        );
11866    }
11867
11868    fn add_edit_breakpoint_block(
11869        &mut self,
11870        anchor: Anchor,
11871        breakpoint: &Breakpoint,
11872        edit_action: BreakpointPromptEditAction,
11873        window: &mut Window,
11874        cx: &mut Context<Self>,
11875    ) {
11876        let weak_editor = cx.weak_entity();
11877        let bp_prompt = cx.new(|cx| {
11878            BreakpointPromptEditor::new(
11879                weak_editor,
11880                anchor,
11881                breakpoint.clone(),
11882                edit_action,
11883                window,
11884                cx,
11885            )
11886        });
11887
11888        let height = bp_prompt.update(cx, |this, cx| {
11889            this.prompt
11890                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11891        });
11892        let cloned_prompt = bp_prompt.clone();
11893        let blocks = vec![BlockProperties {
11894            style: BlockStyle::Sticky,
11895            placement: BlockPlacement::Above(anchor),
11896            height: Some(height),
11897            render: Arc::new(move |cx| {
11898                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11899                cloned_prompt.clone().into_any_element()
11900            }),
11901            priority: 0,
11902        }];
11903
11904        let focus_handle = bp_prompt.focus_handle(cx);
11905        window.focus(&focus_handle, cx);
11906
11907        let block_ids = self.insert_blocks(blocks, None, cx);
11908        bp_prompt.update(cx, |prompt, _| {
11909            prompt.add_block_ids(block_ids);
11910        });
11911    }
11912
11913    pub(crate) fn breakpoint_at_row(
11914        &self,
11915        row: u32,
11916        window: &mut Window,
11917        cx: &mut Context<Self>,
11918    ) -> Option<(Anchor, Breakpoint)> {
11919        let snapshot = self.snapshot(window, cx);
11920        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11921
11922        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11923    }
11924
11925    pub(crate) fn breakpoint_at_anchor(
11926        &self,
11927        breakpoint_position: Anchor,
11928        snapshot: &EditorSnapshot,
11929        cx: &mut Context<Self>,
11930    ) -> Option<(Anchor, Breakpoint)> {
11931        let buffer = self
11932            .buffer
11933            .read(cx)
11934            .buffer_for_anchor(breakpoint_position, cx)?;
11935
11936        let enclosing_excerpt = breakpoint_position.excerpt_id;
11937        let buffer_snapshot = buffer.read(cx).snapshot();
11938
11939        let row = buffer_snapshot
11940            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11941            .row;
11942
11943        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11944        let anchor_end = snapshot
11945            .buffer_snapshot()
11946            .anchor_after(Point::new(row, line_len));
11947
11948        self.breakpoint_store
11949            .as_ref()?
11950            .read_with(cx, |breakpoint_store, cx| {
11951                breakpoint_store
11952                    .breakpoints(
11953                        &buffer,
11954                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11955                        &buffer_snapshot,
11956                        cx,
11957                    )
11958                    .next()
11959                    .and_then(|(bp, _)| {
11960                        let breakpoint_row = buffer_snapshot
11961                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11962                            .row;
11963
11964                        if breakpoint_row == row {
11965                            snapshot
11966                                .buffer_snapshot()
11967                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11968                                .map(|position| (position, bp.bp.clone()))
11969                        } else {
11970                            None
11971                        }
11972                    })
11973            })
11974    }
11975
11976    pub fn edit_log_breakpoint(
11977        &mut self,
11978        _: &EditLogBreakpoint,
11979        window: &mut Window,
11980        cx: &mut Context<Self>,
11981    ) {
11982        if self.breakpoint_store.is_none() {
11983            return;
11984        }
11985
11986        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11987            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11988                message: None,
11989                state: BreakpointState::Enabled,
11990                condition: None,
11991                hit_condition: None,
11992            });
11993
11994            self.add_edit_breakpoint_block(
11995                anchor,
11996                &breakpoint,
11997                BreakpointPromptEditAction::Log,
11998                window,
11999                cx,
12000            );
12001        }
12002    }
12003
12004    fn breakpoints_at_cursors(
12005        &self,
12006        window: &mut Window,
12007        cx: &mut Context<Self>,
12008    ) -> Vec<(Anchor, Option<Breakpoint>)> {
12009        let snapshot = self.snapshot(window, cx);
12010        let cursors = self
12011            .selections
12012            .disjoint_anchors_arc()
12013            .iter()
12014            .map(|selection| {
12015                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
12016
12017                let breakpoint_position = self
12018                    .breakpoint_at_row(cursor_position.row, window, cx)
12019                    .map(|bp| bp.0)
12020                    .unwrap_or_else(|| {
12021                        snapshot
12022                            .display_snapshot
12023                            .buffer_snapshot()
12024                            .anchor_after(Point::new(cursor_position.row, 0))
12025                    });
12026
12027                let breakpoint = self
12028                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
12029                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
12030
12031                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
12032            })
12033            // 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.
12034            .collect::<HashMap<Anchor, _>>();
12035
12036        cursors.into_iter().collect()
12037    }
12038
12039    pub fn enable_breakpoint(
12040        &mut self,
12041        _: &crate::actions::EnableBreakpoint,
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_disabled()) 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 align_selections(
12063        &mut self,
12064        _: &crate::actions::AlignSelections,
12065        window: &mut Window,
12066        cx: &mut Context<Self>,
12067    ) {
12068        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12069
12070        let display_snapshot = self.display_snapshot(cx);
12071
12072        struct CursorData {
12073            anchor: Anchor,
12074            point: Point,
12075        }
12076        let cursor_data: Vec<CursorData> = self
12077            .selections
12078            .disjoint_anchors()
12079            .iter()
12080            .map(|selection| {
12081                let anchor = if selection.reversed {
12082                    selection.head()
12083                } else {
12084                    selection.tail()
12085                };
12086                CursorData {
12087                    anchor: anchor,
12088                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
12089                }
12090            })
12091            .collect();
12092
12093        let rows_anchors_count: Vec<usize> = cursor_data
12094            .iter()
12095            .map(|cursor| cursor.point.row)
12096            .chunk_by(|&row| row)
12097            .into_iter()
12098            .map(|(_, group)| group.count())
12099            .collect();
12100        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
12101        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
12102        let mut edits = Vec::new();
12103
12104        for column_idx in 0..max_columns {
12105            let mut cursor_index = 0;
12106
12107            // Calculate target_column => position that the selections will go
12108            let mut target_column = 0;
12109            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12110                // Skip rows that don't have this column
12111                if column_idx >= *cursor_count {
12112                    cursor_index += cursor_count;
12113                    continue;
12114                }
12115
12116                let point = &cursor_data[cursor_index + column_idx].point;
12117                let adjusted_column = point.column + rows_column_offset[row_idx];
12118                if adjusted_column > target_column {
12119                    target_column = adjusted_column;
12120                }
12121                cursor_index += cursor_count;
12122            }
12123
12124            // Collect edits for this column
12125            cursor_index = 0;
12126            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12127                // Skip rows that don't have this column
12128                if column_idx >= *cursor_count {
12129                    cursor_index += *cursor_count;
12130                    continue;
12131                }
12132
12133                let point = &cursor_data[cursor_index + column_idx].point;
12134                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
12135                if spaces_needed > 0 {
12136                    let anchor = cursor_data[cursor_index + column_idx]
12137                        .anchor
12138                        .bias_left(&display_snapshot);
12139                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
12140                }
12141                rows_column_offset[row_idx] += spaces_needed;
12142
12143                cursor_index += *cursor_count;
12144            }
12145        }
12146
12147        if !edits.is_empty() {
12148            self.transact(window, cx, |editor, _window, cx| {
12149                editor.edit(edits, cx);
12150            });
12151        }
12152    }
12153
12154    pub fn disable_breakpoint(
12155        &mut self,
12156        _: &crate::actions::DisableBreakpoint,
12157        window: &mut Window,
12158        cx: &mut Context<Self>,
12159    ) {
12160        if self.breakpoint_store.is_none() {
12161            return;
12162        }
12163
12164        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12165            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12166                continue;
12167            };
12168            self.edit_breakpoint_at_anchor(
12169                anchor,
12170                breakpoint,
12171                BreakpointEditAction::InvertState,
12172                cx,
12173            );
12174        }
12175    }
12176
12177    pub fn toggle_breakpoint(
12178        &mut self,
12179        _: &crate::actions::ToggleBreakpoint,
12180        window: &mut Window,
12181        cx: &mut Context<Self>,
12182    ) {
12183        if self.breakpoint_store.is_none() {
12184            return;
12185        }
12186
12187        let snapshot = self.snapshot(window, cx);
12188        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12189            if self.gutter_breakpoint_indicator.0.is_some() {
12190                let display_row = anchor
12191                    .to_point(snapshot.buffer_snapshot())
12192                    .to_display_point(&snapshot.display_snapshot)
12193                    .row();
12194                self.update_breakpoint_collision_on_toggle(
12195                    display_row,
12196                    &BreakpointEditAction::Toggle,
12197                );
12198            }
12199
12200            if let Some(breakpoint) = breakpoint {
12201                self.edit_breakpoint_at_anchor(
12202                    anchor,
12203                    breakpoint,
12204                    BreakpointEditAction::Toggle,
12205                    cx,
12206                );
12207            } else {
12208                self.edit_breakpoint_at_anchor(
12209                    anchor,
12210                    Breakpoint::new_standard(),
12211                    BreakpointEditAction::Toggle,
12212                    cx,
12213                );
12214            }
12215        }
12216    }
12217
12218    fn update_breakpoint_collision_on_toggle(
12219        &mut self,
12220        display_row: DisplayRow,
12221        edit_action: &BreakpointEditAction,
12222    ) {
12223        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12224            if breakpoint_indicator.display_row == display_row
12225                && matches!(edit_action, BreakpointEditAction::Toggle)
12226            {
12227                breakpoint_indicator.collides_with_existing_breakpoint =
12228                    !breakpoint_indicator.collides_with_existing_breakpoint;
12229            }
12230        }
12231    }
12232
12233    pub fn edit_breakpoint_at_anchor(
12234        &mut self,
12235        breakpoint_position: Anchor,
12236        breakpoint: Breakpoint,
12237        edit_action: BreakpointEditAction,
12238        cx: &mut Context<Self>,
12239    ) {
12240        let Some(breakpoint_store) = &self.breakpoint_store else {
12241            return;
12242        };
12243
12244        let Some(buffer) = self
12245            .buffer
12246            .read(cx)
12247            .buffer_for_anchor(breakpoint_position, cx)
12248        else {
12249            return;
12250        };
12251
12252        breakpoint_store.update(cx, |breakpoint_store, cx| {
12253            breakpoint_store.toggle_breakpoint(
12254                buffer,
12255                BreakpointWithPosition {
12256                    position: breakpoint_position.text_anchor,
12257                    bp: breakpoint,
12258                },
12259                edit_action,
12260                cx,
12261            );
12262        });
12263
12264        cx.notify();
12265    }
12266
12267    #[cfg(any(test, feature = "test-support"))]
12268    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12269        self.breakpoint_store.clone()
12270    }
12271
12272    pub fn prepare_restore_change(
12273        &self,
12274        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12275        hunk: &MultiBufferDiffHunk,
12276        cx: &mut App,
12277    ) -> Option<()> {
12278        if hunk.is_created_file() {
12279            return None;
12280        }
12281        let buffer = self.buffer.read(cx);
12282        let diff = buffer.diff_for(hunk.buffer_id)?;
12283        let buffer = buffer.buffer(hunk.buffer_id)?;
12284        let buffer = buffer.read(cx);
12285        let original_text = diff
12286            .read(cx)
12287            .base_text(cx)
12288            .as_rope()
12289            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12290        let buffer_snapshot = buffer.snapshot();
12291        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12292        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12293            probe
12294                .0
12295                .start
12296                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12297                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12298        }) {
12299            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12300            Some(())
12301        } else {
12302            None
12303        }
12304    }
12305
12306    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12307        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12308    }
12309
12310    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12311        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12312    }
12313
12314    pub fn rotate_selections_forward(
12315        &mut self,
12316        _: &RotateSelectionsForward,
12317        window: &mut Window,
12318        cx: &mut Context<Self>,
12319    ) {
12320        self.rotate_selections(window, cx, false)
12321    }
12322
12323    pub fn rotate_selections_backward(
12324        &mut self,
12325        _: &RotateSelectionsBackward,
12326        window: &mut Window,
12327        cx: &mut Context<Self>,
12328    ) {
12329        self.rotate_selections(window, cx, true)
12330    }
12331
12332    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12333        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12334        let display_snapshot = self.display_snapshot(cx);
12335        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12336
12337        if selections.len() < 2 {
12338            return;
12339        }
12340
12341        let (edits, new_selections) = {
12342            let buffer = self.buffer.read(cx).read(cx);
12343            let has_selections = selections.iter().any(|s| !s.is_empty());
12344            if has_selections {
12345                let mut selected_texts: Vec<String> = selections
12346                    .iter()
12347                    .map(|selection| {
12348                        buffer
12349                            .text_for_range(selection.start..selection.end)
12350                            .collect()
12351                    })
12352                    .collect();
12353
12354                if reverse {
12355                    selected_texts.rotate_left(1);
12356                } else {
12357                    selected_texts.rotate_right(1);
12358                }
12359
12360                let mut offset_delta: i64 = 0;
12361                let mut new_selections = Vec::new();
12362                let edits: Vec<_> = selections
12363                    .iter()
12364                    .zip(selected_texts.iter())
12365                    .map(|(selection, new_text)| {
12366                        let old_len = (selection.end.0 - selection.start.0) as i64;
12367                        let new_len = new_text.len() as i64;
12368                        let adjusted_start =
12369                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12370                        let adjusted_end =
12371                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12372
12373                        new_selections.push(Selection {
12374                            id: selection.id,
12375                            start: adjusted_start,
12376                            end: adjusted_end,
12377                            reversed: selection.reversed,
12378                            goal: selection.goal,
12379                        });
12380
12381                        offset_delta += new_len - old_len;
12382                        (selection.start..selection.end, new_text.clone())
12383                    })
12384                    .collect();
12385                (edits, new_selections)
12386            } else {
12387                let mut all_rows: Vec<u32> = selections
12388                    .iter()
12389                    .map(|selection| buffer.offset_to_point(selection.start).row)
12390                    .collect();
12391                all_rows.sort_unstable();
12392                all_rows.dedup();
12393
12394                if all_rows.len() < 2 {
12395                    return;
12396                }
12397
12398                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12399                    .iter()
12400                    .map(|&row| {
12401                        let start = Point::new(row, 0);
12402                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12403                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12404                    })
12405                    .collect();
12406
12407                let mut line_texts: Vec<String> = line_ranges
12408                    .iter()
12409                    .map(|range| buffer.text_for_range(range.clone()).collect())
12410                    .collect();
12411
12412                if reverse {
12413                    line_texts.rotate_left(1);
12414                } else {
12415                    line_texts.rotate_right(1);
12416                }
12417
12418                let edits = line_ranges
12419                    .iter()
12420                    .zip(line_texts.iter())
12421                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12422                    .collect();
12423
12424                let num_rows = all_rows.len();
12425                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12426                    .iter()
12427                    .enumerate()
12428                    .map(|(i, &row)| (row, i))
12429                    .collect();
12430
12431                // Compute new line start offsets after rotation (handles CRLF)
12432                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12433                let first_line_start = line_ranges[0].start.0;
12434                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12435                for text in line_texts.iter().take(num_rows - 1) {
12436                    let prev_start = *new_line_starts.last().unwrap();
12437                    new_line_starts.push(prev_start + text.len() + newline_len);
12438                }
12439
12440                let new_selections = selections
12441                    .iter()
12442                    .map(|selection| {
12443                        let point = buffer.offset_to_point(selection.start);
12444                        let old_index = row_to_index[&point.row];
12445                        let new_index = if reverse {
12446                            (old_index + num_rows - 1) % num_rows
12447                        } else {
12448                            (old_index + 1) % num_rows
12449                        };
12450                        let new_offset =
12451                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12452                        Selection {
12453                            id: selection.id,
12454                            start: new_offset,
12455                            end: new_offset,
12456                            reversed: selection.reversed,
12457                            goal: selection.goal,
12458                        }
12459                    })
12460                    .collect();
12461
12462                (edits, new_selections)
12463            }
12464        };
12465
12466        self.transact(window, cx, |this, window, cx| {
12467            this.buffer.update(cx, |buffer, cx| {
12468                buffer.edit(edits, None, cx);
12469            });
12470            this.change_selections(Default::default(), window, cx, |s| {
12471                s.select(new_selections);
12472            });
12473        });
12474    }
12475
12476    fn manipulate_lines<M>(
12477        &mut self,
12478        window: &mut Window,
12479        cx: &mut Context<Self>,
12480        mut manipulate: M,
12481    ) where
12482        M: FnMut(&str) -> LineManipulationResult,
12483    {
12484        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12485
12486        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12487        let buffer = self.buffer.read(cx).snapshot(cx);
12488
12489        let mut edits = Vec::new();
12490
12491        let selections = self.selections.all::<Point>(&display_map);
12492        let mut selections = selections.iter().peekable();
12493        let mut contiguous_row_selections = Vec::new();
12494        let mut new_selections = Vec::new();
12495        let mut added_lines = 0;
12496        let mut removed_lines = 0;
12497
12498        while let Some(selection) = selections.next() {
12499            let (start_row, end_row) = consume_contiguous_rows(
12500                &mut contiguous_row_selections,
12501                selection,
12502                &display_map,
12503                &mut selections,
12504            );
12505
12506            let start_point = Point::new(start_row.0, 0);
12507            let end_point = Point::new(
12508                end_row.previous_row().0,
12509                buffer.line_len(end_row.previous_row()),
12510            );
12511            let text = buffer
12512                .text_for_range(start_point..end_point)
12513                .collect::<String>();
12514
12515            let LineManipulationResult {
12516                new_text,
12517                line_count_before,
12518                line_count_after,
12519            } = manipulate(&text);
12520
12521            edits.push((start_point..end_point, new_text));
12522
12523            // Selections must change based on added and removed line count
12524            let start_row =
12525                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12526            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12527            new_selections.push(Selection {
12528                id: selection.id,
12529                start: start_row,
12530                end: end_row,
12531                goal: SelectionGoal::None,
12532                reversed: selection.reversed,
12533            });
12534
12535            if line_count_after > line_count_before {
12536                added_lines += line_count_after - line_count_before;
12537            } else if line_count_before > line_count_after {
12538                removed_lines += line_count_before - line_count_after;
12539            }
12540        }
12541
12542        self.transact(window, cx, |this, window, cx| {
12543            let buffer = this.buffer.update(cx, |buffer, cx| {
12544                buffer.edit(edits, None, cx);
12545                buffer.snapshot(cx)
12546            });
12547
12548            // Recalculate offsets on newly edited buffer
12549            let new_selections = new_selections
12550                .iter()
12551                .map(|s| {
12552                    let start_point = Point::new(s.start.0, 0);
12553                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12554                    Selection {
12555                        id: s.id,
12556                        start: buffer.point_to_offset(start_point),
12557                        end: buffer.point_to_offset(end_point),
12558                        goal: s.goal,
12559                        reversed: s.reversed,
12560                    }
12561                })
12562                .collect();
12563
12564            this.change_selections(Default::default(), window, cx, |s| {
12565                s.select(new_selections);
12566            });
12567
12568            this.request_autoscroll(Autoscroll::fit(), cx);
12569        });
12570    }
12571
12572    fn manipulate_immutable_lines<Fn>(
12573        &mut self,
12574        window: &mut Window,
12575        cx: &mut Context<Self>,
12576        mut callback: Fn,
12577    ) where
12578        Fn: FnMut(&mut Vec<&str>),
12579    {
12580        self.manipulate_lines(window, cx, |text| {
12581            let mut lines: Vec<&str> = text.split('\n').collect();
12582            let line_count_before = lines.len();
12583
12584            callback(&mut lines);
12585
12586            LineManipulationResult {
12587                new_text: lines.join("\n"),
12588                line_count_before,
12589                line_count_after: lines.len(),
12590            }
12591        });
12592    }
12593
12594    fn manipulate_mutable_lines<Fn>(
12595        &mut self,
12596        window: &mut Window,
12597        cx: &mut Context<Self>,
12598        mut callback: Fn,
12599    ) where
12600        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12601    {
12602        self.manipulate_lines(window, cx, |text| {
12603            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12604            let line_count_before = lines.len();
12605
12606            callback(&mut lines);
12607
12608            LineManipulationResult {
12609                new_text: lines.join("\n"),
12610                line_count_before,
12611                line_count_after: lines.len(),
12612            }
12613        });
12614    }
12615
12616    pub fn convert_indentation_to_spaces(
12617        &mut self,
12618        _: &ConvertIndentationToSpaces,
12619        window: &mut Window,
12620        cx: &mut Context<Self>,
12621    ) {
12622        let settings = self.buffer.read(cx).language_settings(cx);
12623        let tab_size = settings.tab_size.get() as usize;
12624
12625        self.manipulate_mutable_lines(window, cx, |lines| {
12626            // Allocates a reasonably sized scratch buffer once for the whole loop
12627            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12628            // Avoids recomputing spaces that could be inserted many times
12629            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12630                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12631                .collect();
12632
12633            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12634                let mut chars = line.as_ref().chars();
12635                let mut col = 0;
12636                let mut changed = false;
12637
12638                for ch in chars.by_ref() {
12639                    match ch {
12640                        ' ' => {
12641                            reindented_line.push(' ');
12642                            col += 1;
12643                        }
12644                        '\t' => {
12645                            // \t are converted to spaces depending on the current column
12646                            let spaces_len = tab_size - (col % tab_size);
12647                            reindented_line.extend(&space_cache[spaces_len - 1]);
12648                            col += spaces_len;
12649                            changed = true;
12650                        }
12651                        _ => {
12652                            // If we dont append before break, the character is consumed
12653                            reindented_line.push(ch);
12654                            break;
12655                        }
12656                    }
12657                }
12658
12659                if !changed {
12660                    reindented_line.clear();
12661                    continue;
12662                }
12663                // Append the rest of the line and replace old reference with new one
12664                reindented_line.extend(chars);
12665                *line = Cow::Owned(reindented_line.clone());
12666                reindented_line.clear();
12667            }
12668        });
12669    }
12670
12671    pub fn convert_indentation_to_tabs(
12672        &mut self,
12673        _: &ConvertIndentationToTabs,
12674        window: &mut Window,
12675        cx: &mut Context<Self>,
12676    ) {
12677        let settings = self.buffer.read(cx).language_settings(cx);
12678        let tab_size = settings.tab_size.get() as usize;
12679
12680        self.manipulate_mutable_lines(window, cx, |lines| {
12681            // Allocates a reasonably sized buffer once for the whole loop
12682            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12683            // Avoids recomputing spaces that could be inserted many times
12684            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12685                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12686                .collect();
12687
12688            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12689                let mut chars = line.chars();
12690                let mut spaces_count = 0;
12691                let mut first_non_indent_char = None;
12692                let mut changed = false;
12693
12694                for ch in chars.by_ref() {
12695                    match ch {
12696                        ' ' => {
12697                            // Keep track of spaces. Append \t when we reach tab_size
12698                            spaces_count += 1;
12699                            changed = true;
12700                            if spaces_count == tab_size {
12701                                reindented_line.push('\t');
12702                                spaces_count = 0;
12703                            }
12704                        }
12705                        '\t' => {
12706                            reindented_line.push('\t');
12707                            spaces_count = 0;
12708                        }
12709                        _ => {
12710                            // Dont append it yet, we might have remaining spaces
12711                            first_non_indent_char = Some(ch);
12712                            break;
12713                        }
12714                    }
12715                }
12716
12717                if !changed {
12718                    reindented_line.clear();
12719                    continue;
12720                }
12721                // Remaining spaces that didn't make a full tab stop
12722                if spaces_count > 0 {
12723                    reindented_line.extend(&space_cache[spaces_count - 1]);
12724                }
12725                // If we consume an extra character that was not indentation, add it back
12726                if let Some(extra_char) = first_non_indent_char {
12727                    reindented_line.push(extra_char);
12728                }
12729                // Append the rest of the line and replace old reference with new one
12730                reindented_line.extend(chars);
12731                *line = Cow::Owned(reindented_line.clone());
12732                reindented_line.clear();
12733            }
12734        });
12735    }
12736
12737    pub fn convert_to_upper_case(
12738        &mut self,
12739        _: &ConvertToUpperCase,
12740        window: &mut Window,
12741        cx: &mut Context<Self>,
12742    ) {
12743        self.manipulate_text(window, cx, |text| text.to_uppercase())
12744    }
12745
12746    pub fn convert_to_lower_case(
12747        &mut self,
12748        _: &ConvertToLowerCase,
12749        window: &mut Window,
12750        cx: &mut Context<Self>,
12751    ) {
12752        self.manipulate_text(window, cx, |text| text.to_lowercase())
12753    }
12754
12755    pub fn convert_to_title_case(
12756        &mut self,
12757        _: &ConvertToTitleCase,
12758        window: &mut Window,
12759        cx: &mut Context<Self>,
12760    ) {
12761        self.manipulate_text(window, cx, |text| {
12762            Self::convert_text_case(text, Case::Title)
12763        })
12764    }
12765
12766    pub fn convert_to_snake_case(
12767        &mut self,
12768        _: &ConvertToSnakeCase,
12769        window: &mut Window,
12770        cx: &mut Context<Self>,
12771    ) {
12772        self.manipulate_text(window, cx, |text| {
12773            Self::convert_text_case(text, Case::Snake)
12774        })
12775    }
12776
12777    pub fn convert_to_kebab_case(
12778        &mut self,
12779        _: &ConvertToKebabCase,
12780        window: &mut Window,
12781        cx: &mut Context<Self>,
12782    ) {
12783        self.manipulate_text(window, cx, |text| {
12784            Self::convert_text_case(text, Case::Kebab)
12785        })
12786    }
12787
12788    pub fn convert_to_upper_camel_case(
12789        &mut self,
12790        _: &ConvertToUpperCamelCase,
12791        window: &mut Window,
12792        cx: &mut Context<Self>,
12793    ) {
12794        self.manipulate_text(window, cx, |text| {
12795            Self::convert_text_case(text, Case::UpperCamel)
12796        })
12797    }
12798
12799    pub fn convert_to_lower_camel_case(
12800        &mut self,
12801        _: &ConvertToLowerCamelCase,
12802        window: &mut Window,
12803        cx: &mut Context<Self>,
12804    ) {
12805        self.manipulate_text(window, cx, |text| {
12806            Self::convert_text_case(text, Case::Camel)
12807        })
12808    }
12809
12810    pub fn convert_to_opposite_case(
12811        &mut self,
12812        _: &ConvertToOppositeCase,
12813        window: &mut Window,
12814        cx: &mut Context<Self>,
12815    ) {
12816        self.manipulate_text(window, cx, |text| {
12817            text.chars()
12818                .fold(String::with_capacity(text.len()), |mut t, c| {
12819                    if c.is_uppercase() {
12820                        t.extend(c.to_lowercase());
12821                    } else {
12822                        t.extend(c.to_uppercase());
12823                    }
12824                    t
12825                })
12826        })
12827    }
12828
12829    pub fn convert_to_sentence_case(
12830        &mut self,
12831        _: &ConvertToSentenceCase,
12832        window: &mut Window,
12833        cx: &mut Context<Self>,
12834    ) {
12835        self.manipulate_text(window, cx, |text| {
12836            Self::convert_text_case(text, Case::Sentence)
12837        })
12838    }
12839
12840    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12841        self.manipulate_text(window, cx, |text| {
12842            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12843            if has_upper_case_characters {
12844                text.to_lowercase()
12845            } else {
12846                text.to_uppercase()
12847            }
12848        })
12849    }
12850
12851    pub fn convert_to_rot13(
12852        &mut self,
12853        _: &ConvertToRot13,
12854        window: &mut Window,
12855        cx: &mut Context<Self>,
12856    ) {
12857        self.manipulate_text(window, cx, |text| {
12858            text.chars()
12859                .map(|c| match c {
12860                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12861                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12862                    _ => c,
12863                })
12864                .collect()
12865        })
12866    }
12867
12868    fn convert_text_case(text: &str, case: Case) -> String {
12869        text.lines()
12870            .map(|line| {
12871                let trimmed_start = line.trim_start();
12872                let leading = &line[..line.len() - trimmed_start.len()];
12873                let trimmed = trimmed_start.trim_end();
12874                let trailing = &trimmed_start[trimmed.len()..];
12875                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12876            })
12877            .join("\n")
12878    }
12879
12880    pub fn convert_to_rot47(
12881        &mut self,
12882        _: &ConvertToRot47,
12883        window: &mut Window,
12884        cx: &mut Context<Self>,
12885    ) {
12886        self.manipulate_text(window, cx, |text| {
12887            text.chars()
12888                .map(|c| {
12889                    let code_point = c as u32;
12890                    if code_point >= 33 && code_point <= 126 {
12891                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12892                    }
12893                    c
12894                })
12895                .collect()
12896        })
12897    }
12898
12899    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12900    where
12901        Fn: FnMut(&str) -> String,
12902    {
12903        let buffer = self.buffer.read(cx).snapshot(cx);
12904
12905        let mut new_selections = Vec::new();
12906        let mut edits = Vec::new();
12907        let mut selection_adjustment = 0isize;
12908
12909        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12910            let selection_is_empty = selection.is_empty();
12911
12912            let (start, end) = if selection_is_empty {
12913                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12914                (word_range.start, word_range.end)
12915            } else {
12916                (
12917                    buffer.point_to_offset(selection.start),
12918                    buffer.point_to_offset(selection.end),
12919                )
12920            };
12921
12922            let text = buffer.text_for_range(start..end).collect::<String>();
12923            let old_length = text.len() as isize;
12924            let text = callback(&text);
12925
12926            new_selections.push(Selection {
12927                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12928                end: MultiBufferOffset(
12929                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12930                ),
12931                goal: SelectionGoal::None,
12932                id: selection.id,
12933                reversed: selection.reversed,
12934            });
12935
12936            selection_adjustment += old_length - text.len() as isize;
12937
12938            edits.push((start..end, text));
12939        }
12940
12941        self.transact(window, cx, |this, window, cx| {
12942            this.buffer.update(cx, |buffer, cx| {
12943                buffer.edit(edits, None, cx);
12944            });
12945
12946            this.change_selections(Default::default(), window, cx, |s| {
12947                s.select(new_selections);
12948            });
12949
12950            this.request_autoscroll(Autoscroll::fit(), cx);
12951        });
12952    }
12953
12954    pub fn move_selection_on_drop(
12955        &mut self,
12956        selection: &Selection<Anchor>,
12957        target: DisplayPoint,
12958        is_cut: bool,
12959        window: &mut Window,
12960        cx: &mut Context<Self>,
12961    ) {
12962        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12963        let buffer = display_map.buffer_snapshot();
12964        let mut edits = Vec::new();
12965        let insert_point = display_map
12966            .clip_point(target, Bias::Left)
12967            .to_point(&display_map);
12968        let text = buffer
12969            .text_for_range(selection.start..selection.end)
12970            .collect::<String>();
12971        if is_cut {
12972            edits.push(((selection.start..selection.end), String::new()));
12973        }
12974        let insert_anchor = buffer.anchor_before(insert_point);
12975        edits.push(((insert_anchor..insert_anchor), text));
12976        let last_edit_start = insert_anchor.bias_left(buffer);
12977        let last_edit_end = insert_anchor.bias_right(buffer);
12978        self.transact(window, cx, |this, window, cx| {
12979            this.buffer.update(cx, |buffer, cx| {
12980                buffer.edit(edits, None, cx);
12981            });
12982            this.change_selections(Default::default(), window, cx, |s| {
12983                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12984            });
12985        });
12986    }
12987
12988    pub fn clear_selection_drag_state(&mut self) {
12989        self.selection_drag_state = SelectionDragState::None;
12990    }
12991
12992    pub fn duplicate(
12993        &mut self,
12994        upwards: bool,
12995        whole_lines: bool,
12996        window: &mut Window,
12997        cx: &mut Context<Self>,
12998    ) {
12999        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13000
13001        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13002        let buffer = display_map.buffer_snapshot();
13003        let selections = self.selections.all::<Point>(&display_map);
13004
13005        let mut edits = Vec::new();
13006        let mut selections_iter = selections.iter().peekable();
13007        while let Some(selection) = selections_iter.next() {
13008            let mut rows = selection.spanned_rows(false, &display_map);
13009            // duplicate line-wise
13010            if whole_lines || selection.start == selection.end {
13011                // Avoid duplicating the same lines twice.
13012                while let Some(next_selection) = selections_iter.peek() {
13013                    let next_rows = next_selection.spanned_rows(false, &display_map);
13014                    if next_rows.start < rows.end {
13015                        rows.end = next_rows.end;
13016                        selections_iter.next().unwrap();
13017                    } else {
13018                        break;
13019                    }
13020                }
13021
13022                // Copy the text from the selected row region and splice it either at the start
13023                // or end of the region.
13024                let start = Point::new(rows.start.0, 0);
13025                let end = Point::new(
13026                    rows.end.previous_row().0,
13027                    buffer.line_len(rows.end.previous_row()),
13028                );
13029
13030                let mut text = buffer.text_for_range(start..end).collect::<String>();
13031
13032                let insert_location = if upwards {
13033                    // When duplicating upward, we need to insert before the current line.
13034                    // If we're on the last line and it doesn't end with a newline,
13035                    // we need to add a newline before the duplicated content.
13036                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
13037                        && buffer.max_point().column > 0
13038                        && !text.ends_with('\n');
13039
13040                    if needs_leading_newline {
13041                        text.insert(0, '\n');
13042                        end
13043                    } else {
13044                        text.push('\n');
13045                        Point::new(rows.start.0, 0)
13046                    }
13047                } else {
13048                    text.push('\n');
13049                    start
13050                };
13051                edits.push((insert_location..insert_location, text));
13052            } else {
13053                // duplicate character-wise
13054                let start = selection.start;
13055                let end = selection.end;
13056                let text = buffer.text_for_range(start..end).collect::<String>();
13057                edits.push((selection.end..selection.end, text));
13058            }
13059        }
13060
13061        self.transact(window, cx, |this, window, cx| {
13062            this.buffer.update(cx, |buffer, cx| {
13063                buffer.edit(edits, None, cx);
13064            });
13065
13066            // When duplicating upward with whole lines, move the cursor to the duplicated line
13067            if upwards && whole_lines {
13068                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
13069
13070                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13071                    let mut new_ranges = Vec::new();
13072                    let selections = s.all::<Point>(&display_map);
13073                    let mut selections_iter = selections.iter().peekable();
13074
13075                    while let Some(first_selection) = selections_iter.next() {
13076                        // Group contiguous selections together to find the total row span
13077                        let mut group_selections = vec![first_selection];
13078                        let mut rows = first_selection.spanned_rows(false, &display_map);
13079
13080                        while let Some(next_selection) = selections_iter.peek() {
13081                            let next_rows = next_selection.spanned_rows(false, &display_map);
13082                            if next_rows.start < rows.end {
13083                                rows.end = next_rows.end;
13084                                group_selections.push(selections_iter.next().unwrap());
13085                            } else {
13086                                break;
13087                            }
13088                        }
13089
13090                        let row_count = rows.end.0 - rows.start.0;
13091
13092                        // Move all selections in this group up by the total number of duplicated rows
13093                        for selection in group_selections {
13094                            let new_start = Point::new(
13095                                selection.start.row.saturating_sub(row_count),
13096                                selection.start.column,
13097                            );
13098
13099                            let new_end = Point::new(
13100                                selection.end.row.saturating_sub(row_count),
13101                                selection.end.column,
13102                            );
13103
13104                            new_ranges.push(new_start..new_end);
13105                        }
13106                    }
13107
13108                    s.select_ranges(new_ranges);
13109                });
13110            }
13111
13112            this.request_autoscroll(Autoscroll::fit(), cx);
13113        });
13114    }
13115
13116    pub fn duplicate_line_up(
13117        &mut self,
13118        _: &DuplicateLineUp,
13119        window: &mut Window,
13120        cx: &mut Context<Self>,
13121    ) {
13122        self.duplicate(true, true, window, cx);
13123    }
13124
13125    pub fn duplicate_line_down(
13126        &mut self,
13127        _: &DuplicateLineDown,
13128        window: &mut Window,
13129        cx: &mut Context<Self>,
13130    ) {
13131        self.duplicate(false, true, window, cx);
13132    }
13133
13134    pub fn duplicate_selection(
13135        &mut self,
13136        _: &DuplicateSelection,
13137        window: &mut Window,
13138        cx: &mut Context<Self>,
13139    ) {
13140        self.duplicate(false, false, window, cx);
13141    }
13142
13143    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13144        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13145        if self.mode.is_single_line() {
13146            cx.propagate();
13147            return;
13148        }
13149
13150        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13151        let buffer = self.buffer.read(cx).snapshot(cx);
13152
13153        let mut edits = Vec::new();
13154        let mut unfold_ranges = Vec::new();
13155        let mut refold_creases = Vec::new();
13156
13157        let selections = self.selections.all::<Point>(&display_map);
13158        let mut selections = selections.iter().peekable();
13159        let mut contiguous_row_selections = Vec::new();
13160        let mut new_selections = Vec::new();
13161
13162        while let Some(selection) = selections.next() {
13163            // Find all the selections that span a contiguous row range
13164            let (start_row, end_row) = consume_contiguous_rows(
13165                &mut contiguous_row_selections,
13166                selection,
13167                &display_map,
13168                &mut selections,
13169            );
13170
13171            // Move the text spanned by the row range to be before the line preceding the row range
13172            if start_row.0 > 0 {
13173                let range_to_move = Point::new(
13174                    start_row.previous_row().0,
13175                    buffer.line_len(start_row.previous_row()),
13176                )
13177                    ..Point::new(
13178                        end_row.previous_row().0,
13179                        buffer.line_len(end_row.previous_row()),
13180                    );
13181                let insertion_point = display_map
13182                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13183                    .0;
13184
13185                // Don't move lines across excerpts
13186                if buffer
13187                    .excerpt_containing(insertion_point..range_to_move.end)
13188                    .is_some()
13189                {
13190                    let text = buffer
13191                        .text_for_range(range_to_move.clone())
13192                        .flat_map(|s| s.chars())
13193                        .skip(1)
13194                        .chain(['\n'])
13195                        .collect::<String>();
13196
13197                    edits.push((
13198                        buffer.anchor_after(range_to_move.start)
13199                            ..buffer.anchor_before(range_to_move.end),
13200                        String::new(),
13201                    ));
13202                    let insertion_anchor = buffer.anchor_after(insertion_point);
13203                    edits.push((insertion_anchor..insertion_anchor, text));
13204
13205                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13206
13207                    // Move selections up
13208                    new_selections.extend(contiguous_row_selections.drain(..).map(
13209                        |mut selection| {
13210                            selection.start.row -= row_delta;
13211                            selection.end.row -= row_delta;
13212                            selection
13213                        },
13214                    ));
13215
13216                    // Move folds up
13217                    unfold_ranges.push(range_to_move.clone());
13218                    for fold in display_map.folds_in_range(
13219                        buffer.anchor_before(range_to_move.start)
13220                            ..buffer.anchor_after(range_to_move.end),
13221                    ) {
13222                        let mut start = fold.range.start.to_point(&buffer);
13223                        let mut end = fold.range.end.to_point(&buffer);
13224                        start.row -= row_delta;
13225                        end.row -= row_delta;
13226                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13227                    }
13228                }
13229            }
13230
13231            // If we didn't move line(s), preserve the existing selections
13232            new_selections.append(&mut contiguous_row_selections);
13233        }
13234
13235        self.transact(window, cx, |this, window, cx| {
13236            this.unfold_ranges(&unfold_ranges, true, true, cx);
13237            this.buffer.update(cx, |buffer, cx| {
13238                for (range, text) in edits {
13239                    buffer.edit([(range, text)], None, cx);
13240                }
13241            });
13242            this.fold_creases(refold_creases, true, window, cx);
13243            this.change_selections(Default::default(), window, cx, |s| {
13244                s.select(new_selections);
13245            })
13246        });
13247    }
13248
13249    pub fn move_line_down(
13250        &mut self,
13251        _: &MoveLineDown,
13252        window: &mut Window,
13253        cx: &mut Context<Self>,
13254    ) {
13255        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13256        if self.mode.is_single_line() {
13257            cx.propagate();
13258            return;
13259        }
13260
13261        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13262        let buffer = self.buffer.read(cx).snapshot(cx);
13263
13264        let mut edits = Vec::new();
13265        let mut unfold_ranges = Vec::new();
13266        let mut refold_creases = Vec::new();
13267
13268        let selections = self.selections.all::<Point>(&display_map);
13269        let mut selections = selections.iter().peekable();
13270        let mut contiguous_row_selections = Vec::new();
13271        let mut new_selections = Vec::new();
13272
13273        while let Some(selection) = selections.next() {
13274            // Find all the selections that span a contiguous row range
13275            let (start_row, end_row) = consume_contiguous_rows(
13276                &mut contiguous_row_selections,
13277                selection,
13278                &display_map,
13279                &mut selections,
13280            );
13281
13282            // Move the text spanned by the row range to be after the last line of the row range
13283            if end_row.0 <= buffer.max_point().row {
13284                let range_to_move =
13285                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13286                let insertion_point = display_map
13287                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13288                    .0;
13289
13290                // Don't move lines across excerpt boundaries
13291                if buffer
13292                    .excerpt_containing(range_to_move.start..insertion_point)
13293                    .is_some()
13294                {
13295                    let mut text = String::from("\n");
13296                    text.extend(buffer.text_for_range(range_to_move.clone()));
13297                    text.pop(); // Drop trailing newline
13298                    edits.push((
13299                        buffer.anchor_after(range_to_move.start)
13300                            ..buffer.anchor_before(range_to_move.end),
13301                        String::new(),
13302                    ));
13303                    let insertion_anchor = buffer.anchor_after(insertion_point);
13304                    edits.push((insertion_anchor..insertion_anchor, text));
13305
13306                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13307
13308                    // Move selections down
13309                    new_selections.extend(contiguous_row_selections.drain(..).map(
13310                        |mut selection| {
13311                            selection.start.row += row_delta;
13312                            selection.end.row += row_delta;
13313                            selection
13314                        },
13315                    ));
13316
13317                    // Move folds down
13318                    unfold_ranges.push(range_to_move.clone());
13319                    for fold in display_map.folds_in_range(
13320                        buffer.anchor_before(range_to_move.start)
13321                            ..buffer.anchor_after(range_to_move.end),
13322                    ) {
13323                        let mut start = fold.range.start.to_point(&buffer);
13324                        let mut end = fold.range.end.to_point(&buffer);
13325                        start.row += row_delta;
13326                        end.row += row_delta;
13327                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13328                    }
13329                }
13330            }
13331
13332            // If we didn't move line(s), preserve the existing selections
13333            new_selections.append(&mut contiguous_row_selections);
13334        }
13335
13336        self.transact(window, cx, |this, window, cx| {
13337            this.unfold_ranges(&unfold_ranges, true, true, cx);
13338            this.buffer.update(cx, |buffer, cx| {
13339                for (range, text) in edits {
13340                    buffer.edit([(range, text)], None, cx);
13341                }
13342            });
13343            this.fold_creases(refold_creases, true, window, cx);
13344            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13345        });
13346    }
13347
13348    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13349        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13350        let text_layout_details = &self.text_layout_details(window, cx);
13351        self.transact(window, cx, |this, window, cx| {
13352            let edits = this.change_selections(Default::default(), window, cx, |s| {
13353                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13354                s.move_with(&mut |display_map, selection| {
13355                    if !selection.is_empty() {
13356                        return;
13357                    }
13358
13359                    let mut head = selection.head();
13360                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13361                    if head.column() == display_map.line_len(head.row()) {
13362                        transpose_offset = display_map
13363                            .buffer_snapshot()
13364                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13365                    }
13366
13367                    if transpose_offset == MultiBufferOffset(0) {
13368                        return;
13369                    }
13370
13371                    *head.column_mut() += 1;
13372                    head = display_map.clip_point(head, Bias::Right);
13373                    let goal = SelectionGoal::HorizontalPosition(
13374                        display_map
13375                            .x_for_display_point(head, text_layout_details)
13376                            .into(),
13377                    );
13378                    selection.collapse_to(head, goal);
13379
13380                    let transpose_start = display_map
13381                        .buffer_snapshot()
13382                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13383                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13384                        let transpose_end = display_map
13385                            .buffer_snapshot()
13386                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13387                        if let Some(ch) = display_map
13388                            .buffer_snapshot()
13389                            .chars_at(transpose_start)
13390                            .next()
13391                        {
13392                            edits.push((transpose_start..transpose_offset, String::new()));
13393                            edits.push((transpose_end..transpose_end, ch.to_string()));
13394                        }
13395                    }
13396                });
13397                edits
13398            });
13399            this.buffer
13400                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13401            let selections = this
13402                .selections
13403                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13404            this.change_selections(Default::default(), window, cx, |s| {
13405                s.select(selections);
13406            });
13407        });
13408    }
13409
13410    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13411        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13412        if self.mode.is_single_line() {
13413            cx.propagate();
13414            return;
13415        }
13416
13417        self.rewrap_impl(RewrapOptions::default(), cx)
13418    }
13419
13420    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13421        let buffer = self.buffer.read(cx).snapshot(cx);
13422        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13423
13424        #[derive(Clone, Debug, PartialEq)]
13425        enum CommentFormat {
13426            /// single line comment, with prefix for line
13427            Line(String),
13428            /// single line within a block comment, with prefix for line
13429            BlockLine(String),
13430            /// a single line of a block comment that includes the initial delimiter
13431            BlockCommentWithStart(BlockCommentConfig),
13432            /// a single line of a block comment that includes the ending delimiter
13433            BlockCommentWithEnd(BlockCommentConfig),
13434        }
13435
13436        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13437        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13438            let language_settings = buffer.language_settings_at(selection.head(), cx);
13439            let language_scope = buffer.language_scope_at(selection.head());
13440
13441            let indent_and_prefix_for_row =
13442                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13443                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13444                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13445                        &language_scope
13446                    {
13447                        let indent_end = Point::new(row, indent.len);
13448                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13449                        let line_text_after_indent = buffer
13450                            .text_for_range(indent_end..line_end)
13451                            .collect::<String>();
13452
13453                        let is_within_comment_override = buffer
13454                            .language_scope_at(indent_end)
13455                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13456                        let comment_delimiters = if is_within_comment_override {
13457                            // we are within a comment syntax node, but we don't
13458                            // yet know what kind of comment: block, doc or line
13459                            match (
13460                                language_scope.documentation_comment(),
13461                                language_scope.block_comment(),
13462                            ) {
13463                                (Some(config), _) | (_, Some(config))
13464                                    if buffer.contains_str_at(indent_end, &config.start) =>
13465                                {
13466                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13467                                }
13468                                (Some(config), _) | (_, Some(config))
13469                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13470                                {
13471                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13472                                }
13473                                (Some(config), _) | (_, Some(config))
13474                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13475                                {
13476                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13477                                }
13478                                (_, _) => language_scope
13479                                    .line_comment_prefixes()
13480                                    .iter()
13481                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13482                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13483                            }
13484                        } else {
13485                            // we not in an overridden comment node, but we may
13486                            // be within a non-overridden line comment node
13487                            language_scope
13488                                .line_comment_prefixes()
13489                                .iter()
13490                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13491                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13492                        };
13493
13494                        let rewrap_prefix = language_scope
13495                            .rewrap_prefixes()
13496                            .iter()
13497                            .find_map(|prefix_regex| {
13498                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13499                                    if mat.start() == 0 {
13500                                        Some(mat.as_str().to_string())
13501                                    } else {
13502                                        None
13503                                    }
13504                                })
13505                            })
13506                            .flatten();
13507                        (comment_delimiters, rewrap_prefix)
13508                    } else {
13509                        (None, None)
13510                    };
13511                    (indent, comment_prefix, rewrap_prefix)
13512                };
13513
13514            let mut start_row = selection.start.row;
13515            let mut end_row = selection.end.row;
13516
13517            if selection.is_empty() {
13518                let cursor_row = selection.start.row;
13519
13520                let (mut indent_size, comment_prefix, _) = indent_and_prefix_for_row(cursor_row);
13521                let line_prefix = match &comment_prefix {
13522                    Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13523                        Some(prefix.as_str())
13524                    }
13525                    Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13526                        prefix, ..
13527                    })) => Some(prefix.as_ref()),
13528                    Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13529                        start: _,
13530                        end: _,
13531                        prefix,
13532                        tab_size,
13533                    })) => {
13534                        indent_size.len += tab_size;
13535                        Some(prefix.as_ref())
13536                    }
13537                    None => None,
13538                };
13539                let indent_prefix = indent_size.chars().collect::<String>();
13540                let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13541
13542                'expand_upwards: while start_row > 0 {
13543                    let prev_row = start_row - 1;
13544                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13545                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13546                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13547                    {
13548                        start_row = prev_row;
13549                    } else {
13550                        break 'expand_upwards;
13551                    }
13552                }
13553
13554                'expand_downwards: while end_row < buffer.max_point().row {
13555                    let next_row = end_row + 1;
13556                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13557                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13558                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13559                    {
13560                        end_row = next_row;
13561                    } else {
13562                        break 'expand_downwards;
13563                    }
13564                }
13565            }
13566
13567            let mut non_blank_rows_iter = (start_row..=end_row)
13568                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13569                .peekable();
13570
13571            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13572                row
13573            } else {
13574                return Vec::new();
13575            };
13576
13577            let mut ranges = Vec::new();
13578
13579            let mut current_range_start = first_row;
13580            let mut prev_row = first_row;
13581            let (
13582                mut current_range_indent,
13583                mut current_range_comment_delimiters,
13584                mut current_range_rewrap_prefix,
13585            ) = indent_and_prefix_for_row(first_row);
13586
13587            for row in non_blank_rows_iter.skip(1) {
13588                let has_paragraph_break = row > prev_row + 1;
13589
13590                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13591                    indent_and_prefix_for_row(row);
13592
13593                let has_indent_change = row_indent != current_range_indent;
13594                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13595
13596                let has_boundary_change = has_comment_change
13597                    || row_rewrap_prefix.is_some()
13598                    || (has_indent_change && current_range_comment_delimiters.is_some());
13599
13600                if has_paragraph_break || has_boundary_change {
13601                    ranges.push((
13602                        language_settings.clone(),
13603                        Point::new(current_range_start, 0)
13604                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13605                        current_range_indent,
13606                        current_range_comment_delimiters.clone(),
13607                        current_range_rewrap_prefix.clone(),
13608                    ));
13609                    current_range_start = row;
13610                    current_range_indent = row_indent;
13611                    current_range_comment_delimiters = row_comment_delimiters;
13612                    current_range_rewrap_prefix = row_rewrap_prefix;
13613                }
13614                prev_row = row;
13615            }
13616
13617            ranges.push((
13618                language_settings.clone(),
13619                Point::new(current_range_start, 0)
13620                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13621                current_range_indent,
13622                current_range_comment_delimiters,
13623                current_range_rewrap_prefix,
13624            ));
13625
13626            ranges
13627        });
13628
13629        let mut edits = Vec::new();
13630        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13631
13632        for (language_settings, wrap_range, mut indent_size, comment_prefix, rewrap_prefix) in
13633            wrap_ranges
13634        {
13635            let start_row = wrap_range.start.row;
13636            let end_row = wrap_range.end.row;
13637
13638            // Skip selections that overlap with a range that has already been rewrapped.
13639            let selection_range = start_row..end_row;
13640            if rewrapped_row_ranges
13641                .iter()
13642                .any(|range| range.overlaps(&selection_range))
13643            {
13644                continue;
13645            }
13646
13647            let tab_size = language_settings.tab_size;
13648
13649            let (line_prefix, inside_comment) = match &comment_prefix {
13650                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13651                    (Some(prefix.as_str()), true)
13652                }
13653                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13654                    (Some(prefix.as_ref()), true)
13655                }
13656                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13657                    start: _,
13658                    end: _,
13659                    prefix,
13660                    tab_size,
13661                })) => {
13662                    indent_size.len += tab_size;
13663                    (Some(prefix.as_ref()), true)
13664                }
13665                None => (None, false),
13666            };
13667            let indent_prefix = indent_size.chars().collect::<String>();
13668            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13669
13670            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13671                RewrapBehavior::InComments => inside_comment,
13672                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13673                RewrapBehavior::Anywhere => true,
13674            };
13675
13676            let should_rewrap = options.override_language_settings
13677                || allow_rewrap_based_on_language
13678                || self.hard_wrap.is_some();
13679            if !should_rewrap {
13680                continue;
13681            }
13682
13683            let start = Point::new(start_row, 0);
13684            let start_offset = ToOffset::to_offset(&start, &buffer);
13685            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13686            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13687            let mut first_line_delimiter = None;
13688            let mut last_line_delimiter = None;
13689            let Some(lines_without_prefixes) = selection_text
13690                .lines()
13691                .enumerate()
13692                .map(|(ix, line)| {
13693                    let line_trimmed = line.trim_start();
13694                    if rewrap_prefix.is_some() && ix > 0 {
13695                        Ok(line_trimmed)
13696                    } else if let Some(
13697                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13698                            start,
13699                            prefix,
13700                            end,
13701                            tab_size,
13702                        })
13703                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13704                            start,
13705                            prefix,
13706                            end,
13707                            tab_size,
13708                        }),
13709                    ) = &comment_prefix
13710                    {
13711                        let line_trimmed = line_trimmed
13712                            .strip_prefix(start.as_ref())
13713                            .map(|s| {
13714                                let mut indent_size = indent_size;
13715                                indent_size.len -= tab_size;
13716                                let indent_prefix: String = indent_size.chars().collect();
13717                                first_line_delimiter = Some((indent_prefix, start));
13718                                s.trim_start()
13719                            })
13720                            .unwrap_or(line_trimmed);
13721                        let line_trimmed = line_trimmed
13722                            .strip_suffix(end.as_ref())
13723                            .map(|s| {
13724                                last_line_delimiter = Some(end);
13725                                s.trim_end()
13726                            })
13727                            .unwrap_or(line_trimmed);
13728                        let line_trimmed = line_trimmed
13729                            .strip_prefix(prefix.as_ref())
13730                            .unwrap_or(line_trimmed);
13731                        Ok(line_trimmed)
13732                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13733                        line_trimmed.strip_prefix(prefix).with_context(|| {
13734                            format!("line did not start with prefix {prefix:?}: {line:?}")
13735                        })
13736                    } else {
13737                        line_trimmed
13738                            .strip_prefix(&line_prefix.trim_start())
13739                            .with_context(|| {
13740                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13741                            })
13742                    }
13743                })
13744                .collect::<Result<Vec<_>, _>>()
13745                .log_err()
13746            else {
13747                continue;
13748            };
13749
13750            let wrap_column = options.line_length.or(self.hard_wrap).unwrap_or_else(|| {
13751                buffer
13752                    .language_settings_at(Point::new(start_row, 0), cx)
13753                    .preferred_line_length as usize
13754            });
13755
13756            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13757                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13758            } else {
13759                line_prefix.clone()
13760            };
13761
13762            let wrapped_text = {
13763                let mut wrapped_text = wrap_with_prefix(
13764                    line_prefix,
13765                    subsequent_lines_prefix,
13766                    lines_without_prefixes.join("\n"),
13767                    wrap_column,
13768                    tab_size,
13769                    options.preserve_existing_whitespace,
13770                );
13771
13772                if let Some((indent, delimiter)) = first_line_delimiter {
13773                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13774                }
13775                if let Some(last_line) = last_line_delimiter {
13776                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13777                }
13778
13779                wrapped_text
13780            };
13781
13782            // TODO: should always use char-based diff while still supporting cursor behavior that
13783            // matches vim.
13784            let mut diff_options = DiffOptions::default();
13785            if options.override_language_settings {
13786                diff_options.max_word_diff_len = 0;
13787                diff_options.max_word_diff_line_count = 0;
13788            } else {
13789                diff_options.max_word_diff_len = usize::MAX;
13790                diff_options.max_word_diff_line_count = usize::MAX;
13791            }
13792
13793            for (old_range, new_text) in
13794                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13795            {
13796                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13797                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13798                edits.push((edit_start..edit_end, new_text));
13799            }
13800
13801            rewrapped_row_ranges.push(start_row..=end_row);
13802        }
13803
13804        self.buffer
13805            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13806    }
13807
13808    pub fn cut_common(
13809        &mut self,
13810        cut_no_selection_line: bool,
13811        window: &mut Window,
13812        cx: &mut Context<Self>,
13813    ) -> ClipboardItem {
13814        let mut text = String::new();
13815        let buffer = self.buffer.read(cx).snapshot(cx);
13816        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13817        let mut clipboard_selections = Vec::with_capacity(selections.len());
13818        {
13819            let max_point = buffer.max_point();
13820            let mut is_first = true;
13821            let mut prev_selection_was_entire_line = false;
13822            for selection in &mut selections {
13823                let is_entire_line =
13824                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13825                if is_entire_line {
13826                    selection.start = Point::new(selection.start.row, 0);
13827                    if !selection.is_empty() && selection.end.column == 0 {
13828                        selection.end = cmp::min(max_point, selection.end);
13829                    } else {
13830                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13831                    }
13832                    selection.goal = SelectionGoal::None;
13833                }
13834                if is_first {
13835                    is_first = false;
13836                } else if !prev_selection_was_entire_line {
13837                    text += "\n";
13838                }
13839                prev_selection_was_entire_line = is_entire_line;
13840                let mut len = 0;
13841                for chunk in buffer.text_for_range(selection.start..selection.end) {
13842                    text.push_str(chunk);
13843                    len += chunk.len();
13844                }
13845
13846                clipboard_selections.push(ClipboardSelection::for_buffer(
13847                    len,
13848                    is_entire_line,
13849                    selection.range(),
13850                    &buffer,
13851                    self.project.as_ref(),
13852                    cx,
13853                ));
13854            }
13855        }
13856
13857        self.transact(window, cx, |this, window, cx| {
13858            this.change_selections(Default::default(), window, cx, |s| {
13859                s.select(selections);
13860            });
13861            this.insert("", window, cx);
13862        });
13863        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13864    }
13865
13866    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13867        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13868        let item = self.cut_common(true, window, cx);
13869        cx.write_to_clipboard(item);
13870    }
13871
13872    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13873        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13874        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13875            s.move_with(&mut |snapshot, sel| {
13876                if sel.is_empty() {
13877                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13878                }
13879                if sel.is_empty() {
13880                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13881                }
13882            });
13883        });
13884        let item = self.cut_common(false, window, cx);
13885        cx.set_global(KillRing(item))
13886    }
13887
13888    pub fn kill_ring_yank(
13889        &mut self,
13890        _: &KillRingYank,
13891        window: &mut Window,
13892        cx: &mut Context<Self>,
13893    ) {
13894        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13895        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13896            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13897                (kill_ring.text().to_string(), kill_ring.metadata_json())
13898            } else {
13899                return;
13900            }
13901        } else {
13902            return;
13903        };
13904        self.do_paste(&text, metadata, false, window, cx);
13905    }
13906
13907    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13908        self.do_copy(true, cx);
13909    }
13910
13911    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13912        self.do_copy(false, cx);
13913    }
13914
13915    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13916        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13917        let buffer = self.buffer.read(cx).read(cx);
13918        let mut text = String::new();
13919        let mut clipboard_selections = Vec::with_capacity(selections.len());
13920
13921        let max_point = buffer.max_point();
13922        let mut is_first = true;
13923        for selection in &selections {
13924            let mut start = selection.start;
13925            let mut end = selection.end;
13926            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13927            let mut add_trailing_newline = false;
13928            if is_entire_line {
13929                start = Point::new(start.row, 0);
13930                let next_line_start = Point::new(end.row + 1, 0);
13931                if next_line_start <= max_point {
13932                    end = next_line_start;
13933                } else {
13934                    // We're on the last line without a trailing newline.
13935                    // Copy to the end of the line and add a newline afterwards.
13936                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13937                    add_trailing_newline = true;
13938                }
13939            }
13940
13941            let mut trimmed_selections = Vec::new();
13942            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13943                let row = MultiBufferRow(start.row);
13944                let first_indent = buffer.indent_size_for_line(row);
13945                if first_indent.len == 0 || start.column > first_indent.len {
13946                    trimmed_selections.push(start..end);
13947                } else {
13948                    trimmed_selections.push(
13949                        Point::new(row.0, first_indent.len)
13950                            ..Point::new(row.0, buffer.line_len(row)),
13951                    );
13952                    for row in start.row + 1..=end.row {
13953                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13954                        if row == end.row {
13955                            line_len = end.column;
13956                        }
13957                        if line_len == 0 {
13958                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13959                            continue;
13960                        }
13961                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13962                        if row_indent_size.len >= first_indent.len {
13963                            trimmed_selections
13964                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13965                        } else {
13966                            trimmed_selections.clear();
13967                            trimmed_selections.push(start..end);
13968                            break;
13969                        }
13970                    }
13971                }
13972            } else {
13973                trimmed_selections.push(start..end);
13974            }
13975
13976            let is_multiline_trim = trimmed_selections.len() > 1;
13977            let mut selection_len: usize = 0;
13978            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13979
13980            for trimmed_range in trimmed_selections {
13981                if is_first {
13982                    is_first = false;
13983                } else if is_multiline_trim || !prev_selection_was_entire_line {
13984                    text.push('\n');
13985                    if is_multiline_trim {
13986                        selection_len += 1;
13987                    }
13988                }
13989                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13990                    text.push_str(chunk);
13991                    selection_len += chunk.len();
13992                }
13993                if add_trailing_newline {
13994                    text.push('\n');
13995                    selection_len += 1;
13996                }
13997            }
13998
13999            clipboard_selections.push(ClipboardSelection::for_buffer(
14000                selection_len,
14001                is_entire_line,
14002                start..end,
14003                &buffer,
14004                self.project.as_ref(),
14005                cx,
14006            ));
14007        }
14008
14009        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
14010            text,
14011            clipboard_selections,
14012        ));
14013    }
14014
14015    pub fn do_paste(
14016        &mut self,
14017        text: &String,
14018        clipboard_selections: Option<Vec<ClipboardSelection>>,
14019        handle_entire_lines: bool,
14020        window: &mut Window,
14021        cx: &mut Context<Self>,
14022    ) {
14023        if self.read_only(cx) {
14024            return;
14025        }
14026
14027        let clipboard_text = Cow::Borrowed(text.as_str());
14028
14029        self.transact(window, cx, |this, window, cx| {
14030            let had_active_edit_prediction = this.has_active_edit_prediction();
14031            let display_map = this.display_snapshot(cx);
14032            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
14033            let cursor_offset = this
14034                .selections
14035                .last::<MultiBufferOffset>(&display_map)
14036                .head();
14037
14038            if let Some(mut clipboard_selections) = clipboard_selections {
14039                let all_selections_were_entire_line =
14040                    clipboard_selections.iter().all(|s| s.is_entire_line);
14041                let first_selection_indent_column =
14042                    clipboard_selections.first().map(|s| s.first_line_indent);
14043                if clipboard_selections.len() != old_selections.len() {
14044                    clipboard_selections.drain(..);
14045                }
14046                let mut auto_indent_on_paste = true;
14047
14048                this.buffer.update(cx, |buffer, cx| {
14049                    let snapshot = buffer.read(cx);
14050                    auto_indent_on_paste = snapshot
14051                        .language_settings_at(cursor_offset, cx)
14052                        .auto_indent_on_paste;
14053
14054                    let mut start_offset = 0;
14055                    let mut edits = Vec::new();
14056                    let mut original_indent_columns = Vec::new();
14057                    for (ix, selection) in old_selections.iter().enumerate() {
14058                        let to_insert;
14059                        let entire_line;
14060                        let original_indent_column;
14061                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
14062                            let end_offset = start_offset + clipboard_selection.len;
14063                            to_insert = &clipboard_text[start_offset..end_offset];
14064                            entire_line = clipboard_selection.is_entire_line;
14065                            start_offset = if entire_line {
14066                                end_offset
14067                            } else {
14068                                end_offset + 1
14069                            };
14070                            original_indent_column = Some(clipboard_selection.first_line_indent);
14071                        } else {
14072                            to_insert = &*clipboard_text;
14073                            entire_line = all_selections_were_entire_line;
14074                            original_indent_column = first_selection_indent_column
14075                        }
14076
14077                        let (range, to_insert) =
14078                            if selection.is_empty() && handle_entire_lines && entire_line {
14079                                // If the corresponding selection was empty when this slice of the
14080                                // clipboard text was written, then the entire line containing the
14081                                // selection was copied. If this selection is also currently empty,
14082                                // then paste the line before the current line of the buffer.
14083                                let column = selection.start.to_point(&snapshot).column as usize;
14084                                let line_start = selection.start - column;
14085                                (line_start..line_start, Cow::Borrowed(to_insert))
14086                            } else {
14087                                let language = snapshot.language_at(selection.head());
14088                                let range = selection.range();
14089                                if let Some(language) = language
14090                                    && language.name() == "Markdown"
14091                                {
14092                                    edit_for_markdown_paste(
14093                                        &snapshot,
14094                                        range,
14095                                        to_insert,
14096                                        url::Url::parse(to_insert).ok(),
14097                                    )
14098                                } else {
14099                                    (range, Cow::Borrowed(to_insert))
14100                                }
14101                            };
14102
14103                        edits.push((range, to_insert));
14104                        original_indent_columns.push(original_indent_column);
14105                    }
14106                    drop(snapshot);
14107
14108                    buffer.edit(
14109                        edits,
14110                        if auto_indent_on_paste {
14111                            Some(AutoindentMode::Block {
14112                                original_indent_columns,
14113                            })
14114                        } else {
14115                            None
14116                        },
14117                        cx,
14118                    );
14119                });
14120
14121                let selections = this
14122                    .selections
14123                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
14124                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14125            } else {
14126                let url = url::Url::parse(&clipboard_text).ok();
14127
14128                let auto_indent_mode = if !clipboard_text.is_empty() {
14129                    Some(AutoindentMode::Block {
14130                        original_indent_columns: Vec::new(),
14131                    })
14132                } else {
14133                    None
14134                };
14135
14136                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
14137                    let snapshot = buffer.snapshot(cx);
14138
14139                    let anchors = old_selections
14140                        .iter()
14141                        .map(|s| {
14142                            let anchor = snapshot.anchor_after(s.head());
14143                            s.map(|_| anchor)
14144                        })
14145                        .collect::<Vec<_>>();
14146
14147                    let mut edits = Vec::new();
14148
14149                    // When pasting text without metadata (e.g. copied from an
14150                    // external editor using multiple cursors) and the number of
14151                    // lines matches the number of selections, distribute one
14152                    // line per cursor instead of pasting the whole text at each.
14153                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14154                    let distribute_lines =
14155                        old_selections.len() > 1 && lines.len() == old_selections.len();
14156
14157                    for (ix, selection) in old_selections.iter().enumerate() {
14158                        let language = snapshot.language_at(selection.head());
14159                        let range = selection.range();
14160
14161                        let text_for_cursor: &str = if distribute_lines {
14162                            lines[ix]
14163                        } else {
14164                            &clipboard_text
14165                        };
14166
14167                        let (edit_range, edit_text) = if let Some(language) = language
14168                            && language.name() == "Markdown"
14169                        {
14170                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14171                        } else {
14172                            (range, Cow::Borrowed(text_for_cursor))
14173                        };
14174
14175                        edits.push((edit_range, edit_text));
14176                    }
14177
14178                    drop(snapshot);
14179                    buffer.edit(edits, auto_indent_mode, cx);
14180
14181                    anchors
14182                });
14183
14184                this.change_selections(Default::default(), window, cx, |s| {
14185                    s.select_anchors(selection_anchors);
14186                });
14187            }
14188
14189            //   🤔                 |    ..     | show_in_menu |
14190            // | ..                  |   true        true
14191            // | had_edit_prediction |   false       true
14192
14193            let trigger_in_words =
14194                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14195
14196            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14197        });
14198    }
14199
14200    pub fn diff_clipboard_with_selection(
14201        &mut self,
14202        _: &DiffClipboardWithSelection,
14203        window: &mut Window,
14204        cx: &mut Context<Self>,
14205    ) {
14206        let selections = self
14207            .selections
14208            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14209
14210        if selections.is_empty() {
14211            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14212            return;
14213        };
14214
14215        let clipboard_text = cx.read_from_clipboard().and_then(|item| {
14216            item.entries().iter().find_map(|entry| match entry {
14217                ClipboardEntry::String(text) => Some(text.text().to_string()),
14218                _ => None,
14219            })
14220        });
14221
14222        let Some(clipboard_text) = clipboard_text else {
14223            log::warn!("Clipboard doesn't contain text.");
14224            return;
14225        };
14226
14227        window.dispatch_action(
14228            Box::new(DiffClipboardWithSelectionData {
14229                clipboard_text,
14230                editor: cx.entity(),
14231            }),
14232            cx,
14233        );
14234    }
14235
14236    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14237        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14238        if let Some(item) = cx.read_from_clipboard() {
14239            let clipboard_string = item.entries().iter().find_map(|entry| match entry {
14240                ClipboardEntry::String(s) => Some(s),
14241                _ => None,
14242            });
14243            match clipboard_string {
14244                Some(clipboard_string) => self.do_paste(
14245                    clipboard_string.text(),
14246                    clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14247                    true,
14248                    window,
14249                    cx,
14250                ),
14251                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14252            }
14253        }
14254    }
14255
14256    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14257        if self.read_only(cx) {
14258            return;
14259        }
14260
14261        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14262
14263        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14264            if let Some((selections, _)) =
14265                self.selection_history.transaction(transaction_id).cloned()
14266            {
14267                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14268                    s.select_anchors(selections.to_vec());
14269                });
14270            } else {
14271                log::error!(
14272                    "No entry in selection_history found for undo. \
14273                     This may correspond to a bug where undo does not update the selection. \
14274                     If this is occurring, please add details to \
14275                     https://github.com/zed-industries/zed/issues/22692"
14276                );
14277            }
14278            self.request_autoscroll(Autoscroll::fit(), cx);
14279            self.unmark_text(window, cx);
14280            self.refresh_edit_prediction(true, false, window, cx);
14281            cx.emit(EditorEvent::Edited { transaction_id });
14282            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14283        }
14284    }
14285
14286    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14287        if self.read_only(cx) {
14288            return;
14289        }
14290
14291        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14292
14293        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14294            if let Some((_, Some(selections))) =
14295                self.selection_history.transaction(transaction_id).cloned()
14296            {
14297                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14298                    s.select_anchors(selections.to_vec());
14299                });
14300            } else {
14301                log::error!(
14302                    "No entry in selection_history found for redo. \
14303                     This may correspond to a bug where undo does not update the selection. \
14304                     If this is occurring, please add details to \
14305                     https://github.com/zed-industries/zed/issues/22692"
14306                );
14307            }
14308            self.request_autoscroll(Autoscroll::fit(), cx);
14309            self.unmark_text(window, cx);
14310            self.refresh_edit_prediction(true, false, window, cx);
14311            cx.emit(EditorEvent::Edited { transaction_id });
14312        }
14313    }
14314
14315    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14316        self.buffer
14317            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14318    }
14319
14320    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14321        self.buffer
14322            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14323    }
14324
14325    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14326        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14327        self.change_selections(Default::default(), window, cx, |s| {
14328            s.move_with(&mut |map, selection| {
14329                let cursor = if selection.is_empty() {
14330                    movement::left(map, selection.start)
14331                } else {
14332                    selection.start
14333                };
14334                selection.collapse_to(cursor, SelectionGoal::None);
14335            });
14336        })
14337    }
14338
14339    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14340        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14341        self.change_selections(Default::default(), window, cx, |s| {
14342            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14343        })
14344    }
14345
14346    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14347        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14348        self.change_selections(Default::default(), window, cx, |s| {
14349            s.move_with(&mut |map, selection| {
14350                let cursor = if selection.is_empty() {
14351                    movement::right(map, selection.end)
14352                } else {
14353                    selection.end
14354                };
14355                selection.collapse_to(cursor, SelectionGoal::None)
14356            });
14357        })
14358    }
14359
14360    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14361        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14362        self.change_selections(Default::default(), window, cx, |s| {
14363            s.move_heads_with(&mut |map, head, _| {
14364                (movement::right(map, head), SelectionGoal::None)
14365            });
14366        });
14367    }
14368
14369    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14370        if self.take_rename(true, window, cx).is_some() {
14371            return;
14372        }
14373
14374        if self.mode.is_single_line() {
14375            cx.propagate();
14376            return;
14377        }
14378
14379        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14380
14381        let text_layout_details = &self.text_layout_details(window, cx);
14382        let selection_count = self.selections.count();
14383        let first_selection = self.selections.first_anchor();
14384
14385        self.change_selections(Default::default(), window, cx, |s| {
14386            s.move_with(&mut |map, selection| {
14387                if !selection.is_empty() {
14388                    selection.goal = SelectionGoal::None;
14389                }
14390                let (cursor, goal) = movement::up(
14391                    map,
14392                    selection.start,
14393                    selection.goal,
14394                    false,
14395                    text_layout_details,
14396                );
14397                selection.collapse_to(cursor, goal);
14398            });
14399        });
14400
14401        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14402        {
14403            cx.propagate();
14404        }
14405    }
14406
14407    pub fn move_up_by_lines(
14408        &mut self,
14409        action: &MoveUpByLines,
14410        window: &mut Window,
14411        cx: &mut Context<Self>,
14412    ) {
14413        if self.take_rename(true, window, cx).is_some() {
14414            return;
14415        }
14416
14417        if self.mode.is_single_line() {
14418            cx.propagate();
14419            return;
14420        }
14421
14422        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14423
14424        let text_layout_details = &self.text_layout_details(window, cx);
14425
14426        self.change_selections(Default::default(), window, cx, |s| {
14427            s.move_with(&mut |map, selection| {
14428                if !selection.is_empty() {
14429                    selection.goal = SelectionGoal::None;
14430                }
14431                let (cursor, goal) = movement::up_by_rows(
14432                    map,
14433                    selection.start,
14434                    action.lines,
14435                    selection.goal,
14436                    false,
14437                    text_layout_details,
14438                );
14439                selection.collapse_to(cursor, goal);
14440            });
14441        })
14442    }
14443
14444    pub fn move_down_by_lines(
14445        &mut self,
14446        action: &MoveDownByLines,
14447        window: &mut Window,
14448        cx: &mut Context<Self>,
14449    ) {
14450        if self.take_rename(true, window, cx).is_some() {
14451            return;
14452        }
14453
14454        if self.mode.is_single_line() {
14455            cx.propagate();
14456            return;
14457        }
14458
14459        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14460
14461        let text_layout_details = &self.text_layout_details(window, cx);
14462
14463        self.change_selections(Default::default(), window, cx, |s| {
14464            s.move_with(&mut |map, selection| {
14465                if !selection.is_empty() {
14466                    selection.goal = SelectionGoal::None;
14467                }
14468                let (cursor, goal) = movement::down_by_rows(
14469                    map,
14470                    selection.start,
14471                    action.lines,
14472                    selection.goal,
14473                    false,
14474                    text_layout_details,
14475                );
14476                selection.collapse_to(cursor, goal);
14477            });
14478        })
14479    }
14480
14481    pub fn select_down_by_lines(
14482        &mut self,
14483        action: &SelectDownByLines,
14484        window: &mut Window,
14485        cx: &mut Context<Self>,
14486    ) {
14487        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14488        let text_layout_details = &self.text_layout_details(window, cx);
14489        self.change_selections(Default::default(), window, cx, |s| {
14490            s.move_heads_with(&mut |map, head, goal| {
14491                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14492            })
14493        })
14494    }
14495
14496    pub fn select_up_by_lines(
14497        &mut self,
14498        action: &SelectUpByLines,
14499        window: &mut Window,
14500        cx: &mut Context<Self>,
14501    ) {
14502        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14503        let text_layout_details = &self.text_layout_details(window, cx);
14504        self.change_selections(Default::default(), window, cx, |s| {
14505            s.move_heads_with(&mut |map, head, goal| {
14506                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14507            })
14508        })
14509    }
14510
14511    pub fn select_page_up(
14512        &mut self,
14513        _: &SelectPageUp,
14514        window: &mut Window,
14515        cx: &mut Context<Self>,
14516    ) {
14517        let Some(row_count) = self.visible_row_count() else {
14518            return;
14519        };
14520
14521        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14522
14523        let text_layout_details = &self.text_layout_details(window, cx);
14524
14525        self.change_selections(Default::default(), window, cx, |s| {
14526            s.move_heads_with(&mut |map, head, goal| {
14527                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14528            })
14529        })
14530    }
14531
14532    pub fn move_page_up(
14533        &mut self,
14534        action: &MovePageUp,
14535        window: &mut Window,
14536        cx: &mut Context<Self>,
14537    ) {
14538        if self.take_rename(true, window, cx).is_some() {
14539            return;
14540        }
14541
14542        if self
14543            .context_menu
14544            .borrow_mut()
14545            .as_mut()
14546            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14547            .unwrap_or(false)
14548        {
14549            return;
14550        }
14551
14552        if matches!(self.mode, EditorMode::SingleLine) {
14553            cx.propagate();
14554            return;
14555        }
14556
14557        let Some(row_count) = self.visible_row_count() else {
14558            return;
14559        };
14560
14561        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14562
14563        let effects = if action.center_cursor {
14564            SelectionEffects::scroll(Autoscroll::center())
14565        } else {
14566            SelectionEffects::default()
14567        };
14568
14569        let text_layout_details = &self.text_layout_details(window, cx);
14570
14571        self.change_selections(effects, window, cx, |s| {
14572            s.move_with(&mut |map, selection| {
14573                if !selection.is_empty() {
14574                    selection.goal = SelectionGoal::None;
14575                }
14576                let (cursor, goal) = movement::up_by_rows(
14577                    map,
14578                    selection.end,
14579                    row_count,
14580                    selection.goal,
14581                    false,
14582                    text_layout_details,
14583                );
14584                selection.collapse_to(cursor, goal);
14585            });
14586        });
14587    }
14588
14589    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14590        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14591        let text_layout_details = &self.text_layout_details(window, cx);
14592        self.change_selections(Default::default(), window, cx, |s| {
14593            s.move_heads_with(&mut |map, head, goal| {
14594                movement::up(map, head, goal, false, text_layout_details)
14595            })
14596        })
14597    }
14598
14599    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14600        self.take_rename(true, window, cx);
14601
14602        if self.mode.is_single_line() {
14603            cx.propagate();
14604            return;
14605        }
14606
14607        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14608
14609        let text_layout_details = &self.text_layout_details(window, cx);
14610        let selection_count = self.selections.count();
14611        let first_selection = self.selections.first_anchor();
14612
14613        self.change_selections(Default::default(), window, cx, |s| {
14614            s.move_with(&mut |map, selection| {
14615                if !selection.is_empty() {
14616                    selection.goal = SelectionGoal::None;
14617                }
14618                let (cursor, goal) = movement::down(
14619                    map,
14620                    selection.end,
14621                    selection.goal,
14622                    false,
14623                    text_layout_details,
14624                );
14625                selection.collapse_to(cursor, goal);
14626            });
14627        });
14628
14629        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14630        {
14631            cx.propagate();
14632        }
14633    }
14634
14635    pub fn select_page_down(
14636        &mut self,
14637        _: &SelectPageDown,
14638        window: &mut Window,
14639        cx: &mut Context<Self>,
14640    ) {
14641        let Some(row_count) = self.visible_row_count() else {
14642            return;
14643        };
14644
14645        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14646
14647        let text_layout_details = &self.text_layout_details(window, cx);
14648
14649        self.change_selections(Default::default(), window, cx, |s| {
14650            s.move_heads_with(&mut |map, head, goal| {
14651                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14652            })
14653        })
14654    }
14655
14656    pub fn move_page_down(
14657        &mut self,
14658        action: &MovePageDown,
14659        window: &mut Window,
14660        cx: &mut Context<Self>,
14661    ) {
14662        if self.take_rename(true, window, cx).is_some() {
14663            return;
14664        }
14665
14666        if self
14667            .context_menu
14668            .borrow_mut()
14669            .as_mut()
14670            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14671            .unwrap_or(false)
14672        {
14673            return;
14674        }
14675
14676        if matches!(self.mode, EditorMode::SingleLine) {
14677            cx.propagate();
14678            return;
14679        }
14680
14681        let Some(row_count) = self.visible_row_count() else {
14682            return;
14683        };
14684
14685        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14686
14687        let effects = if action.center_cursor {
14688            SelectionEffects::scroll(Autoscroll::center())
14689        } else {
14690            SelectionEffects::default()
14691        };
14692
14693        let text_layout_details = &self.text_layout_details(window, cx);
14694        self.change_selections(effects, window, cx, |s| {
14695            s.move_with(&mut |map, selection| {
14696                if !selection.is_empty() {
14697                    selection.goal = SelectionGoal::None;
14698                }
14699                let (cursor, goal) = movement::down_by_rows(
14700                    map,
14701                    selection.end,
14702                    row_count,
14703                    selection.goal,
14704                    false,
14705                    text_layout_details,
14706                );
14707                selection.collapse_to(cursor, goal);
14708            });
14709        });
14710    }
14711
14712    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14713        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14714        let text_layout_details = &self.text_layout_details(window, cx);
14715        self.change_selections(Default::default(), window, cx, |s| {
14716            s.move_heads_with(&mut |map, head, goal| {
14717                movement::down(map, head, goal, false, text_layout_details)
14718            })
14719        });
14720    }
14721
14722    pub fn context_menu_first(
14723        &mut self,
14724        _: &ContextMenuFirst,
14725        window: &mut Window,
14726        cx: &mut Context<Self>,
14727    ) {
14728        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14729            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14730        }
14731    }
14732
14733    pub fn context_menu_prev(
14734        &mut self,
14735        _: &ContextMenuPrevious,
14736        window: &mut Window,
14737        cx: &mut Context<Self>,
14738    ) {
14739        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14740            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14741        }
14742    }
14743
14744    pub fn context_menu_next(
14745        &mut self,
14746        _: &ContextMenuNext,
14747        window: &mut Window,
14748        cx: &mut Context<Self>,
14749    ) {
14750        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14751            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14752        }
14753    }
14754
14755    pub fn context_menu_last(
14756        &mut self,
14757        _: &ContextMenuLast,
14758        window: &mut Window,
14759        cx: &mut Context<Self>,
14760    ) {
14761        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14762            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14763        }
14764    }
14765
14766    pub fn signature_help_prev(
14767        &mut self,
14768        _: &SignatureHelpPrevious,
14769        _: &mut Window,
14770        cx: &mut Context<Self>,
14771    ) {
14772        if let Some(popover) = self.signature_help_state.popover_mut() {
14773            if popover.current_signature == 0 {
14774                popover.current_signature = popover.signatures.len() - 1;
14775            } else {
14776                popover.current_signature -= 1;
14777            }
14778            cx.notify();
14779        }
14780    }
14781
14782    pub fn signature_help_next(
14783        &mut self,
14784        _: &SignatureHelpNext,
14785        _: &mut Window,
14786        cx: &mut Context<Self>,
14787    ) {
14788        if let Some(popover) = self.signature_help_state.popover_mut() {
14789            if popover.current_signature + 1 == popover.signatures.len() {
14790                popover.current_signature = 0;
14791            } else {
14792                popover.current_signature += 1;
14793            }
14794            cx.notify();
14795        }
14796    }
14797
14798    pub fn move_to_previous_word_start(
14799        &mut self,
14800        _: &MoveToPreviousWordStart,
14801        window: &mut Window,
14802        cx: &mut Context<Self>,
14803    ) {
14804        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14805        self.change_selections(Default::default(), window, cx, |s| {
14806            s.move_cursors_with(&mut |map, head, _| {
14807                (
14808                    movement::previous_word_start(map, head),
14809                    SelectionGoal::None,
14810                )
14811            });
14812        })
14813    }
14814
14815    pub fn move_to_previous_subword_start(
14816        &mut self,
14817        _: &MoveToPreviousSubwordStart,
14818        window: &mut Window,
14819        cx: &mut Context<Self>,
14820    ) {
14821        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14822        self.change_selections(Default::default(), window, cx, |s| {
14823            s.move_cursors_with(&mut |map, head, _| {
14824                (
14825                    movement::previous_subword_start(map, head),
14826                    SelectionGoal::None,
14827                )
14828            });
14829        })
14830    }
14831
14832    pub fn select_to_previous_word_start(
14833        &mut self,
14834        _: &SelectToPreviousWordStart,
14835        window: &mut Window,
14836        cx: &mut Context<Self>,
14837    ) {
14838        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14839        self.change_selections(Default::default(), window, cx, |s| {
14840            s.move_heads_with(&mut |map, head, _| {
14841                (
14842                    movement::previous_word_start(map, head),
14843                    SelectionGoal::None,
14844                )
14845            });
14846        })
14847    }
14848
14849    pub fn select_to_previous_subword_start(
14850        &mut self,
14851        _: &SelectToPreviousSubwordStart,
14852        window: &mut Window,
14853        cx: &mut Context<Self>,
14854    ) {
14855        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14856        self.change_selections(Default::default(), window, cx, |s| {
14857            s.move_heads_with(&mut |map, head, _| {
14858                (
14859                    movement::previous_subword_start(map, head),
14860                    SelectionGoal::None,
14861                )
14862            });
14863        })
14864    }
14865
14866    pub fn delete_to_previous_word_start(
14867        &mut self,
14868        action: &DeleteToPreviousWordStart,
14869        window: &mut Window,
14870        cx: &mut Context<Self>,
14871    ) {
14872        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14873        self.transact(window, cx, |this, window, cx| {
14874            this.select_autoclose_pair(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::previous_word_start(map, selection.head())
14880                        } else {
14881                            movement::previous_word_start_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 delete_to_previous_subword_start(
14898        &mut self,
14899        action: &DeleteToPreviousSubwordStart,
14900        window: &mut Window,
14901        cx: &mut Context<Self>,
14902    ) {
14903        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14904        self.transact(window, cx, |this, window, cx| {
14905            this.select_autoclose_pair(window, cx);
14906            this.change_selections(Default::default(), window, cx, |s| {
14907                s.move_with(&mut |map, selection| {
14908                    if selection.is_empty() {
14909                        let mut cursor = if action.ignore_newlines {
14910                            movement::previous_subword_start(map, selection.head())
14911                        } else {
14912                            movement::previous_subword_start_or_newline(map, selection.head())
14913                        };
14914                        cursor = movement::adjust_greedy_deletion(
14915                            map,
14916                            selection.head(),
14917                            cursor,
14918                            action.ignore_brackets,
14919                        );
14920                        selection.set_head(cursor, SelectionGoal::None);
14921                    }
14922                });
14923            });
14924            this.insert("", window, cx);
14925        });
14926    }
14927
14928    pub fn move_to_next_word_end(
14929        &mut self,
14930        _: &MoveToNextWordEnd,
14931        window: &mut Window,
14932        cx: &mut Context<Self>,
14933    ) {
14934        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14935        self.change_selections(Default::default(), window, cx, |s| {
14936            s.move_cursors_with(&mut |map, head, _| {
14937                (movement::next_word_end(map, head), SelectionGoal::None)
14938            });
14939        })
14940    }
14941
14942    pub fn move_to_next_subword_end(
14943        &mut self,
14944        _: &MoveToNextSubwordEnd,
14945        window: &mut Window,
14946        cx: &mut Context<Self>,
14947    ) {
14948        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14949        self.change_selections(Default::default(), window, cx, |s| {
14950            s.move_cursors_with(&mut |map, head, _| {
14951                (movement::next_subword_end(map, head), SelectionGoal::None)
14952            });
14953        })
14954    }
14955
14956    pub fn select_to_next_word_end(
14957        &mut self,
14958        _: &SelectToNextWordEnd,
14959        window: &mut Window,
14960        cx: &mut Context<Self>,
14961    ) {
14962        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14963        self.change_selections(Default::default(), window, cx, |s| {
14964            s.move_heads_with(&mut |map, head, _| {
14965                (movement::next_word_end(map, head), SelectionGoal::None)
14966            });
14967        })
14968    }
14969
14970    pub fn select_to_next_subword_end(
14971        &mut self,
14972        _: &SelectToNextSubwordEnd,
14973        window: &mut Window,
14974        cx: &mut Context<Self>,
14975    ) {
14976        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14977        self.change_selections(Default::default(), window, cx, |s| {
14978            s.move_heads_with(&mut |map, head, _| {
14979                (movement::next_subword_end(map, head), SelectionGoal::None)
14980            });
14981        })
14982    }
14983
14984    pub fn delete_to_next_word_end(
14985        &mut self,
14986        action: &DeleteToNextWordEnd,
14987        window: &mut Window,
14988        cx: &mut Context<Self>,
14989    ) {
14990        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14991        self.transact(window, cx, |this, window, cx| {
14992            this.change_selections(Default::default(), window, cx, |s| {
14993                s.move_with(&mut |map, selection| {
14994                    if selection.is_empty() {
14995                        let mut cursor = if action.ignore_newlines {
14996                            movement::next_word_end(map, selection.head())
14997                        } else {
14998                            movement::next_word_end_or_newline(map, selection.head())
14999                        };
15000                        cursor = movement::adjust_greedy_deletion(
15001                            map,
15002                            selection.head(),
15003                            cursor,
15004                            action.ignore_brackets,
15005                        );
15006                        selection.set_head(cursor, SelectionGoal::None);
15007                    }
15008                });
15009            });
15010            this.insert("", window, cx);
15011        });
15012    }
15013
15014    pub fn delete_to_next_subword_end(
15015        &mut self,
15016        action: &DeleteToNextSubwordEnd,
15017        window: &mut Window,
15018        cx: &mut Context<Self>,
15019    ) {
15020        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15021        self.transact(window, cx, |this, window, cx| {
15022            this.change_selections(Default::default(), window, cx, |s| {
15023                s.move_with(&mut |map, selection| {
15024                    if selection.is_empty() {
15025                        let mut cursor = if action.ignore_newlines {
15026                            movement::next_subword_end(map, selection.head())
15027                        } else {
15028                            movement::next_subword_end_or_newline(map, selection.head())
15029                        };
15030                        cursor = movement::adjust_greedy_deletion(
15031                            map,
15032                            selection.head(),
15033                            cursor,
15034                            action.ignore_brackets,
15035                        );
15036                        selection.set_head(cursor, SelectionGoal::None);
15037                    }
15038                });
15039            });
15040            this.insert("", window, cx);
15041        });
15042    }
15043
15044    pub fn move_to_beginning_of_line(
15045        &mut self,
15046        action: &MoveToBeginningOfLine,
15047        window: &mut Window,
15048        cx: &mut Context<Self>,
15049    ) {
15050        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15051        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15052        self.change_selections(Default::default(), window, cx, |s| {
15053            s.move_cursors_with(&mut |map, head, _| {
15054                (
15055                    movement::indented_line_beginning(
15056                        map,
15057                        head,
15058                        action.stop_at_soft_wraps,
15059                        stop_at_indent,
15060                    ),
15061                    SelectionGoal::None,
15062                )
15063            });
15064        })
15065    }
15066
15067    pub fn select_to_beginning_of_line(
15068        &mut self,
15069        action: &SelectToBeginningOfLine,
15070        window: &mut Window,
15071        cx: &mut Context<Self>,
15072    ) {
15073        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15074        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15075        self.change_selections(Default::default(), window, cx, |s| {
15076            s.move_heads_with(&mut |map, head, _| {
15077                (
15078                    movement::indented_line_beginning(
15079                        map,
15080                        head,
15081                        action.stop_at_soft_wraps,
15082                        stop_at_indent,
15083                    ),
15084                    SelectionGoal::None,
15085                )
15086            });
15087        });
15088    }
15089
15090    pub fn delete_to_beginning_of_line(
15091        &mut self,
15092        action: &DeleteToBeginningOfLine,
15093        window: &mut Window,
15094        cx: &mut Context<Self>,
15095    ) {
15096        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15097        self.transact(window, cx, |this, window, cx| {
15098            this.change_selections(Default::default(), window, cx, |s| {
15099                s.move_with(&mut |_, selection| {
15100                    selection.reversed = true;
15101                });
15102            });
15103
15104            this.select_to_beginning_of_line(
15105                &SelectToBeginningOfLine {
15106                    stop_at_soft_wraps: false,
15107                    stop_at_indent: action.stop_at_indent,
15108                },
15109                window,
15110                cx,
15111            );
15112            this.backspace(&Backspace, window, cx);
15113        });
15114    }
15115
15116    pub fn move_to_end_of_line(
15117        &mut self,
15118        action: &MoveToEndOfLine,
15119        window: &mut Window,
15120        cx: &mut Context<Self>,
15121    ) {
15122        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15123        self.change_selections(Default::default(), window, cx, |s| {
15124            s.move_cursors_with(&mut |map, head, _| {
15125                (
15126                    movement::line_end(map, head, action.stop_at_soft_wraps),
15127                    SelectionGoal::None,
15128                )
15129            });
15130        })
15131    }
15132
15133    pub fn select_to_end_of_line(
15134        &mut self,
15135        action: &SelectToEndOfLine,
15136        window: &mut Window,
15137        cx: &mut Context<Self>,
15138    ) {
15139        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15140        self.change_selections(Default::default(), window, cx, |s| {
15141            s.move_heads_with(&mut |map, head, _| {
15142                (
15143                    movement::line_end(map, head, action.stop_at_soft_wraps),
15144                    SelectionGoal::None,
15145                )
15146            });
15147        })
15148    }
15149
15150    pub fn delete_to_end_of_line(
15151        &mut self,
15152        _: &DeleteToEndOfLine,
15153        window: &mut Window,
15154        cx: &mut Context<Self>,
15155    ) {
15156        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15157        self.transact(window, cx, |this, window, cx| {
15158            this.select_to_end_of_line(
15159                &SelectToEndOfLine {
15160                    stop_at_soft_wraps: false,
15161                },
15162                window,
15163                cx,
15164            );
15165            this.delete(&Delete, window, cx);
15166        });
15167    }
15168
15169    pub fn cut_to_end_of_line(
15170        &mut self,
15171        action: &CutToEndOfLine,
15172        window: &mut Window,
15173        cx: &mut Context<Self>,
15174    ) {
15175        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15176        self.transact(window, cx, |this, window, cx| {
15177            this.select_to_end_of_line(
15178                &SelectToEndOfLine {
15179                    stop_at_soft_wraps: false,
15180                },
15181                window,
15182                cx,
15183            );
15184            if !action.stop_at_newlines {
15185                this.change_selections(Default::default(), window, cx, |s| {
15186                    s.move_with(&mut |_, sel| {
15187                        if sel.is_empty() {
15188                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15189                        }
15190                    });
15191                });
15192            }
15193            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15194            let item = this.cut_common(false, window, cx);
15195            cx.write_to_clipboard(item);
15196        });
15197    }
15198
15199    pub fn move_to_start_of_paragraph(
15200        &mut self,
15201        _: &MoveToStartOfParagraph,
15202        window: &mut Window,
15203        cx: &mut Context<Self>,
15204    ) {
15205        if matches!(self.mode, EditorMode::SingleLine) {
15206            cx.propagate();
15207            return;
15208        }
15209        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15210        self.change_selections(Default::default(), window, cx, |s| {
15211            s.move_with(&mut |map, selection| {
15212                selection.collapse_to(
15213                    movement::start_of_paragraph(map, selection.head(), 1),
15214                    SelectionGoal::None,
15215                )
15216            });
15217        })
15218    }
15219
15220    pub fn move_to_end_of_paragraph(
15221        &mut self,
15222        _: &MoveToEndOfParagraph,
15223        window: &mut Window,
15224        cx: &mut Context<Self>,
15225    ) {
15226        if matches!(self.mode, EditorMode::SingleLine) {
15227            cx.propagate();
15228            return;
15229        }
15230        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15231        self.change_selections(Default::default(), window, cx, |s| {
15232            s.move_with(&mut |map, selection| {
15233                selection.collapse_to(
15234                    movement::end_of_paragraph(map, selection.head(), 1),
15235                    SelectionGoal::None,
15236                )
15237            });
15238        })
15239    }
15240
15241    pub fn select_to_start_of_paragraph(
15242        &mut self,
15243        _: &SelectToStartOfParagraph,
15244        window: &mut Window,
15245        cx: &mut Context<Self>,
15246    ) {
15247        if matches!(self.mode, EditorMode::SingleLine) {
15248            cx.propagate();
15249            return;
15250        }
15251        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15252        self.change_selections(Default::default(), window, cx, |s| {
15253            s.move_heads_with(&mut |map, head, _| {
15254                (
15255                    movement::start_of_paragraph(map, head, 1),
15256                    SelectionGoal::None,
15257                )
15258            });
15259        })
15260    }
15261
15262    pub fn select_to_end_of_paragraph(
15263        &mut self,
15264        _: &SelectToEndOfParagraph,
15265        window: &mut Window,
15266        cx: &mut Context<Self>,
15267    ) {
15268        if matches!(self.mode, EditorMode::SingleLine) {
15269            cx.propagate();
15270            return;
15271        }
15272        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15273        self.change_selections(Default::default(), window, cx, |s| {
15274            s.move_heads_with(&mut |map, head, _| {
15275                (
15276                    movement::end_of_paragraph(map, head, 1),
15277                    SelectionGoal::None,
15278                )
15279            });
15280        })
15281    }
15282
15283    pub fn move_to_start_of_excerpt(
15284        &mut self,
15285        _: &MoveToStartOfExcerpt,
15286        window: &mut Window,
15287        cx: &mut Context<Self>,
15288    ) {
15289        if matches!(self.mode, EditorMode::SingleLine) {
15290            cx.propagate();
15291            return;
15292        }
15293        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15294        self.change_selections(Default::default(), window, cx, |s| {
15295            s.move_with(&mut |map, selection| {
15296                selection.collapse_to(
15297                    movement::start_of_excerpt(
15298                        map,
15299                        selection.head(),
15300                        workspace::searchable::Direction::Prev,
15301                    ),
15302                    SelectionGoal::None,
15303                )
15304            });
15305        })
15306    }
15307
15308    pub fn move_to_start_of_next_excerpt(
15309        &mut self,
15310        _: &MoveToStartOfNextExcerpt,
15311        window: &mut Window,
15312        cx: &mut Context<Self>,
15313    ) {
15314        if matches!(self.mode, EditorMode::SingleLine) {
15315            cx.propagate();
15316            return;
15317        }
15318
15319        self.change_selections(Default::default(), window, cx, |s| {
15320            s.move_with(&mut |map, selection| {
15321                selection.collapse_to(
15322                    movement::start_of_excerpt(
15323                        map,
15324                        selection.head(),
15325                        workspace::searchable::Direction::Next,
15326                    ),
15327                    SelectionGoal::None,
15328                )
15329            });
15330        })
15331    }
15332
15333    pub fn move_to_end_of_excerpt(
15334        &mut self,
15335        _: &MoveToEndOfExcerpt,
15336        window: &mut Window,
15337        cx: &mut Context<Self>,
15338    ) {
15339        if matches!(self.mode, EditorMode::SingleLine) {
15340            cx.propagate();
15341            return;
15342        }
15343        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15344        self.change_selections(Default::default(), window, cx, |s| {
15345            s.move_with(&mut |map, selection| {
15346                selection.collapse_to(
15347                    movement::end_of_excerpt(
15348                        map,
15349                        selection.head(),
15350                        workspace::searchable::Direction::Next,
15351                    ),
15352                    SelectionGoal::None,
15353                )
15354            });
15355        })
15356    }
15357
15358    pub fn move_to_end_of_previous_excerpt(
15359        &mut self,
15360        _: &MoveToEndOfPreviousExcerpt,
15361        window: &mut Window,
15362        cx: &mut Context<Self>,
15363    ) {
15364        if matches!(self.mode, EditorMode::SingleLine) {
15365            cx.propagate();
15366            return;
15367        }
15368        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15369        self.change_selections(Default::default(), window, cx, |s| {
15370            s.move_with(&mut |map, selection| {
15371                selection.collapse_to(
15372                    movement::end_of_excerpt(
15373                        map,
15374                        selection.head(),
15375                        workspace::searchable::Direction::Prev,
15376                    ),
15377                    SelectionGoal::None,
15378                )
15379            });
15380        })
15381    }
15382
15383    pub fn select_to_start_of_excerpt(
15384        &mut self,
15385        _: &SelectToStartOfExcerpt,
15386        window: &mut Window,
15387        cx: &mut Context<Self>,
15388    ) {
15389        if matches!(self.mode, EditorMode::SingleLine) {
15390            cx.propagate();
15391            return;
15392        }
15393        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15394        self.change_selections(Default::default(), window, cx, |s| {
15395            s.move_heads_with(&mut |map, head, _| {
15396                (
15397                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15398                    SelectionGoal::None,
15399                )
15400            });
15401        })
15402    }
15403
15404    pub fn select_to_start_of_next_excerpt(
15405        &mut self,
15406        _: &SelectToStartOfNextExcerpt,
15407        window: &mut Window,
15408        cx: &mut Context<Self>,
15409    ) {
15410        if matches!(self.mode, EditorMode::SingleLine) {
15411            cx.propagate();
15412            return;
15413        }
15414        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15415        self.change_selections(Default::default(), window, cx, |s| {
15416            s.move_heads_with(&mut |map, head, _| {
15417                (
15418                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15419                    SelectionGoal::None,
15420                )
15421            });
15422        })
15423    }
15424
15425    pub fn select_to_end_of_excerpt(
15426        &mut self,
15427        _: &SelectToEndOfExcerpt,
15428        window: &mut Window,
15429        cx: &mut Context<Self>,
15430    ) {
15431        if matches!(self.mode, EditorMode::SingleLine) {
15432            cx.propagate();
15433            return;
15434        }
15435        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15436        self.change_selections(Default::default(), window, cx, |s| {
15437            s.move_heads_with(&mut |map, head, _| {
15438                (
15439                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15440                    SelectionGoal::None,
15441                )
15442            });
15443        })
15444    }
15445
15446    pub fn select_to_end_of_previous_excerpt(
15447        &mut self,
15448        _: &SelectToEndOfPreviousExcerpt,
15449        window: &mut Window,
15450        cx: &mut Context<Self>,
15451    ) {
15452        if matches!(self.mode, EditorMode::SingleLine) {
15453            cx.propagate();
15454            return;
15455        }
15456        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15457        self.change_selections(Default::default(), window, cx, |s| {
15458            s.move_heads_with(&mut |map, head, _| {
15459                (
15460                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15461                    SelectionGoal::None,
15462                )
15463            });
15464        })
15465    }
15466
15467    pub fn move_to_beginning(
15468        &mut self,
15469        _: &MoveToBeginning,
15470        window: &mut Window,
15471        cx: &mut Context<Self>,
15472    ) {
15473        if matches!(self.mode, EditorMode::SingleLine) {
15474            cx.propagate();
15475            return;
15476        }
15477        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15478        self.change_selections(Default::default(), window, cx, |s| {
15479            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15480        });
15481    }
15482
15483    pub fn select_to_beginning(
15484        &mut self,
15485        _: &SelectToBeginning,
15486        window: &mut Window,
15487        cx: &mut Context<Self>,
15488    ) {
15489        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15490        selection.set_head(Point::zero(), SelectionGoal::None);
15491        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15492        self.change_selections(Default::default(), window, cx, |s| {
15493            s.select(vec![selection]);
15494        });
15495    }
15496
15497    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15498        if matches!(self.mode, EditorMode::SingleLine) {
15499            cx.propagate();
15500            return;
15501        }
15502        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15503        let cursor = self.buffer.read(cx).read(cx).len();
15504        self.change_selections(Default::default(), window, cx, |s| {
15505            s.select_ranges(vec![cursor..cursor])
15506        });
15507    }
15508
15509    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15510        self.nav_history = nav_history;
15511    }
15512
15513    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15514        self.nav_history.as_ref()
15515    }
15516
15517    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15518        self.push_to_nav_history(
15519            self.selections.newest_anchor().head(),
15520            None,
15521            false,
15522            true,
15523            cx,
15524        );
15525    }
15526
15527    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15528        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15529        let buffer = self.buffer.read(cx).read(cx);
15530        let cursor_position = cursor_anchor.to_point(&buffer);
15531        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15532        let scroll_top_row = scroll_anchor.top_row(&buffer);
15533        drop(buffer);
15534
15535        NavigationData {
15536            cursor_anchor,
15537            cursor_position,
15538            scroll_anchor,
15539            scroll_top_row,
15540        }
15541    }
15542
15543    fn navigation_entry(
15544        &self,
15545        cursor_anchor: Anchor,
15546        cx: &mut Context<Self>,
15547    ) -> Option<NavigationEntry> {
15548        let Some(history) = self.nav_history.clone() else {
15549            return None;
15550        };
15551        let data = self.navigation_data(cursor_anchor, cx);
15552        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15553    }
15554
15555    fn push_to_nav_history(
15556        &mut self,
15557        cursor_anchor: Anchor,
15558        new_position: Option<Point>,
15559        is_deactivate: bool,
15560        always: bool,
15561        cx: &mut Context<Self>,
15562    ) {
15563        let data = self.navigation_data(cursor_anchor, cx);
15564        if let Some(nav_history) = self.nav_history.as_mut() {
15565            if let Some(new_position) = new_position {
15566                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15567                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15568                    return;
15569                }
15570            }
15571
15572            let cursor_row = data.cursor_position.row;
15573            nav_history.push(Some(data), Some(cursor_row), cx);
15574            cx.emit(EditorEvent::PushedToNavHistory {
15575                anchor: cursor_anchor,
15576                is_deactivate,
15577            })
15578        }
15579    }
15580
15581    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15582        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15583        let buffer = self.buffer.read(cx).snapshot(cx);
15584        let mut selection = self
15585            .selections
15586            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15587        selection.set_head(buffer.len(), SelectionGoal::None);
15588        self.change_selections(Default::default(), window, cx, |s| {
15589            s.select(vec![selection]);
15590        });
15591    }
15592
15593    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15594        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15595        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15596            s.select_ranges([Anchor::min()..Anchor::max()]);
15597        });
15598    }
15599
15600    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15601        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15602        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15603        let mut selections = self.selections.all::<Point>(&display_map);
15604        let max_point = display_map.buffer_snapshot().max_point();
15605        for selection in &mut selections {
15606            let rows = selection.spanned_rows(true, &display_map);
15607            selection.start = Point::new(rows.start.0, 0);
15608            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15609            selection.reversed = false;
15610        }
15611        self.change_selections(Default::default(), window, cx, |s| {
15612            s.select(selections);
15613        });
15614    }
15615
15616    pub fn split_selection_into_lines(
15617        &mut self,
15618        action: &SplitSelectionIntoLines,
15619        window: &mut Window,
15620        cx: &mut Context<Self>,
15621    ) {
15622        let selections = self
15623            .selections
15624            .all::<Point>(&self.display_snapshot(cx))
15625            .into_iter()
15626            .map(|selection| selection.start..selection.end)
15627            .collect::<Vec<_>>();
15628        self.unfold_ranges(&selections, true, false, cx);
15629
15630        let mut new_selection_ranges = Vec::new();
15631        {
15632            let buffer = self.buffer.read(cx).read(cx);
15633            for selection in selections {
15634                for row in selection.start.row..selection.end.row {
15635                    let line_start = Point::new(row, 0);
15636                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15637
15638                    if action.keep_selections {
15639                        // Keep the selection range for each line
15640                        let selection_start = if row == selection.start.row {
15641                            selection.start
15642                        } else {
15643                            line_start
15644                        };
15645                        new_selection_ranges.push(selection_start..line_end);
15646                    } else {
15647                        // Collapse to cursor at end of line
15648                        new_selection_ranges.push(line_end..line_end);
15649                    }
15650                }
15651
15652                let is_multiline_selection = selection.start.row != selection.end.row;
15653                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15654                // so this action feels more ergonomic when paired with other selection operations
15655                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15656                if !should_skip_last {
15657                    if action.keep_selections {
15658                        if is_multiline_selection {
15659                            let line_start = Point::new(selection.end.row, 0);
15660                            new_selection_ranges.push(line_start..selection.end);
15661                        } else {
15662                            new_selection_ranges.push(selection.start..selection.end);
15663                        }
15664                    } else {
15665                        new_selection_ranges.push(selection.end..selection.end);
15666                    }
15667                }
15668            }
15669        }
15670        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15671            s.select_ranges(new_selection_ranges);
15672        });
15673    }
15674
15675    pub fn add_selection_above(
15676        &mut self,
15677        action: &AddSelectionAbove,
15678        window: &mut Window,
15679        cx: &mut Context<Self>,
15680    ) {
15681        self.add_selection(true, action.skip_soft_wrap, window, cx);
15682    }
15683
15684    pub fn add_selection_below(
15685        &mut self,
15686        action: &AddSelectionBelow,
15687        window: &mut Window,
15688        cx: &mut Context<Self>,
15689    ) {
15690        self.add_selection(false, action.skip_soft_wrap, window, cx);
15691    }
15692
15693    fn add_selection(
15694        &mut self,
15695        above: bool,
15696        skip_soft_wrap: bool,
15697        window: &mut Window,
15698        cx: &mut Context<Self>,
15699    ) {
15700        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15701
15702        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15703        let all_selections = self.selections.all::<Point>(&display_map);
15704        let text_layout_details = self.text_layout_details(window, cx);
15705
15706        let (mut columnar_selections, new_selections_to_columnarize) = {
15707            if let Some(state) = self.add_selections_state.as_ref() {
15708                let columnar_selection_ids: HashSet<_> = state
15709                    .groups
15710                    .iter()
15711                    .flat_map(|group| group.stack.iter())
15712                    .copied()
15713                    .collect();
15714
15715                all_selections
15716                    .into_iter()
15717                    .partition(|s| columnar_selection_ids.contains(&s.id))
15718            } else {
15719                (Vec::new(), all_selections)
15720            }
15721        };
15722
15723        let mut state = self
15724            .add_selections_state
15725            .take()
15726            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15727
15728        for selection in new_selections_to_columnarize {
15729            let range = selection.display_range(&display_map).sorted();
15730            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15731            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15732            let positions = start_x.min(end_x)..start_x.max(end_x);
15733            let mut stack = Vec::new();
15734            for row in range.start.row().0..=range.end.row().0 {
15735                if let Some(selection) = self.selections.build_columnar_selection(
15736                    &display_map,
15737                    DisplayRow(row),
15738                    &positions,
15739                    selection.reversed,
15740                    &text_layout_details,
15741                ) {
15742                    stack.push(selection.id);
15743                    columnar_selections.push(selection);
15744                }
15745            }
15746            if !stack.is_empty() {
15747                if above {
15748                    stack.reverse();
15749                }
15750                state.groups.push(AddSelectionsGroup { above, stack });
15751            }
15752        }
15753
15754        let mut final_selections = Vec::new();
15755        let end_row = if above {
15756            DisplayRow(0)
15757        } else {
15758            display_map.max_point().row()
15759        };
15760
15761        // When `skip_soft_wrap` is true, we use UTF-16 columns instead of pixel
15762        // positions to place new selections, so we need to keep track of the
15763        // column range of the oldest selection in each group, because
15764        // intermediate selections may have been clamped to shorter lines.
15765        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15766            let mut map = HashMap::default();
15767            for group in state.groups.iter() {
15768                if let Some(oldest_id) = group.stack.first() {
15769                    if let Some(oldest_selection) =
15770                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15771                    {
15772                        let snapshot = display_map.buffer_snapshot();
15773                        let start_col =
15774                            snapshot.point_to_point_utf16(oldest_selection.start).column;
15775                        let end_col = snapshot.point_to_point_utf16(oldest_selection.end).column;
15776                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15777                        for id in &group.stack {
15778                            map.insert(*id, goal_columns.clone());
15779                        }
15780                    }
15781                }
15782            }
15783            map
15784        } else {
15785            HashMap::default()
15786        };
15787
15788        let mut last_added_item_per_group = HashMap::default();
15789        for group in state.groups.iter_mut() {
15790            if let Some(last_id) = group.stack.last() {
15791                last_added_item_per_group.insert(*last_id, group);
15792            }
15793        }
15794
15795        for selection in columnar_selections {
15796            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15797                if above == group.above {
15798                    let range = selection.display_range(&display_map).sorted();
15799                    debug_assert_eq!(range.start.row(), range.end.row());
15800                    let row = range.start.row();
15801                    let positions =
15802                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15803                            Pixels::from(start)..Pixels::from(end)
15804                        } else {
15805                            let start_x =
15806                                display_map.x_for_display_point(range.start, &text_layout_details);
15807                            let end_x =
15808                                display_map.x_for_display_point(range.end, &text_layout_details);
15809                            start_x.min(end_x)..start_x.max(end_x)
15810                        };
15811
15812                    let maybe_new_selection = if skip_soft_wrap {
15813                        let goal_columns = goal_columns_by_selection_id
15814                            .remove(&selection.id)
15815                            .unwrap_or_else(|| {
15816                                let snapshot = display_map.buffer_snapshot();
15817                                let start_col =
15818                                    snapshot.point_to_point_utf16(selection.start).column;
15819                                let end_col = snapshot.point_to_point_utf16(selection.end).column;
15820                                start_col.min(end_col)..start_col.max(end_col)
15821                            });
15822                        self.selections.find_next_columnar_selection_by_buffer_row(
15823                            &display_map,
15824                            row,
15825                            end_row,
15826                            above,
15827                            &goal_columns,
15828                            selection.reversed,
15829                            &text_layout_details,
15830                        )
15831                    } else {
15832                        self.selections.find_next_columnar_selection_by_display_row(
15833                            &display_map,
15834                            row,
15835                            end_row,
15836                            above,
15837                            &positions,
15838                            selection.reversed,
15839                            &text_layout_details,
15840                        )
15841                    };
15842
15843                    if let Some(new_selection) = maybe_new_selection {
15844                        group.stack.push(new_selection.id);
15845                        if above {
15846                            final_selections.push(new_selection);
15847                            final_selections.push(selection);
15848                        } else {
15849                            final_selections.push(selection);
15850                            final_selections.push(new_selection);
15851                        }
15852                    } else {
15853                        final_selections.push(selection);
15854                    }
15855                } else {
15856                    group.stack.pop();
15857                }
15858            } else {
15859                final_selections.push(selection);
15860            }
15861        }
15862
15863        self.change_selections(Default::default(), window, cx, |s| {
15864            s.select(final_selections);
15865        });
15866
15867        let final_selection_ids: HashSet<_> = self
15868            .selections
15869            .all::<Point>(&display_map)
15870            .iter()
15871            .map(|s| s.id)
15872            .collect();
15873        state.groups.retain_mut(|group| {
15874            // selections might get merged above so we remove invalid items from stacks
15875            group.stack.retain(|id| final_selection_ids.contains(id));
15876
15877            // single selection in stack can be treated as initial state
15878            group.stack.len() > 1
15879        });
15880
15881        if !state.groups.is_empty() {
15882            self.add_selections_state = Some(state);
15883        }
15884    }
15885
15886    pub fn insert_snippet_at_selections(
15887        &mut self,
15888        action: &InsertSnippet,
15889        window: &mut Window,
15890        cx: &mut Context<Self>,
15891    ) {
15892        self.try_insert_snippet_at_selections(action, window, cx)
15893            .log_err();
15894    }
15895
15896    fn try_insert_snippet_at_selections(
15897        &mut self,
15898        action: &InsertSnippet,
15899        window: &mut Window,
15900        cx: &mut Context<Self>,
15901    ) -> Result<()> {
15902        let insertion_ranges = self
15903            .selections
15904            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15905            .into_iter()
15906            .map(|selection| selection.range())
15907            .collect_vec();
15908
15909        let snippet = if let Some(snippet_body) = &action.snippet {
15910            if action.language.is_none() && action.name.is_none() {
15911                Snippet::parse(snippet_body)?
15912            } else {
15913                bail!("`snippet` is mutually exclusive with `language` and `name`")
15914            }
15915        } else if let Some(name) = &action.name {
15916            let project = self.project().context("no project")?;
15917            let snippet_store = project.read(cx).snippets().read(cx);
15918            let snippet = snippet_store
15919                .snippets_for(action.language.clone(), cx)
15920                .into_iter()
15921                .find(|snippet| snippet.name == *name)
15922                .context("snippet not found")?;
15923            Snippet::parse(&snippet.body)?
15924        } else {
15925            // todo(andrew): open modal to select snippet
15926            bail!("`name` or `snippet` is required")
15927        };
15928
15929        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15930    }
15931
15932    fn select_match_ranges(
15933        &mut self,
15934        range: Range<MultiBufferOffset>,
15935        reversed: bool,
15936        replace_newest: bool,
15937        auto_scroll: Option<Autoscroll>,
15938        window: &mut Window,
15939        cx: &mut Context<Editor>,
15940    ) {
15941        self.unfold_ranges(
15942            std::slice::from_ref(&range),
15943            false,
15944            auto_scroll.is_some(),
15945            cx,
15946        );
15947        let effects = if let Some(scroll) = auto_scroll {
15948            SelectionEffects::scroll(scroll)
15949        } else {
15950            SelectionEffects::no_scroll()
15951        };
15952        self.change_selections(effects, window, cx, |s| {
15953            if replace_newest {
15954                s.delete(s.newest_anchor().id);
15955            }
15956            if reversed {
15957                s.insert_range(range.end..range.start);
15958            } else {
15959                s.insert_range(range);
15960            }
15961        });
15962    }
15963
15964    pub fn select_next_match_internal(
15965        &mut self,
15966        display_map: &DisplaySnapshot,
15967        replace_newest: bool,
15968        autoscroll: Option<Autoscroll>,
15969        window: &mut Window,
15970        cx: &mut Context<Self>,
15971    ) -> Result<()> {
15972        let buffer = display_map.buffer_snapshot();
15973        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15974        if let Some(mut select_next_state) = self.select_next_state.take() {
15975            let query = &select_next_state.query;
15976            if !select_next_state.done {
15977                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15978                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15979                let mut next_selected_range = None;
15980
15981                let bytes_after_last_selection =
15982                    buffer.bytes_in_range(last_selection.end..buffer.len());
15983                let bytes_before_first_selection =
15984                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15985                let query_matches = query
15986                    .stream_find_iter(bytes_after_last_selection)
15987                    .map(|result| (last_selection.end, result))
15988                    .chain(
15989                        query
15990                            .stream_find_iter(bytes_before_first_selection)
15991                            .map(|result| (MultiBufferOffset(0), result)),
15992                    );
15993
15994                for (start_offset, query_match) in query_matches {
15995                    let query_match = query_match.unwrap(); // can only fail due to I/O
15996                    let offset_range =
15997                        start_offset + query_match.start()..start_offset + query_match.end();
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                        let idx = selections
16004                            .partition_point(|selection| selection.end <= offset_range.start);
16005                        let overlaps = selections
16006                            .get(idx)
16007                            .map_or(false, |selection| selection.start < offset_range.end);
16008
16009                        if !overlaps {
16010                            next_selected_range = Some(offset_range);
16011                            break;
16012                        }
16013                    }
16014                }
16015
16016                if let Some(next_selected_range) = next_selected_range {
16017                    self.select_match_ranges(
16018                        next_selected_range,
16019                        last_selection.reversed,
16020                        replace_newest,
16021                        autoscroll,
16022                        window,
16023                        cx,
16024                    );
16025                } else {
16026                    select_next_state.done = true;
16027                }
16028            }
16029
16030            self.select_next_state = Some(select_next_state);
16031        } else {
16032            let mut only_carets = true;
16033            let mut same_text_selected = true;
16034            let mut selected_text = None;
16035
16036            let mut selections_iter = selections.iter().peekable();
16037            while let Some(selection) = selections_iter.next() {
16038                if selection.start != selection.end {
16039                    only_carets = false;
16040                }
16041
16042                if same_text_selected {
16043                    if selected_text.is_none() {
16044                        selected_text =
16045                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16046                    }
16047
16048                    if let Some(next_selection) = selections_iter.peek() {
16049                        if next_selection.len() == selection.len() {
16050                            let next_selected_text = buffer
16051                                .text_for_range(next_selection.range())
16052                                .collect::<String>();
16053                            if Some(next_selected_text) != selected_text {
16054                                same_text_selected = false;
16055                                selected_text = None;
16056                            }
16057                        } else {
16058                            same_text_selected = false;
16059                            selected_text = None;
16060                        }
16061                    }
16062                }
16063            }
16064
16065            if only_carets {
16066                for selection in &mut selections {
16067                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16068                    selection.start = word_range.start;
16069                    selection.end = word_range.end;
16070                    selection.goal = SelectionGoal::None;
16071                    selection.reversed = false;
16072                    self.select_match_ranges(
16073                        selection.start..selection.end,
16074                        selection.reversed,
16075                        replace_newest,
16076                        autoscroll,
16077                        window,
16078                        cx,
16079                    );
16080                }
16081
16082                if selections.len() == 1 {
16083                    let selection = selections
16084                        .last()
16085                        .expect("ensured that there's only one selection");
16086                    let query = buffer
16087                        .text_for_range(selection.start..selection.end)
16088                        .collect::<String>();
16089                    let is_empty = query.is_empty();
16090                    let select_state = SelectNextState {
16091                        query: self.build_query(&[query], cx)?,
16092                        wordwise: true,
16093                        done: is_empty,
16094                    };
16095                    self.select_next_state = Some(select_state);
16096                } else {
16097                    self.select_next_state = None;
16098                }
16099            } else if let Some(selected_text) = selected_text {
16100                self.select_next_state = Some(SelectNextState {
16101                    query: self.build_query(&[selected_text], cx)?,
16102                    wordwise: false,
16103                    done: false,
16104                });
16105                self.select_next_match_internal(
16106                    display_map,
16107                    replace_newest,
16108                    autoscroll,
16109                    window,
16110                    cx,
16111                )?;
16112            }
16113        }
16114        Ok(())
16115    }
16116
16117    pub fn select_all_matches(
16118        &mut self,
16119        _action: &SelectAllMatches,
16120        window: &mut Window,
16121        cx: &mut Context<Self>,
16122    ) -> Result<()> {
16123        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16124
16125        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16126
16127        self.select_next_match_internal(&display_map, false, None, window, cx)?;
16128        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
16129        else {
16130            return Ok(());
16131        };
16132
16133        let mut new_selections = Vec::new();
16134        let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map);
16135        let reversed = initial_selection.reversed;
16136        let buffer = display_map.buffer_snapshot();
16137        let query_matches = select_next_state
16138            .query
16139            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
16140
16141        for query_match in query_matches.into_iter() {
16142            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
16143            let offset_range = if reversed {
16144                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
16145            } else {
16146                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
16147            };
16148
16149            let is_partial_word_match = select_next_state.wordwise
16150                && (buffer.is_inside_word(offset_range.start, None)
16151                    || buffer.is_inside_word(offset_range.end, None));
16152
16153            let is_initial_selection = MultiBufferOffset(query_match.start())
16154                == initial_selection.start
16155                && MultiBufferOffset(query_match.end()) == initial_selection.end;
16156
16157            if !is_partial_word_match && !is_initial_selection {
16158                new_selections.push(offset_range);
16159            }
16160        }
16161
16162        // Ensure that the initial range is the last selection, as
16163        // `MutableSelectionsCollection::select_ranges` makes the last selection
16164        // the newest selection, which the editor then relies on as the primary
16165        // cursor for scroll targeting. Without this, the last match would then
16166        // be automatically focused when the user started editing the selected
16167        // matches.
16168        let initial_directed_range = if reversed {
16169            initial_selection.end..initial_selection.start
16170        } else {
16171            initial_selection.start..initial_selection.end
16172        };
16173        new_selections.push(initial_directed_range);
16174
16175        select_next_state.done = true;
16176        self.unfold_ranges(&new_selections, false, false, cx);
16177        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16178            selections.select_ranges(new_selections)
16179        });
16180
16181        Ok(())
16182    }
16183
16184    pub fn select_next(
16185        &mut self,
16186        action: &SelectNext,
16187        window: &mut Window,
16188        cx: &mut Context<Self>,
16189    ) -> Result<()> {
16190        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16191        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16192        self.select_next_match_internal(
16193            &display_map,
16194            action.replace_newest,
16195            Some(Autoscroll::newest()),
16196            window,
16197            cx,
16198        )
16199    }
16200
16201    pub fn select_previous(
16202        &mut self,
16203        action: &SelectPrevious,
16204        window: &mut Window,
16205        cx: &mut Context<Self>,
16206    ) -> Result<()> {
16207        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16208        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16209        let buffer = display_map.buffer_snapshot();
16210        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16211        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16212            let query = &select_prev_state.query;
16213            if !select_prev_state.done {
16214                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16215                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16216                let mut next_selected_range = None;
16217                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16218                let bytes_before_last_selection =
16219                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16220                let bytes_after_first_selection =
16221                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16222                let query_matches = query
16223                    .stream_find_iter(bytes_before_last_selection)
16224                    .map(|result| (last_selection.start, result))
16225                    .chain(
16226                        query
16227                            .stream_find_iter(bytes_after_first_selection)
16228                            .map(|result| (buffer.len(), result)),
16229                    );
16230                for (end_offset, query_match) in query_matches {
16231                    let query_match = query_match.unwrap(); // can only fail due to I/O
16232                    let offset_range =
16233                        end_offset - query_match.end()..end_offset - query_match.start();
16234
16235                    if !select_prev_state.wordwise
16236                        || (!buffer.is_inside_word(offset_range.start, None)
16237                            && !buffer.is_inside_word(offset_range.end, None))
16238                    {
16239                        next_selected_range = Some(offset_range);
16240                        break;
16241                    }
16242                }
16243
16244                if let Some(next_selected_range) = next_selected_range {
16245                    self.select_match_ranges(
16246                        next_selected_range,
16247                        last_selection.reversed,
16248                        action.replace_newest,
16249                        Some(Autoscroll::newest()),
16250                        window,
16251                        cx,
16252                    );
16253                } else {
16254                    select_prev_state.done = true;
16255                }
16256            }
16257
16258            self.select_prev_state = Some(select_prev_state);
16259        } else {
16260            let mut only_carets = true;
16261            let mut same_text_selected = true;
16262            let mut selected_text = None;
16263
16264            let mut selections_iter = selections.iter().peekable();
16265            while let Some(selection) = selections_iter.next() {
16266                if selection.start != selection.end {
16267                    only_carets = false;
16268                }
16269
16270                if same_text_selected {
16271                    if selected_text.is_none() {
16272                        selected_text =
16273                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16274                    }
16275
16276                    if let Some(next_selection) = selections_iter.peek() {
16277                        if next_selection.len() == selection.len() {
16278                            let next_selected_text = buffer
16279                                .text_for_range(next_selection.range())
16280                                .collect::<String>();
16281                            if Some(next_selected_text) != selected_text {
16282                                same_text_selected = false;
16283                                selected_text = None;
16284                            }
16285                        } else {
16286                            same_text_selected = false;
16287                            selected_text = None;
16288                        }
16289                    }
16290                }
16291            }
16292
16293            if only_carets {
16294                for selection in &mut selections {
16295                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16296                    selection.start = word_range.start;
16297                    selection.end = word_range.end;
16298                    selection.goal = SelectionGoal::None;
16299                    selection.reversed = false;
16300                    self.select_match_ranges(
16301                        selection.start..selection.end,
16302                        selection.reversed,
16303                        action.replace_newest,
16304                        Some(Autoscroll::newest()),
16305                        window,
16306                        cx,
16307                    );
16308                }
16309                if selections.len() == 1 {
16310                    let selection = selections
16311                        .last()
16312                        .expect("ensured that there's only one selection");
16313                    let query = buffer
16314                        .text_for_range(selection.start..selection.end)
16315                        .collect::<String>();
16316                    let is_empty = query.is_empty();
16317                    let select_state = SelectNextState {
16318                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16319                        wordwise: true,
16320                        done: is_empty,
16321                    };
16322                    self.select_prev_state = Some(select_state);
16323                } else {
16324                    self.select_prev_state = None;
16325                }
16326            } else if let Some(selected_text) = selected_text {
16327                self.select_prev_state = Some(SelectNextState {
16328                    query: self
16329                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16330                    wordwise: false,
16331                    done: false,
16332                });
16333                self.select_previous(action, window, cx)?;
16334            }
16335        }
16336        Ok(())
16337    }
16338
16339    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16340    /// setting the case sensitivity based on the global
16341    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16342    /// editor's settings.
16343    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16344    where
16345        I: IntoIterator<Item = P>,
16346        P: AsRef<[u8]>,
16347    {
16348        let case_sensitive = self
16349            .select_next_is_case_sensitive
16350            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16351
16352        let mut builder = AhoCorasickBuilder::new();
16353        builder.ascii_case_insensitive(!case_sensitive);
16354        builder.build(patterns)
16355    }
16356
16357    pub fn find_next_match(
16358        &mut self,
16359        _: &FindNextMatch,
16360        window: &mut Window,
16361        cx: &mut Context<Self>,
16362    ) -> Result<()> {
16363        let selections = self.selections.disjoint_anchors_arc();
16364        match selections.first() {
16365            Some(first) if selections.len() >= 2 => {
16366                self.change_selections(Default::default(), window, cx, |s| {
16367                    s.select_ranges([first.range()]);
16368                });
16369            }
16370            _ => self.select_next(
16371                &SelectNext {
16372                    replace_newest: true,
16373                },
16374                window,
16375                cx,
16376            )?,
16377        }
16378        Ok(())
16379    }
16380
16381    pub fn find_previous_match(
16382        &mut self,
16383        _: &FindPreviousMatch,
16384        window: &mut Window,
16385        cx: &mut Context<Self>,
16386    ) -> Result<()> {
16387        let selections = self.selections.disjoint_anchors_arc();
16388        match selections.last() {
16389            Some(last) if selections.len() >= 2 => {
16390                self.change_selections(Default::default(), window, cx, |s| {
16391                    s.select_ranges([last.range()]);
16392                });
16393            }
16394            _ => self.select_previous(
16395                &SelectPrevious {
16396                    replace_newest: true,
16397                },
16398                window,
16399                cx,
16400            )?,
16401        }
16402        Ok(())
16403    }
16404
16405    pub fn toggle_comments(
16406        &mut self,
16407        action: &ToggleComments,
16408        window: &mut Window,
16409        cx: &mut Context<Self>,
16410    ) {
16411        if self.read_only(cx) {
16412            return;
16413        }
16414        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16415        let text_layout_details = &self.text_layout_details(window, cx);
16416        self.transact(window, cx, |this, window, cx| {
16417            let mut selections = this
16418                .selections
16419                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16420            let mut edits = Vec::new();
16421            let mut selection_edit_ranges = Vec::new();
16422            let mut last_toggled_row = None;
16423            let snapshot = this.buffer.read(cx).read(cx);
16424            let empty_str: Arc<str> = Arc::default();
16425            let mut suffixes_inserted = Vec::new();
16426            let ignore_indent = action.ignore_indent;
16427
16428            fn comment_prefix_range(
16429                snapshot: &MultiBufferSnapshot,
16430                row: MultiBufferRow,
16431                comment_prefix: &str,
16432                comment_prefix_whitespace: &str,
16433                ignore_indent: bool,
16434            ) -> Range<Point> {
16435                let indent_size = if ignore_indent {
16436                    0
16437                } else {
16438                    snapshot.indent_size_for_line(row).len
16439                };
16440
16441                let start = Point::new(row.0, indent_size);
16442
16443                let mut line_bytes = snapshot
16444                    .bytes_in_range(start..snapshot.max_point())
16445                    .flatten()
16446                    .copied();
16447
16448                // If this line currently begins with the line comment prefix, then record
16449                // the range containing the prefix.
16450                if line_bytes
16451                    .by_ref()
16452                    .take(comment_prefix.len())
16453                    .eq(comment_prefix.bytes())
16454                {
16455                    // Include any whitespace that matches the comment prefix.
16456                    let matching_whitespace_len = line_bytes
16457                        .zip(comment_prefix_whitespace.bytes())
16458                        .take_while(|(a, b)| a == b)
16459                        .count() as u32;
16460                    let end = Point::new(
16461                        start.row,
16462                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16463                    );
16464                    start..end
16465                } else {
16466                    start..start
16467                }
16468            }
16469
16470            fn comment_suffix_range(
16471                snapshot: &MultiBufferSnapshot,
16472                row: MultiBufferRow,
16473                comment_suffix: &str,
16474                comment_suffix_has_leading_space: bool,
16475            ) -> Range<Point> {
16476                let end = Point::new(row.0, snapshot.line_len(row));
16477                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16478
16479                let mut line_end_bytes = snapshot
16480                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16481                    .flatten()
16482                    .copied();
16483
16484                let leading_space_len = if suffix_start_column > 0
16485                    && line_end_bytes.next() == Some(b' ')
16486                    && comment_suffix_has_leading_space
16487                {
16488                    1
16489                } else {
16490                    0
16491                };
16492
16493                // If this line currently begins with the line comment prefix, then record
16494                // the range containing the prefix.
16495                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16496                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16497                    start..end
16498                } else {
16499                    end..end
16500                }
16501            }
16502
16503            // TODO: Handle selections that cross excerpts
16504            for selection in &mut selections {
16505                let start_column = snapshot
16506                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16507                    .len;
16508                let language = if let Some(language) =
16509                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16510                {
16511                    language
16512                } else {
16513                    continue;
16514                };
16515
16516                selection_edit_ranges.clear();
16517
16518                // If multiple selections contain a given row, avoid processing that
16519                // row more than once.
16520                let mut start_row = MultiBufferRow(selection.start.row);
16521                if last_toggled_row == Some(start_row) {
16522                    start_row = start_row.next_row();
16523                }
16524                let end_row =
16525                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16526                        MultiBufferRow(selection.end.row - 1)
16527                    } else {
16528                        MultiBufferRow(selection.end.row)
16529                    };
16530                last_toggled_row = Some(end_row);
16531
16532                if start_row > end_row {
16533                    continue;
16534                }
16535
16536                // If the language has line comments, toggle those.
16537                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16538
16539                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16540                if ignore_indent {
16541                    full_comment_prefixes = full_comment_prefixes
16542                        .into_iter()
16543                        .map(|s| Arc::from(s.trim_end()))
16544                        .collect();
16545                }
16546
16547                if !full_comment_prefixes.is_empty() {
16548                    let first_prefix = full_comment_prefixes
16549                        .first()
16550                        .expect("prefixes is non-empty");
16551                    let prefix_trimmed_lengths = full_comment_prefixes
16552                        .iter()
16553                        .map(|p| p.trim_end_matches(' ').len())
16554                        .collect::<SmallVec<[usize; 4]>>();
16555
16556                    let mut all_selection_lines_are_comments = true;
16557
16558                    for row in start_row.0..=end_row.0 {
16559                        let row = MultiBufferRow(row);
16560                        if start_row < end_row && snapshot.is_line_blank(row) {
16561                            continue;
16562                        }
16563
16564                        let prefix_range = full_comment_prefixes
16565                            .iter()
16566                            .zip(prefix_trimmed_lengths.iter().copied())
16567                            .map(|(prefix, trimmed_prefix_len)| {
16568                                comment_prefix_range(
16569                                    snapshot.deref(),
16570                                    row,
16571                                    &prefix[..trimmed_prefix_len],
16572                                    &prefix[trimmed_prefix_len..],
16573                                    ignore_indent,
16574                                )
16575                            })
16576                            .max_by_key(|range| range.end.column - range.start.column)
16577                            .expect("prefixes is non-empty");
16578
16579                        if prefix_range.is_empty() {
16580                            all_selection_lines_are_comments = false;
16581                        }
16582
16583                        selection_edit_ranges.push(prefix_range);
16584                    }
16585
16586                    if all_selection_lines_are_comments {
16587                        edits.extend(
16588                            selection_edit_ranges
16589                                .iter()
16590                                .cloned()
16591                                .map(|range| (range, empty_str.clone())),
16592                        );
16593                    } else {
16594                        let min_column = selection_edit_ranges
16595                            .iter()
16596                            .map(|range| range.start.column)
16597                            .min()
16598                            .unwrap_or(0);
16599                        edits.extend(selection_edit_ranges.iter().map(|range| {
16600                            let position = Point::new(range.start.row, min_column);
16601                            (position..position, first_prefix.clone())
16602                        }));
16603                    }
16604                } else if let Some(BlockCommentConfig {
16605                    start: full_comment_prefix,
16606                    end: comment_suffix,
16607                    ..
16608                }) = language.block_comment()
16609                {
16610                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16611                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16612                    let prefix_range = comment_prefix_range(
16613                        snapshot.deref(),
16614                        start_row,
16615                        comment_prefix,
16616                        comment_prefix_whitespace,
16617                        ignore_indent,
16618                    );
16619                    let suffix_range = comment_suffix_range(
16620                        snapshot.deref(),
16621                        end_row,
16622                        comment_suffix.trim_start_matches(' '),
16623                        comment_suffix.starts_with(' '),
16624                    );
16625
16626                    if prefix_range.is_empty() || suffix_range.is_empty() {
16627                        edits.push((
16628                            prefix_range.start..prefix_range.start,
16629                            full_comment_prefix.clone(),
16630                        ));
16631                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16632                        suffixes_inserted.push((end_row, comment_suffix.len()));
16633                    } else {
16634                        edits.push((prefix_range, empty_str.clone()));
16635                        edits.push((suffix_range, empty_str.clone()));
16636                    }
16637                } else {
16638                    continue;
16639                }
16640            }
16641
16642            drop(snapshot);
16643            this.buffer.update(cx, |buffer, cx| {
16644                buffer.edit(edits, None, cx);
16645            });
16646
16647            // Adjust selections so that they end before any comment suffixes that
16648            // were inserted.
16649            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16650            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16651            let snapshot = this.buffer.read(cx).read(cx);
16652            for selection in &mut selections {
16653                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16654                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16655                        Ordering::Less => {
16656                            suffixes_inserted.next();
16657                            continue;
16658                        }
16659                        Ordering::Greater => break,
16660                        Ordering::Equal => {
16661                            if selection.end.column == snapshot.line_len(row) {
16662                                if selection.is_empty() {
16663                                    selection.start.column -= suffix_len as u32;
16664                                }
16665                                selection.end.column -= suffix_len as u32;
16666                            }
16667                            break;
16668                        }
16669                    }
16670                }
16671            }
16672
16673            drop(snapshot);
16674            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16675
16676            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16677            let selections_on_single_row = selections.windows(2).all(|selections| {
16678                selections[0].start.row == selections[1].start.row
16679                    && selections[0].end.row == selections[1].end.row
16680                    && selections[0].start.row == selections[0].end.row
16681            });
16682            let selections_selecting = selections
16683                .iter()
16684                .any(|selection| selection.start != selection.end);
16685            let advance_downwards = action.advance_downwards
16686                && selections_on_single_row
16687                && !selections_selecting
16688                && !matches!(this.mode, EditorMode::SingleLine);
16689
16690            if advance_downwards {
16691                let snapshot = this.buffer.read(cx).snapshot(cx);
16692
16693                this.change_selections(Default::default(), window, cx, |s| {
16694                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16695                        let mut point = display_point.to_point(display_snapshot);
16696                        point.row += 1;
16697                        point = snapshot.clip_point(point, Bias::Left);
16698                        let display_point = point.to_display_point(display_snapshot);
16699                        let goal = SelectionGoal::HorizontalPosition(
16700                            display_snapshot
16701                                .x_for_display_point(display_point, text_layout_details)
16702                                .into(),
16703                        );
16704                        (display_point, goal)
16705                    })
16706                });
16707            }
16708        });
16709    }
16710
16711    pub fn select_enclosing_symbol(
16712        &mut self,
16713        _: &SelectEnclosingSymbol,
16714        window: &mut Window,
16715        cx: &mut Context<Self>,
16716    ) {
16717        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16718
16719        let buffer = self.buffer.read(cx).snapshot(cx);
16720        let old_selections = self
16721            .selections
16722            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16723            .into_boxed_slice();
16724
16725        fn update_selection(
16726            selection: &Selection<MultiBufferOffset>,
16727            buffer_snap: &MultiBufferSnapshot,
16728        ) -> Option<Selection<MultiBufferOffset>> {
16729            let cursor = selection.head();
16730            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16731            for symbol in symbols.iter().rev() {
16732                let start = symbol.range.start.to_offset(buffer_snap);
16733                let end = symbol.range.end.to_offset(buffer_snap);
16734                let new_range = start..end;
16735                if start < selection.start || end > selection.end {
16736                    return Some(Selection {
16737                        id: selection.id,
16738                        start: new_range.start,
16739                        end: new_range.end,
16740                        goal: SelectionGoal::None,
16741                        reversed: selection.reversed,
16742                    });
16743                }
16744            }
16745            None
16746        }
16747
16748        let mut selected_larger_symbol = false;
16749        let new_selections = old_selections
16750            .iter()
16751            .map(|selection| match update_selection(selection, &buffer) {
16752                Some(new_selection) => {
16753                    if new_selection.range() != selection.range() {
16754                        selected_larger_symbol = true;
16755                    }
16756                    new_selection
16757                }
16758                None => selection.clone(),
16759            })
16760            .collect::<Vec<_>>();
16761
16762        if selected_larger_symbol {
16763            self.change_selections(Default::default(), window, cx, |s| {
16764                s.select(new_selections);
16765            });
16766        }
16767    }
16768
16769    pub fn select_larger_syntax_node(
16770        &mut self,
16771        _: &SelectLargerSyntaxNode,
16772        window: &mut Window,
16773        cx: &mut Context<Self>,
16774    ) {
16775        let Some(visible_row_count) = self.visible_row_count() else {
16776            return;
16777        };
16778        let old_selections: Box<[_]> = self
16779            .selections
16780            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16781            .into();
16782        if old_selections.is_empty() {
16783            return;
16784        }
16785
16786        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16787
16788        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16789        let buffer = self.buffer.read(cx).snapshot(cx);
16790
16791        let mut selected_larger_node = false;
16792        let mut new_selections = old_selections
16793            .iter()
16794            .map(|selection| {
16795                let old_range = selection.start..selection.end;
16796
16797                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16798                    // manually select word at selection
16799                    if ["string_content", "inline"].contains(&node.kind()) {
16800                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16801                        // ignore if word is already selected
16802                        if !word_range.is_empty() && old_range != word_range {
16803                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16804                            // only select word if start and end point belongs to same word
16805                            if word_range == last_word_range {
16806                                selected_larger_node = true;
16807                                return Selection {
16808                                    id: selection.id,
16809                                    start: word_range.start,
16810                                    end: word_range.end,
16811                                    goal: SelectionGoal::None,
16812                                    reversed: selection.reversed,
16813                                };
16814                            }
16815                        }
16816                    }
16817                }
16818
16819                let mut new_range = old_range.clone();
16820                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16821                    new_range = range;
16822                    if !node.is_named() {
16823                        continue;
16824                    }
16825                    if !display_map.intersects_fold(new_range.start)
16826                        && !display_map.intersects_fold(new_range.end)
16827                    {
16828                        break;
16829                    }
16830                }
16831
16832                selected_larger_node |= new_range != old_range;
16833                Selection {
16834                    id: selection.id,
16835                    start: new_range.start,
16836                    end: new_range.end,
16837                    goal: SelectionGoal::None,
16838                    reversed: selection.reversed,
16839                }
16840            })
16841            .collect::<Vec<_>>();
16842
16843        if !selected_larger_node {
16844            return; // don't put this call in the history
16845        }
16846
16847        // scroll based on transformation done to the last selection created by the user
16848        let (last_old, last_new) = old_selections
16849            .last()
16850            .zip(new_selections.last().cloned())
16851            .expect("old_selections isn't empty");
16852
16853        let is_selection_reversed = if new_selections.len() == 1 {
16854            let should_be_reversed = last_old.start != last_new.start;
16855            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16856            should_be_reversed
16857        } else {
16858            last_new.reversed
16859        };
16860
16861        if selected_larger_node {
16862            self.select_syntax_node_history.disable_clearing = true;
16863            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16864                s.select(new_selections.clone());
16865            });
16866            self.select_syntax_node_history.disable_clearing = false;
16867        }
16868
16869        let start_row = last_new.start.to_display_point(&display_map).row().0;
16870        let end_row = last_new.end.to_display_point(&display_map).row().0;
16871        let selection_height = end_row - start_row + 1;
16872        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16873
16874        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16875        let scroll_behavior = if fits_on_the_screen {
16876            self.request_autoscroll(Autoscroll::fit(), cx);
16877            SelectSyntaxNodeScrollBehavior::FitSelection
16878        } else if is_selection_reversed {
16879            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16880            SelectSyntaxNodeScrollBehavior::CursorTop
16881        } else {
16882            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16883            SelectSyntaxNodeScrollBehavior::CursorBottom
16884        };
16885
16886        let old_selections: Box<[Selection<Anchor>]> = old_selections
16887            .iter()
16888            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16889            .collect();
16890        self.select_syntax_node_history.push((
16891            old_selections,
16892            scroll_behavior,
16893            is_selection_reversed,
16894        ));
16895    }
16896
16897    pub fn select_smaller_syntax_node(
16898        &mut self,
16899        _: &SelectSmallerSyntaxNode,
16900        window: &mut Window,
16901        cx: &mut Context<Self>,
16902    ) {
16903        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16904
16905        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16906            self.select_syntax_node_history.pop()
16907        {
16908            if let Some(selection) = selections.last_mut() {
16909                selection.reversed = is_selection_reversed;
16910            }
16911
16912            let snapshot = self.buffer.read(cx).snapshot(cx);
16913            let selections: Vec<Selection<MultiBufferOffset>> = selections
16914                .iter()
16915                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16916                .collect();
16917
16918            self.select_syntax_node_history.disable_clearing = true;
16919            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16920                s.select(selections);
16921            });
16922            self.select_syntax_node_history.disable_clearing = false;
16923
16924            match scroll_behavior {
16925                SelectSyntaxNodeScrollBehavior::CursorTop => {
16926                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16927                }
16928                SelectSyntaxNodeScrollBehavior::FitSelection => {
16929                    self.request_autoscroll(Autoscroll::fit(), cx);
16930                }
16931                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16932                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16933                }
16934            }
16935        }
16936    }
16937
16938    pub fn unwrap_syntax_node(
16939        &mut self,
16940        _: &UnwrapSyntaxNode,
16941        window: &mut Window,
16942        cx: &mut Context<Self>,
16943    ) {
16944        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16945
16946        let buffer = self.buffer.read(cx).snapshot(cx);
16947        let selections = self
16948            .selections
16949            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16950            .into_iter()
16951            // subtracting the offset requires sorting
16952            .sorted_by_key(|i| i.start);
16953
16954        let full_edits = selections
16955            .into_iter()
16956            .filter_map(|selection| {
16957                let child = if selection.is_empty()
16958                    && let Some((_, ancestor_range)) =
16959                        buffer.syntax_ancestor(selection.start..selection.end)
16960                {
16961                    ancestor_range
16962                } else {
16963                    selection.range()
16964                };
16965
16966                let mut parent = child.clone();
16967                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16968                    parent = ancestor_range;
16969                    if parent.start < child.start || parent.end > child.end {
16970                        break;
16971                    }
16972                }
16973
16974                if parent == child {
16975                    return None;
16976                }
16977                let text = buffer.text_for_range(child).collect::<String>();
16978                Some((selection.id, parent, text))
16979            })
16980            .collect::<Vec<_>>();
16981        if full_edits.is_empty() {
16982            return;
16983        }
16984
16985        self.transact(window, cx, |this, window, cx| {
16986            this.buffer.update(cx, |buffer, cx| {
16987                buffer.edit(
16988                    full_edits
16989                        .iter()
16990                        .map(|(_, p, t)| (p.clone(), t.clone()))
16991                        .collect::<Vec<_>>(),
16992                    None,
16993                    cx,
16994                );
16995            });
16996            this.change_selections(Default::default(), window, cx, |s| {
16997                let mut offset = 0;
16998                let mut selections = vec![];
16999                for (id, parent, text) in full_edits {
17000                    let start = parent.start - offset;
17001                    offset += (parent.end - parent.start) - text.len();
17002                    selections.push(Selection {
17003                        id,
17004                        start,
17005                        end: start + text.len(),
17006                        reversed: false,
17007                        goal: Default::default(),
17008                    });
17009                }
17010                s.select(selections);
17011            });
17012        });
17013    }
17014
17015    pub fn select_next_syntax_node(
17016        &mut self,
17017        _: &SelectNextSyntaxNode,
17018        window: &mut Window,
17019        cx: &mut Context<Self>,
17020    ) {
17021        let old_selections: Box<[_]> = self
17022            .selections
17023            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17024            .into();
17025        if old_selections.is_empty() {
17026            return;
17027        }
17028
17029        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17030
17031        let buffer = self.buffer.read(cx).snapshot(cx);
17032        let mut selected_sibling = false;
17033
17034        let new_selections = old_selections
17035            .iter()
17036            .map(|selection| {
17037                let old_range = selection.start..selection.end;
17038
17039                let old_range =
17040                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17041                let excerpt = buffer.excerpt_containing(old_range.clone());
17042
17043                if let Some(mut excerpt) = excerpt
17044                    && let Some(node) = excerpt
17045                        .buffer()
17046                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
17047                {
17048                    let new_range = excerpt.map_range_from_buffer(
17049                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17050                    );
17051                    selected_sibling = true;
17052                    Selection {
17053                        id: selection.id,
17054                        start: new_range.start,
17055                        end: new_range.end,
17056                        goal: SelectionGoal::None,
17057                        reversed: selection.reversed,
17058                    }
17059                } else {
17060                    selection.clone()
17061                }
17062            })
17063            .collect::<Vec<_>>();
17064
17065        if selected_sibling {
17066            self.change_selections(
17067                SelectionEffects::scroll(Autoscroll::fit()),
17068                window,
17069                cx,
17070                |s| {
17071                    s.select(new_selections);
17072                },
17073            );
17074        }
17075    }
17076
17077    pub fn select_prev_syntax_node(
17078        &mut self,
17079        _: &SelectPreviousSyntaxNode,
17080        window: &mut Window,
17081        cx: &mut Context<Self>,
17082    ) {
17083        let old_selections: Box<[_]> = self
17084            .selections
17085            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17086            .into();
17087        if old_selections.is_empty() {
17088            return;
17089        }
17090
17091        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17092
17093        let buffer = self.buffer.read(cx).snapshot(cx);
17094        let mut selected_sibling = false;
17095
17096        let new_selections = old_selections
17097            .iter()
17098            .map(|selection| {
17099                let old_range = selection.start..selection.end;
17100                let old_range =
17101                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17102                let excerpt = buffer.excerpt_containing(old_range.clone());
17103
17104                if let Some(mut excerpt) = excerpt
17105                    && let Some(node) = excerpt
17106                        .buffer()
17107                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
17108                {
17109                    let new_range = excerpt.map_range_from_buffer(
17110                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17111                    );
17112                    selected_sibling = true;
17113                    Selection {
17114                        id: selection.id,
17115                        start: new_range.start,
17116                        end: new_range.end,
17117                        goal: SelectionGoal::None,
17118                        reversed: selection.reversed,
17119                    }
17120                } else {
17121                    selection.clone()
17122                }
17123            })
17124            .collect::<Vec<_>>();
17125
17126        if selected_sibling {
17127            self.change_selections(
17128                SelectionEffects::scroll(Autoscroll::fit()),
17129                window,
17130                cx,
17131                |s| {
17132                    s.select(new_selections);
17133                },
17134            );
17135        }
17136    }
17137
17138    pub fn move_to_start_of_larger_syntax_node(
17139        &mut self,
17140        _: &MoveToStartOfLargerSyntaxNode,
17141        window: &mut Window,
17142        cx: &mut Context<Self>,
17143    ) {
17144        self.move_cursors_to_syntax_nodes(window, cx, false);
17145    }
17146
17147    pub fn move_to_end_of_larger_syntax_node(
17148        &mut self,
17149        _: &MoveToEndOfLargerSyntaxNode,
17150        window: &mut Window,
17151        cx: &mut Context<Self>,
17152    ) {
17153        self.move_cursors_to_syntax_nodes(window, cx, true);
17154    }
17155
17156    fn find_syntax_node_boundary(
17157        &self,
17158        selection_pos: MultiBufferOffset,
17159        move_to_end: bool,
17160        display_map: &DisplaySnapshot,
17161        buffer: &MultiBufferSnapshot,
17162    ) -> MultiBufferOffset {
17163        let old_range = selection_pos..selection_pos;
17164        let mut new_pos = selection_pos;
17165        let mut search_range = old_range;
17166        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17167            search_range = range.clone();
17168            if !node.is_named()
17169                || display_map.intersects_fold(range.start)
17170                || display_map.intersects_fold(range.end)
17171                // If cursor is already at the end of the syntax node, continue searching
17172                || (move_to_end && range.end == selection_pos)
17173                // If cursor is already at the start of the syntax node, continue searching
17174                || (!move_to_end && range.start == selection_pos)
17175            {
17176                continue;
17177            }
17178
17179            // If we found a string_content node, find the largest parent that is still string_content
17180            // Enables us to skip to the end of strings without taking multiple steps inside the string
17181            let (_, final_range) = if node.kind() == "string_content" {
17182                let mut current_node = node;
17183                let mut current_range = range;
17184                while let Some((parent, parent_range)) =
17185                    buffer.syntax_ancestor(current_range.clone())
17186                {
17187                    if parent.kind() == "string_content" {
17188                        current_node = parent;
17189                        current_range = parent_range;
17190                    } else {
17191                        break;
17192                    }
17193                }
17194
17195                (current_node, current_range)
17196            } else {
17197                (node, range)
17198            };
17199
17200            new_pos = if move_to_end {
17201                final_range.end
17202            } else {
17203                final_range.start
17204            };
17205
17206            break;
17207        }
17208
17209        new_pos
17210    }
17211
17212    fn move_cursors_to_syntax_nodes(
17213        &mut self,
17214        window: &mut Window,
17215        cx: &mut Context<Self>,
17216        move_to_end: bool,
17217    ) -> bool {
17218        let old_selections: Box<[_]> = self
17219            .selections
17220            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17221            .into();
17222        if old_selections.is_empty() {
17223            return false;
17224        }
17225
17226        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17227
17228        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17229        let buffer = self.buffer.read(cx).snapshot(cx);
17230
17231        let mut any_cursor_moved = false;
17232        let new_selections = old_selections
17233            .iter()
17234            .map(|selection| {
17235                if !selection.is_empty() {
17236                    return selection.clone();
17237                }
17238
17239                let selection_pos = selection.head();
17240                let new_pos = self.find_syntax_node_boundary(
17241                    selection_pos,
17242                    move_to_end,
17243                    &display_map,
17244                    &buffer,
17245                );
17246
17247                any_cursor_moved |= new_pos != selection_pos;
17248
17249                Selection {
17250                    id: selection.id,
17251                    start: new_pos,
17252                    end: new_pos,
17253                    goal: SelectionGoal::None,
17254                    reversed: false,
17255                }
17256            })
17257            .collect::<Vec<_>>();
17258
17259        self.change_selections(Default::default(), window, cx, |s| {
17260            s.select(new_selections);
17261        });
17262        self.request_autoscroll(Autoscroll::newest(), cx);
17263
17264        any_cursor_moved
17265    }
17266
17267    pub fn select_to_start_of_larger_syntax_node(
17268        &mut self,
17269        _: &SelectToStartOfLargerSyntaxNode,
17270        window: &mut Window,
17271        cx: &mut Context<Self>,
17272    ) {
17273        self.select_to_syntax_nodes(window, cx, false);
17274    }
17275
17276    pub fn select_to_end_of_larger_syntax_node(
17277        &mut self,
17278        _: &SelectToEndOfLargerSyntaxNode,
17279        window: &mut Window,
17280        cx: &mut Context<Self>,
17281    ) {
17282        self.select_to_syntax_nodes(window, cx, true);
17283    }
17284
17285    fn select_to_syntax_nodes(
17286        &mut self,
17287        window: &mut Window,
17288        cx: &mut Context<Self>,
17289        move_to_end: bool,
17290    ) {
17291        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17292
17293        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17294        let buffer = self.buffer.read(cx).snapshot(cx);
17295        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17296
17297        let new_selections = old_selections
17298            .iter()
17299            .map(|selection| {
17300                let new_pos = self.find_syntax_node_boundary(
17301                    selection.head(),
17302                    move_to_end,
17303                    &display_map,
17304                    &buffer,
17305                );
17306
17307                let mut new_selection = selection.clone();
17308                new_selection.set_head(new_pos, SelectionGoal::None);
17309                new_selection
17310            })
17311            .collect::<Vec<_>>();
17312
17313        self.change_selections(Default::default(), window, cx, |s| {
17314            s.select(new_selections);
17315        });
17316    }
17317
17318    pub fn move_to_enclosing_bracket(
17319        &mut self,
17320        _: &MoveToEnclosingBracket,
17321        window: &mut Window,
17322        cx: &mut Context<Self>,
17323    ) {
17324        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17325        self.change_selections(Default::default(), window, cx, |s| {
17326            s.move_offsets_with(&mut |snapshot, selection| {
17327                let Some(enclosing_bracket_ranges) =
17328                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17329                else {
17330                    return;
17331                };
17332
17333                let mut best_length = usize::MAX;
17334                let mut best_inside = false;
17335                let mut best_in_bracket_range = false;
17336                let mut best_destination = None;
17337                for (open, close) in enclosing_bracket_ranges {
17338                    let close = close.to_inclusive();
17339                    let length = *close.end() - open.start;
17340                    let inside = selection.start >= open.end && selection.end <= *close.start();
17341                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17342                        || close.contains(&selection.head());
17343
17344                    // If best is next to a bracket and current isn't, skip
17345                    if !in_bracket_range && best_in_bracket_range {
17346                        continue;
17347                    }
17348
17349                    // Prefer smaller lengths unless best is inside and current isn't
17350                    if length > best_length && (best_inside || !inside) {
17351                        continue;
17352                    }
17353
17354                    best_length = length;
17355                    best_inside = inside;
17356                    best_in_bracket_range = in_bracket_range;
17357                    best_destination = Some(
17358                        if close.contains(&selection.start) && close.contains(&selection.end) {
17359                            if inside { open.end } else { open.start }
17360                        } else if inside {
17361                            *close.start()
17362                        } else {
17363                            *close.end()
17364                        },
17365                    );
17366                }
17367
17368                if let Some(destination) = best_destination {
17369                    selection.collapse_to(destination, SelectionGoal::None);
17370                }
17371            })
17372        });
17373    }
17374
17375    pub fn undo_selection(
17376        &mut self,
17377        _: &UndoSelection,
17378        window: &mut Window,
17379        cx: &mut Context<Self>,
17380    ) {
17381        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17382        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17383            self.selection_history.mode = SelectionHistoryMode::Undoing;
17384            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17385                this.end_selection(window, cx);
17386                this.change_selections(
17387                    SelectionEffects::scroll(Autoscroll::newest()),
17388                    window,
17389                    cx,
17390                    |s| s.select_anchors(entry.selections.to_vec()),
17391                );
17392            });
17393            self.selection_history.mode = SelectionHistoryMode::Normal;
17394
17395            self.select_next_state = entry.select_next_state;
17396            self.select_prev_state = entry.select_prev_state;
17397            self.add_selections_state = entry.add_selections_state;
17398        }
17399    }
17400
17401    pub fn redo_selection(
17402        &mut self,
17403        _: &RedoSelection,
17404        window: &mut Window,
17405        cx: &mut Context<Self>,
17406    ) {
17407        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17408        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17409            self.selection_history.mode = SelectionHistoryMode::Redoing;
17410            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17411                this.end_selection(window, cx);
17412                this.change_selections(
17413                    SelectionEffects::scroll(Autoscroll::newest()),
17414                    window,
17415                    cx,
17416                    |s| s.select_anchors(entry.selections.to_vec()),
17417                );
17418            });
17419            self.selection_history.mode = SelectionHistoryMode::Normal;
17420
17421            self.select_next_state = entry.select_next_state;
17422            self.select_prev_state = entry.select_prev_state;
17423            self.add_selections_state = entry.add_selections_state;
17424        }
17425    }
17426
17427    pub fn expand_excerpts(
17428        &mut self,
17429        action: &ExpandExcerpts,
17430        _: &mut Window,
17431        cx: &mut Context<Self>,
17432    ) {
17433        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17434    }
17435
17436    pub fn expand_excerpts_down(
17437        &mut self,
17438        action: &ExpandExcerptsDown,
17439        _: &mut Window,
17440        cx: &mut Context<Self>,
17441    ) {
17442        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17443    }
17444
17445    pub fn expand_excerpts_up(
17446        &mut self,
17447        action: &ExpandExcerptsUp,
17448        _: &mut Window,
17449        cx: &mut Context<Self>,
17450    ) {
17451        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17452    }
17453
17454    pub fn expand_excerpts_for_direction(
17455        &mut self,
17456        lines: u32,
17457        direction: ExpandExcerptDirection,
17458        cx: &mut Context<Self>,
17459    ) {
17460        let selections = self.selections.disjoint_anchors_arc();
17461
17462        let lines = if lines == 0 {
17463            EditorSettings::get_global(cx).expand_excerpt_lines
17464        } else {
17465            lines
17466        };
17467
17468        let snapshot = self.buffer.read(cx).snapshot(cx);
17469        let excerpt_ids = selections
17470            .iter()
17471            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17472            .unique()
17473            .sorted()
17474            .collect::<Vec<_>>();
17475
17476        if self.delegate_expand_excerpts {
17477            cx.emit(EditorEvent::ExpandExcerptsRequested {
17478                excerpt_ids,
17479                lines,
17480                direction,
17481            });
17482            return;
17483        }
17484
17485        self.buffer.update(cx, |buffer, cx| {
17486            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17487        })
17488    }
17489
17490    pub fn expand_excerpt(
17491        &mut self,
17492        excerpt: ExcerptId,
17493        direction: ExpandExcerptDirection,
17494        window: &mut Window,
17495        cx: &mut Context<Self>,
17496    ) {
17497        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17498
17499        if self.delegate_expand_excerpts {
17500            cx.emit(EditorEvent::ExpandExcerptsRequested {
17501                excerpt_ids: vec![excerpt],
17502                lines: lines_to_expand,
17503                direction,
17504            });
17505            return;
17506        }
17507
17508        let current_scroll_position = self.scroll_position(cx);
17509        let mut scroll = None;
17510
17511        if direction == ExpandExcerptDirection::Down {
17512            let multi_buffer = self.buffer.read(cx);
17513            let snapshot = multi_buffer.snapshot(cx);
17514            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17515                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17516                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17517            {
17518                let buffer_snapshot = buffer.read(cx).snapshot();
17519                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17520                let last_row = buffer_snapshot.max_point().row;
17521                let lines_below = last_row.saturating_sub(excerpt_end_row);
17522                if lines_below >= lines_to_expand {
17523                    scroll = Some(
17524                        current_scroll_position
17525                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17526                    );
17527                }
17528            }
17529        }
17530        if direction == ExpandExcerptDirection::Up
17531            && self
17532                .buffer
17533                .read(cx)
17534                .snapshot(cx)
17535                .excerpt_before(excerpt)
17536                .is_none()
17537        {
17538            scroll = Some(current_scroll_position);
17539        }
17540
17541        self.buffer.update(cx, |buffer, cx| {
17542            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17543        });
17544
17545        if let Some(new_scroll_position) = scroll {
17546            self.set_scroll_position(new_scroll_position, window, cx);
17547        }
17548    }
17549
17550    pub fn go_to_singleton_buffer_point(
17551        &mut self,
17552        point: Point,
17553        window: &mut Window,
17554        cx: &mut Context<Self>,
17555    ) {
17556        self.go_to_singleton_buffer_range(point..point, window, cx);
17557    }
17558
17559    pub fn go_to_singleton_buffer_range(
17560        &mut self,
17561        range: Range<Point>,
17562        window: &mut Window,
17563        cx: &mut Context<Self>,
17564    ) {
17565        let multibuffer = self.buffer().read(cx);
17566        let Some(buffer) = multibuffer.as_singleton() else {
17567            return;
17568        };
17569        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17570            return;
17571        };
17572        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17573            return;
17574        };
17575        self.change_selections(
17576            SelectionEffects::default().nav_history(true),
17577            window,
17578            cx,
17579            |s| s.select_anchor_ranges([start..end]),
17580        );
17581    }
17582
17583    pub fn go_to_diagnostic(
17584        &mut self,
17585        action: &GoToDiagnostic,
17586        window: &mut Window,
17587        cx: &mut Context<Self>,
17588    ) {
17589        if !self.diagnostics_enabled() {
17590            return;
17591        }
17592        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17593        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17594    }
17595
17596    pub fn go_to_prev_diagnostic(
17597        &mut self,
17598        action: &GoToPreviousDiagnostic,
17599        window: &mut Window,
17600        cx: &mut Context<Self>,
17601    ) {
17602        if !self.diagnostics_enabled() {
17603            return;
17604        }
17605        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17606        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17607    }
17608
17609    pub fn go_to_diagnostic_impl(
17610        &mut self,
17611        direction: Direction,
17612        severity: GoToDiagnosticSeverityFilter,
17613        window: &mut Window,
17614        cx: &mut Context<Self>,
17615    ) {
17616        let buffer = self.buffer.read(cx).snapshot(cx);
17617        let selection = self
17618            .selections
17619            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17620
17621        let mut active_group_id = None;
17622        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17623            && active_group.active_range.start.to_offset(&buffer) == selection.start
17624        {
17625            active_group_id = Some(active_group.group_id);
17626        }
17627
17628        fn filtered<'a>(
17629            severity: GoToDiagnosticSeverityFilter,
17630            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17631        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17632            diagnostics
17633                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17634                .filter(|entry| entry.range.start != entry.range.end)
17635                .filter(|entry| !entry.diagnostic.is_unnecessary)
17636        }
17637
17638        let before = filtered(
17639            severity,
17640            buffer
17641                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17642                .filter(|entry| entry.range.start <= selection.start),
17643        );
17644        let after = filtered(
17645            severity,
17646            buffer
17647                .diagnostics_in_range(selection.start..buffer.len())
17648                .filter(|entry| entry.range.start >= selection.start),
17649        );
17650
17651        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17652        if direction == Direction::Prev {
17653            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17654            {
17655                for diagnostic in prev_diagnostics.into_iter().rev() {
17656                    if diagnostic.range.start != selection.start
17657                        || active_group_id
17658                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17659                    {
17660                        found = Some(diagnostic);
17661                        break 'outer;
17662                    }
17663                }
17664            }
17665        } else {
17666            for diagnostic in after.chain(before) {
17667                if diagnostic.range.start != selection.start
17668                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17669                {
17670                    found = Some(diagnostic);
17671                    break;
17672                }
17673            }
17674        }
17675        let Some(next_diagnostic) = found else {
17676            return;
17677        };
17678
17679        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17680        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17681            return;
17682        };
17683        let snapshot = self.snapshot(window, cx);
17684        if snapshot.intersects_fold(next_diagnostic.range.start) {
17685            self.unfold_ranges(
17686                std::slice::from_ref(&next_diagnostic.range),
17687                true,
17688                false,
17689                cx,
17690            );
17691        }
17692        self.change_selections(Default::default(), window, cx, |s| {
17693            s.select_ranges(vec![
17694                next_diagnostic.range.start..next_diagnostic.range.start,
17695            ])
17696        });
17697        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17698        self.refresh_edit_prediction(false, true, window, cx);
17699    }
17700
17701    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17702        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17703        let snapshot = self.snapshot(window, cx);
17704        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17705        self.go_to_hunk_before_or_after_position(
17706            &snapshot,
17707            selection.head(),
17708            Direction::Next,
17709            true,
17710            window,
17711            cx,
17712        );
17713    }
17714
17715    pub fn go_to_hunk_before_or_after_position(
17716        &mut self,
17717        snapshot: &EditorSnapshot,
17718        position: Point,
17719        direction: Direction,
17720        wrap_around: bool,
17721        window: &mut Window,
17722        cx: &mut Context<Editor>,
17723    ) {
17724        let row = if direction == Direction::Next {
17725            self.hunk_after_position(snapshot, position, wrap_around)
17726                .map(|hunk| hunk.row_range.start)
17727        } else {
17728            self.hunk_before_position(snapshot, position, wrap_around)
17729        };
17730
17731        if let Some(row) = row {
17732            let destination = Point::new(row.0, 0);
17733            let autoscroll = Autoscroll::center();
17734
17735            self.unfold_ranges(&[destination..destination], false, false, cx);
17736            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17737                s.select_ranges([destination..destination]);
17738            });
17739        }
17740    }
17741
17742    fn hunk_after_position(
17743        &mut self,
17744        snapshot: &EditorSnapshot,
17745        position: Point,
17746        wrap_around: bool,
17747    ) -> Option<MultiBufferDiffHunk> {
17748        let result = snapshot
17749            .buffer_snapshot()
17750            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17751            .find(|hunk| hunk.row_range.start.0 > position.row);
17752
17753        if wrap_around {
17754            result.or_else(|| {
17755                snapshot
17756                    .buffer_snapshot()
17757                    .diff_hunks_in_range(Point::zero()..position)
17758                    .find(|hunk| hunk.row_range.end.0 < position.row)
17759            })
17760        } else {
17761            result
17762        }
17763    }
17764
17765    fn go_to_prev_hunk(
17766        &mut self,
17767        _: &GoToPreviousHunk,
17768        window: &mut Window,
17769        cx: &mut Context<Self>,
17770    ) {
17771        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17772        let snapshot = self.snapshot(window, cx);
17773        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17774        self.go_to_hunk_before_or_after_position(
17775            &snapshot,
17776            selection.head(),
17777            Direction::Prev,
17778            true,
17779            window,
17780            cx,
17781        );
17782    }
17783
17784    fn hunk_before_position(
17785        &mut self,
17786        snapshot: &EditorSnapshot,
17787        position: Point,
17788        wrap_around: bool,
17789    ) -> Option<MultiBufferRow> {
17790        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17791
17792        if wrap_around {
17793            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17794        } else {
17795            result
17796        }
17797    }
17798
17799    fn go_to_next_change(
17800        &mut self,
17801        _: &GoToNextChange,
17802        window: &mut Window,
17803        cx: &mut Context<Self>,
17804    ) {
17805        if let Some(selections) = self
17806            .change_list
17807            .next_change(1, Direction::Next)
17808            .map(|s| s.to_vec())
17809        {
17810            self.change_selections(Default::default(), window, cx, |s| {
17811                let map = s.display_snapshot();
17812                s.select_display_ranges(selections.iter().map(|a| {
17813                    let point = a.to_display_point(&map);
17814                    point..point
17815                }))
17816            })
17817        }
17818    }
17819
17820    fn go_to_previous_change(
17821        &mut self,
17822        _: &GoToPreviousChange,
17823        window: &mut Window,
17824        cx: &mut Context<Self>,
17825    ) {
17826        if let Some(selections) = self
17827            .change_list
17828            .next_change(1, Direction::Prev)
17829            .map(|s| s.to_vec())
17830        {
17831            self.change_selections(Default::default(), window, cx, |s| {
17832                let map = s.display_snapshot();
17833                s.select_display_ranges(selections.iter().map(|a| {
17834                    let point = a.to_display_point(&map);
17835                    point..point
17836                }))
17837            })
17838        }
17839    }
17840
17841    pub fn go_to_next_document_highlight(
17842        &mut self,
17843        _: &GoToNextDocumentHighlight,
17844        window: &mut Window,
17845        cx: &mut Context<Self>,
17846    ) {
17847        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17848    }
17849
17850    pub fn go_to_prev_document_highlight(
17851        &mut self,
17852        _: &GoToPreviousDocumentHighlight,
17853        window: &mut Window,
17854        cx: &mut Context<Self>,
17855    ) {
17856        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17857    }
17858
17859    pub fn go_to_document_highlight_before_or_after_position(
17860        &mut self,
17861        direction: Direction,
17862        window: &mut Window,
17863        cx: &mut Context<Editor>,
17864    ) {
17865        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17866        let snapshot = self.snapshot(window, cx);
17867        let buffer = &snapshot.buffer_snapshot();
17868        let position = self
17869            .selections
17870            .newest::<Point>(&snapshot.display_snapshot)
17871            .head();
17872        let anchor_position = buffer.anchor_after(position);
17873
17874        // Get all document highlights (both read and write)
17875        let mut all_highlights = Vec::new();
17876
17877        if let Some((_, read_highlights)) = self
17878            .background_highlights
17879            .get(&HighlightKey::DocumentHighlightRead)
17880        {
17881            all_highlights.extend(read_highlights.iter());
17882        }
17883
17884        if let Some((_, write_highlights)) = self
17885            .background_highlights
17886            .get(&HighlightKey::DocumentHighlightWrite)
17887        {
17888            all_highlights.extend(write_highlights.iter());
17889        }
17890
17891        if all_highlights.is_empty() {
17892            return;
17893        }
17894
17895        // Sort highlights by position
17896        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17897
17898        let target_highlight = match direction {
17899            Direction::Next => {
17900                // Find the first highlight after the current position
17901                all_highlights
17902                    .iter()
17903                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17904            }
17905            Direction::Prev => {
17906                // Find the last highlight before the current position
17907                all_highlights
17908                    .iter()
17909                    .rev()
17910                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17911            }
17912        };
17913
17914        if let Some(highlight) = target_highlight {
17915            let destination = highlight.start.to_point(buffer);
17916            let autoscroll = Autoscroll::center();
17917
17918            self.unfold_ranges(&[destination..destination], false, false, cx);
17919            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17920                s.select_ranges([destination..destination]);
17921            });
17922        }
17923    }
17924
17925    fn go_to_line<T: 'static>(
17926        &mut self,
17927        position: Anchor,
17928        highlight_color: Option<Hsla>,
17929        window: &mut Window,
17930        cx: &mut Context<Self>,
17931    ) {
17932        let snapshot = self.snapshot(window, cx).display_snapshot;
17933        let position = position.to_point(&snapshot.buffer_snapshot());
17934        let start = snapshot
17935            .buffer_snapshot()
17936            .clip_point(Point::new(position.row, 0), Bias::Left);
17937        let end = start + Point::new(1, 0);
17938        let start = snapshot.buffer_snapshot().anchor_before(start);
17939        let end = snapshot.buffer_snapshot().anchor_before(end);
17940
17941        self.highlight_rows::<T>(
17942            start..end,
17943            highlight_color
17944                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17945            Default::default(),
17946            cx,
17947        );
17948
17949        if self.buffer.read(cx).is_singleton() {
17950            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17951        }
17952    }
17953
17954    pub fn go_to_definition(
17955        &mut self,
17956        _: &GoToDefinition,
17957        window: &mut Window,
17958        cx: &mut Context<Self>,
17959    ) -> Task<Result<Navigated>> {
17960        let definition =
17961            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17962        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17963        cx.spawn_in(window, async move |editor, cx| {
17964            if definition.await? == Navigated::Yes {
17965                return Ok(Navigated::Yes);
17966            }
17967            match fallback_strategy {
17968                GoToDefinitionFallback::None => Ok(Navigated::No),
17969                GoToDefinitionFallback::FindAllReferences => {
17970                    match editor.update_in(cx, |editor, window, cx| {
17971                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17972                    })? {
17973                        Some(references) => references.await,
17974                        None => Ok(Navigated::No),
17975                    }
17976                }
17977            }
17978        })
17979    }
17980
17981    pub fn go_to_declaration(
17982        &mut self,
17983        _: &GoToDeclaration,
17984        window: &mut Window,
17985        cx: &mut Context<Self>,
17986    ) -> Task<Result<Navigated>> {
17987        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17988    }
17989
17990    pub fn go_to_declaration_split(
17991        &mut self,
17992        _: &GoToDeclaration,
17993        window: &mut Window,
17994        cx: &mut Context<Self>,
17995    ) -> Task<Result<Navigated>> {
17996        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17997    }
17998
17999    pub fn go_to_implementation(
18000        &mut self,
18001        _: &GoToImplementation,
18002        window: &mut Window,
18003        cx: &mut Context<Self>,
18004    ) -> Task<Result<Navigated>> {
18005        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
18006    }
18007
18008    pub fn go_to_implementation_split(
18009        &mut self,
18010        _: &GoToImplementationSplit,
18011        window: &mut Window,
18012        cx: &mut Context<Self>,
18013    ) -> Task<Result<Navigated>> {
18014        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
18015    }
18016
18017    pub fn go_to_type_definition(
18018        &mut self,
18019        _: &GoToTypeDefinition,
18020        window: &mut Window,
18021        cx: &mut Context<Self>,
18022    ) -> Task<Result<Navigated>> {
18023        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
18024    }
18025
18026    pub fn go_to_definition_split(
18027        &mut self,
18028        _: &GoToDefinitionSplit,
18029        window: &mut Window,
18030        cx: &mut Context<Self>,
18031    ) -> Task<Result<Navigated>> {
18032        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
18033    }
18034
18035    pub fn go_to_type_definition_split(
18036        &mut self,
18037        _: &GoToTypeDefinitionSplit,
18038        window: &mut Window,
18039        cx: &mut Context<Self>,
18040    ) -> Task<Result<Navigated>> {
18041        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18042    }
18043
18044    fn go_to_definition_of_kind(
18045        &mut self,
18046        kind: GotoDefinitionKind,
18047        split: bool,
18048        window: &mut Window,
18049        cx: &mut Context<Self>,
18050    ) -> Task<Result<Navigated>> {
18051        let Some(provider) = self.semantics_provider.clone() else {
18052            return Task::ready(Ok(Navigated::No));
18053        };
18054        let head = self
18055            .selections
18056            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18057            .head();
18058        let buffer = self.buffer.read(cx);
18059        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18060            return Task::ready(Ok(Navigated::No));
18061        };
18062        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18063            return Task::ready(Ok(Navigated::No));
18064        };
18065
18066        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18067
18068        cx.spawn_in(window, async move |editor, cx| {
18069            let Some(definitions) = definitions.await? else {
18070                return Ok(Navigated::No);
18071            };
18072            let navigated = editor
18073                .update_in(cx, |editor, window, cx| {
18074                    editor.navigate_to_hover_links(
18075                        Some(kind),
18076                        definitions
18077                            .into_iter()
18078                            .filter(|location| {
18079                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18080                            })
18081                            .map(HoverLink::Text)
18082                            .collect::<Vec<_>>(),
18083                        nav_entry,
18084                        split,
18085                        window,
18086                        cx,
18087                    )
18088                })?
18089                .await?;
18090            anyhow::Ok(navigated)
18091        })
18092    }
18093
18094    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18095        let selection = self.selections.newest_anchor();
18096        let head = selection.head();
18097        let tail = selection.tail();
18098
18099        let Some((buffer, start_position)) =
18100            self.buffer.read(cx).text_anchor_for_position(head, cx)
18101        else {
18102            return;
18103        };
18104
18105        let end_position = if head != tail {
18106            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18107                return;
18108            };
18109            Some(pos)
18110        } else {
18111            None
18112        };
18113
18114        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18115            let url = if let Some(end_pos) = end_position {
18116                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18117            } else {
18118                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18119            };
18120
18121            if let Some(url) = url {
18122                cx.update(|window, cx| {
18123                    if parse_zed_link(&url, cx).is_some() {
18124                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18125                    } else {
18126                        cx.open_url(&url);
18127                    }
18128                })?;
18129            }
18130
18131            anyhow::Ok(())
18132        });
18133
18134        url_finder.detach();
18135    }
18136
18137    pub fn open_selected_filename(
18138        &mut self,
18139        _: &OpenSelectedFilename,
18140        window: &mut Window,
18141        cx: &mut Context<Self>,
18142    ) {
18143        let Some(workspace) = self.workspace() else {
18144            return;
18145        };
18146
18147        let position = self.selections.newest_anchor().head();
18148
18149        let Some((buffer, buffer_position)) =
18150            self.buffer.read(cx).text_anchor_for_position(position, cx)
18151        else {
18152            return;
18153        };
18154
18155        let project = self.project.clone();
18156
18157        cx.spawn_in(window, async move |_, cx| {
18158            let result = find_file(&buffer, project, buffer_position, cx).await;
18159
18160            if let Some((_, path)) = result {
18161                workspace
18162                    .update_in(cx, |workspace, window, cx| {
18163                        workspace.open_resolved_path(path, window, cx)
18164                    })?
18165                    .await?;
18166            }
18167            anyhow::Ok(())
18168        })
18169        .detach();
18170    }
18171
18172    pub(crate) fn navigate_to_hover_links(
18173        &mut self,
18174        kind: Option<GotoDefinitionKind>,
18175        definitions: Vec<HoverLink>,
18176        origin: Option<NavigationEntry>,
18177        split: bool,
18178        window: &mut Window,
18179        cx: &mut Context<Editor>,
18180    ) -> Task<Result<Navigated>> {
18181        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18182        let mut first_url_or_file = None;
18183        let definitions: Vec<_> = definitions
18184            .into_iter()
18185            .filter_map(|def| match def {
18186                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18187                HoverLink::InlayHint(lsp_location, server_id) => {
18188                    let computation =
18189                        self.compute_target_location(lsp_location, server_id, window, cx);
18190                    Some(cx.background_spawn(computation))
18191                }
18192                HoverLink::Url(url) => {
18193                    first_url_or_file = Some(Either::Left(url));
18194                    None
18195                }
18196                HoverLink::File(path) => {
18197                    first_url_or_file = Some(Either::Right(path));
18198                    None
18199                }
18200            })
18201            .collect();
18202
18203        let workspace = self.workspace();
18204
18205        let excerpt_context_lines = multi_buffer::excerpt_context_lines(cx);
18206        cx.spawn_in(window, async move |editor, cx| {
18207            let locations: Vec<Location> = future::join_all(definitions)
18208                .await
18209                .into_iter()
18210                .filter_map(|location| location.transpose())
18211                .collect::<Result<_>>()
18212                .context("location tasks")?;
18213            let mut locations = cx.update(|_, cx| {
18214                locations
18215                    .into_iter()
18216                    .map(|location| {
18217                        let buffer = location.buffer.read(cx);
18218                        (location.buffer, location.range.to_point(buffer))
18219                    })
18220                    .into_group_map()
18221            })?;
18222            let mut num_locations = 0;
18223            for ranges in locations.values_mut() {
18224                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18225                ranges.dedup();
18226                let fits_in_one_excerpt = ranges
18227                    .iter()
18228                    .tuple_windows()
18229                    .all(|(a, b)| b.start.row - a.end.row <= 2 * excerpt_context_lines);
18230                num_locations += if fits_in_one_excerpt { 1 } else { ranges.len() };
18231            }
18232
18233            if num_locations > 1 {
18234                let tab_kind = match kind {
18235                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18236                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18237                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18238                    Some(GotoDefinitionKind::Type) => "Types",
18239                };
18240                let title = editor
18241                    .update_in(cx, |_, _, cx| {
18242                        let target = locations
18243                            .iter()
18244                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18245                            .map(|(buffer, location)| {
18246                                buffer
18247                                    .read(cx)
18248                                    .text_for_range(location.clone())
18249                                    .collect::<String>()
18250                            })
18251                            .filter(|text| !text.contains('\n'))
18252                            .unique()
18253                            .take(3)
18254                            .join(", ");
18255                        if target.is_empty() {
18256                            tab_kind.to_owned()
18257                        } else {
18258                            format!("{tab_kind} for {target}")
18259                        }
18260                    })
18261                    .context("buffer title")?;
18262
18263                let Some(workspace) = workspace else {
18264                    return Ok(Navigated::No);
18265                };
18266
18267                let opened = workspace
18268                    .update_in(cx, |workspace, window, cx| {
18269                        let allow_preview = PreviewTabsSettings::get_global(cx)
18270                            .enable_preview_multibuffer_from_code_navigation;
18271                        if let Some((target_editor, target_pane)) =
18272                            Self::open_locations_in_multibuffer(
18273                                workspace,
18274                                locations,
18275                                title,
18276                                split,
18277                                allow_preview,
18278                                MultibufferSelectionMode::First,
18279                                window,
18280                                cx,
18281                            )
18282                        {
18283                            // We create our own nav history instead of using
18284                            // `target_editor.nav_history` because `nav_history`
18285                            // seems to be populated asynchronously when an item
18286                            // is added to a pane
18287                            let mut nav_history = target_pane
18288                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18289                            target_editor.update(cx, |editor, cx| {
18290                                let nav_data = editor
18291                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18292                                let target =
18293                                    Some(nav_history.navigation_entry(Some(
18294                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18295                                    )));
18296                                nav_history.push_tag(origin, target);
18297                            })
18298                        }
18299                    })
18300                    .is_ok();
18301
18302                anyhow::Ok(Navigated::from_bool(opened))
18303            } else if num_locations == 0 {
18304                // If there is one url or file, open it directly
18305                match first_url_or_file {
18306                    Some(Either::Left(url)) => {
18307                        cx.update(|window, cx| {
18308                            if parse_zed_link(&url, cx).is_some() {
18309                                window
18310                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18311                            } else {
18312                                cx.open_url(&url);
18313                            }
18314                        })?;
18315                        Ok(Navigated::Yes)
18316                    }
18317                    Some(Either::Right(path)) => {
18318                        // TODO(andrew): respect preview tab settings
18319                        //               `enable_keep_preview_on_code_navigation` and
18320                        //               `enable_preview_file_from_code_navigation`
18321                        let Some(workspace) = workspace else {
18322                            return Ok(Navigated::No);
18323                        };
18324                        workspace
18325                            .update_in(cx, |workspace, window, cx| {
18326                                workspace.open_resolved_path(path, window, cx)
18327                            })?
18328                            .await?;
18329                        Ok(Navigated::Yes)
18330                    }
18331                    None => Ok(Navigated::No),
18332                }
18333            } else {
18334                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18335
18336                editor.update_in(cx, |editor, window, cx| {
18337                    let target_ranges = target_ranges
18338                        .into_iter()
18339                        .map(|r| editor.range_for_match(&r))
18340                        .map(collapse_multiline_range)
18341                        .collect::<Vec<_>>();
18342                    if !split
18343                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18344                    {
18345                        let multibuffer = editor.buffer.read(cx);
18346                        let target_ranges = target_ranges
18347                            .into_iter()
18348                            .filter_map(|r| {
18349                                let start = multibuffer.buffer_point_to_anchor(
18350                                    &target_buffer,
18351                                    r.start,
18352                                    cx,
18353                                )?;
18354                                let end = multibuffer.buffer_point_to_anchor(
18355                                    &target_buffer,
18356                                    r.end,
18357                                    cx,
18358                                )?;
18359                                Some(start..end)
18360                            })
18361                            .collect::<Vec<_>>();
18362                        if target_ranges.is_empty() {
18363                            return Navigated::No;
18364                        }
18365
18366                        editor.change_selections(
18367                            SelectionEffects::default().nav_history(true),
18368                            window,
18369                            cx,
18370                            |s| s.select_anchor_ranges(target_ranges),
18371                        );
18372
18373                        let target =
18374                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18375                        if let Some(mut nav_history) = editor.nav_history.clone() {
18376                            nav_history.push_tag(origin, target);
18377                        }
18378                    } else {
18379                        let Some(workspace) = workspace else {
18380                            return Navigated::No;
18381                        };
18382                        let pane = workspace.read(cx).active_pane().clone();
18383                        window.defer(cx, move |window, cx| {
18384                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18385                                workspace.update(cx, |workspace, cx| {
18386                                    let pane = if split {
18387                                        workspace.adjacent_pane(window, cx)
18388                                    } else {
18389                                        workspace.active_pane().clone()
18390                                    };
18391
18392                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18393                                    let keep_old_preview = preview_tabs_settings
18394                                        .enable_keep_preview_on_code_navigation;
18395                                    let allow_new_preview = preview_tabs_settings
18396                                        .enable_preview_file_from_code_navigation;
18397
18398                                    let editor = workspace.open_project_item(
18399                                        pane.clone(),
18400                                        target_buffer.clone(),
18401                                        true,
18402                                        true,
18403                                        keep_old_preview,
18404                                        allow_new_preview,
18405                                        window,
18406                                        cx,
18407                                    );
18408                                    (editor, pane)
18409                                });
18410                            // We create our own nav history instead of using
18411                            // `target_editor.nav_history` because `nav_history`
18412                            // seems to be populated asynchronously when an item
18413                            // is added to a pane
18414                            let mut nav_history = target_pane
18415                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18416                            target_editor.update(cx, |target_editor, cx| {
18417                                // When selecting a definition in a different buffer, disable the nav history
18418                                // to avoid creating a history entry at the previous cursor location.
18419                                pane.update(cx, |pane, _| pane.disable_history());
18420
18421                                let multibuffer = target_editor.buffer.read(cx);
18422                                let Some(target_buffer) = multibuffer.as_singleton() else {
18423                                    return Navigated::No;
18424                                };
18425                                let target_ranges = target_ranges
18426                                    .into_iter()
18427                                    .filter_map(|r| {
18428                                        let start = multibuffer.buffer_point_to_anchor(
18429                                            &target_buffer,
18430                                            r.start,
18431                                            cx,
18432                                        )?;
18433                                        let end = multibuffer.buffer_point_to_anchor(
18434                                            &target_buffer,
18435                                            r.end,
18436                                            cx,
18437                                        )?;
18438                                        Some(start..end)
18439                                    })
18440                                    .collect::<Vec<_>>();
18441                                if target_ranges.is_empty() {
18442                                    return Navigated::No;
18443                                }
18444
18445                                target_editor.change_selections(
18446                                    SelectionEffects::default().nav_history(true),
18447                                    window,
18448                                    cx,
18449                                    |s| s.select_anchor_ranges(target_ranges),
18450                                );
18451
18452                                let nav_data = target_editor.navigation_data(
18453                                    target_editor.selections.newest_anchor().head(),
18454                                    cx,
18455                                );
18456                                let target =
18457                                    Some(nav_history.navigation_entry(Some(
18458                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18459                                    )));
18460                                nav_history.push_tag(origin, target);
18461                                pane.update(cx, |pane, _| pane.enable_history());
18462                                Navigated::Yes
18463                            });
18464                        });
18465                    }
18466                    Navigated::Yes
18467                })
18468            }
18469        })
18470    }
18471
18472    fn compute_target_location(
18473        &self,
18474        lsp_location: lsp::Location,
18475        server_id: LanguageServerId,
18476        window: &mut Window,
18477        cx: &mut Context<Self>,
18478    ) -> Task<anyhow::Result<Option<Location>>> {
18479        let Some(project) = self.project.clone() else {
18480            return Task::ready(Ok(None));
18481        };
18482
18483        cx.spawn_in(window, async move |editor, cx| {
18484            let location_task = editor.update(cx, |_, cx| {
18485                project.update(cx, |project, cx| {
18486                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18487                })
18488            })?;
18489            let location = Some({
18490                let target_buffer_handle = location_task.await.context("open local buffer")?;
18491                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18492                    let target_start = target_buffer
18493                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18494                    let target_end = target_buffer
18495                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18496                    target_buffer.anchor_after(target_start)
18497                        ..target_buffer.anchor_before(target_end)
18498                });
18499                Location {
18500                    buffer: target_buffer_handle,
18501                    range,
18502                }
18503            });
18504            Ok(location)
18505        })
18506    }
18507
18508    fn go_to_next_reference(
18509        &mut self,
18510        _: &GoToNextReference,
18511        window: &mut Window,
18512        cx: &mut Context<Self>,
18513    ) {
18514        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18515        if let Some(task) = task {
18516            task.detach();
18517        };
18518    }
18519
18520    fn go_to_prev_reference(
18521        &mut self,
18522        _: &GoToPreviousReference,
18523        window: &mut Window,
18524        cx: &mut Context<Self>,
18525    ) {
18526        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18527        if let Some(task) = task {
18528            task.detach();
18529        };
18530    }
18531
18532    fn go_to_symbol_by_offset(
18533        &mut self,
18534        window: &mut Window,
18535        cx: &mut Context<Self>,
18536        offset: i8,
18537    ) -> Task<Result<()>> {
18538        let editor_snapshot = self.snapshot(window, cx);
18539
18540        // We don't care about multi-buffer symbols
18541        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18542            return Task::ready(Ok(()));
18543        };
18544
18545        let cursor_offset = self
18546            .selections
18547            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18548            .head();
18549
18550        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18551            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18552                let buffer = ed.buffer.read(cx).as_singleton()?;
18553                Some(buffer.read(cx).remote_id())
18554            }) else {
18555                return Ok(());
18556            };
18557
18558            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18559            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18560
18561            let multi_snapshot = editor_snapshot.buffer();
18562            let buffer_range = |range: &Range<_>| {
18563                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18564            };
18565
18566            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18567                let current_idx = outline_items
18568                    .iter()
18569                    .enumerate()
18570                    .filter_map(|(idx, item)| {
18571                        // Find the closest outline item by distance between outline text and cursor location
18572                        let source_range = buffer_range(&item.source_range_for_text);
18573                        let distance_to_closest_endpoint = cmp::min(
18574                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18575                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18576                        );
18577
18578                        let item_towards_offset =
18579                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18580                                == (offset as isize).signum();
18581
18582                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18583
18584                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18585                        // we should not already be within the outline's source range. We then pick the closest outline
18586                        // item.
18587                        (item_towards_offset && !source_range_contains_cursor)
18588                            .then_some((distance_to_closest_endpoint, idx))
18589                    })
18590                    .min()
18591                    .map(|(_, idx)| idx);
18592
18593                let Some(idx) = current_idx else {
18594                    return;
18595                };
18596
18597                let range = buffer_range(&outline_items[idx].source_range_for_text);
18598                let selection = [range.start..range.start];
18599
18600                let _ = editor
18601                    .update(acx, |editor, ecx| {
18602                        editor.change_selections(
18603                            SelectionEffects::scroll(Autoscroll::newest()),
18604                            window,
18605                            ecx,
18606                            |s| s.select_ranges(selection),
18607                        );
18608                    })
18609                    .ok();
18610            })?;
18611
18612            Ok(())
18613        })
18614    }
18615
18616    fn go_to_next_symbol(
18617        &mut self,
18618        _: &GoToNextSymbol,
18619        window: &mut Window,
18620        cx: &mut Context<Self>,
18621    ) {
18622        self.go_to_symbol_by_offset(window, cx, 1).detach();
18623    }
18624
18625    fn go_to_previous_symbol(
18626        &mut self,
18627        _: &GoToPreviousSymbol,
18628        window: &mut Window,
18629        cx: &mut Context<Self>,
18630    ) {
18631        self.go_to_symbol_by_offset(window, cx, -1).detach();
18632    }
18633
18634    pub fn go_to_reference_before_or_after_position(
18635        &mut self,
18636        direction: Direction,
18637        count: usize,
18638        window: &mut Window,
18639        cx: &mut Context<Self>,
18640    ) -> Option<Task<Result<()>>> {
18641        let selection = self.selections.newest_anchor();
18642        let head = selection.head();
18643
18644        let multi_buffer = self.buffer.read(cx);
18645
18646        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18647        let workspace = self.workspace()?;
18648        let project = workspace.read(cx).project().clone();
18649        let references =
18650            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18651        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18652            let Some(locations) = references.await? else {
18653                return Ok(());
18654            };
18655
18656            if locations.is_empty() {
18657                // totally normal - the cursor may be on something which is not
18658                // a symbol (e.g. a keyword)
18659                log::info!("no references found under cursor");
18660                return Ok(());
18661            }
18662
18663            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18664
18665            let (locations, current_location_index) =
18666                multi_buffer.update(cx, |multi_buffer, cx| {
18667                    let mut locations = locations
18668                        .into_iter()
18669                        .filter_map(|loc| {
18670                            let start = multi_buffer.buffer_anchor_to_anchor(
18671                                &loc.buffer,
18672                                loc.range.start,
18673                                cx,
18674                            )?;
18675                            let end = multi_buffer.buffer_anchor_to_anchor(
18676                                &loc.buffer,
18677                                loc.range.end,
18678                                cx,
18679                            )?;
18680                            Some(start..end)
18681                        })
18682                        .collect::<Vec<_>>();
18683
18684                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18685                    // There is an O(n) implementation, but given this list will be
18686                    // small (usually <100 items), the extra O(log(n)) factor isn't
18687                    // worth the (surprisingly large amount of) extra complexity.
18688                    locations
18689                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18690
18691                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18692
18693                    let current_location_index = locations.iter().position(|loc| {
18694                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18695                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18696                    });
18697
18698                    (locations, current_location_index)
18699                });
18700
18701            let Some(current_location_index) = current_location_index else {
18702                // This indicates something has gone wrong, because we already
18703                // handle the "no references" case above
18704                log::error!(
18705                    "failed to find current reference under cursor. Total references: {}",
18706                    locations.len()
18707                );
18708                return Ok(());
18709            };
18710
18711            let destination_location_index = match direction {
18712                Direction::Next => (current_location_index + count) % locations.len(),
18713                Direction::Prev => {
18714                    (current_location_index + locations.len() - count % locations.len())
18715                        % locations.len()
18716                }
18717            };
18718
18719            // TODO(cameron): is this needed?
18720            // the thinking is to avoid "jumping to the current location" (avoid
18721            // polluting "jumplist" in vim terms)
18722            if current_location_index == destination_location_index {
18723                return Ok(());
18724            }
18725
18726            let Range { start, end } = locations[destination_location_index];
18727
18728            editor.update_in(cx, |editor, window, cx| {
18729                let effects = SelectionEffects::default();
18730
18731                editor.unfold_ranges(&[start..end], false, false, cx);
18732                editor.change_selections(effects, window, cx, |s| {
18733                    s.select_ranges([start..start]);
18734                });
18735            })?;
18736
18737            Ok(())
18738        }))
18739    }
18740
18741    pub fn find_all_references(
18742        &mut self,
18743        action: &FindAllReferences,
18744        window: &mut Window,
18745        cx: &mut Context<Self>,
18746    ) -> Option<Task<Result<Navigated>>> {
18747        let always_open_multibuffer = action.always_open_multibuffer;
18748        let selection = self.selections.newest_anchor();
18749        let multi_buffer = self.buffer.read(cx);
18750        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18751        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18752        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18753        let head = selection_offset.head();
18754
18755        let head_anchor = multi_buffer_snapshot.anchor_at(
18756            head,
18757            if head < selection_offset.tail() {
18758                Bias::Right
18759            } else {
18760                Bias::Left
18761            },
18762        );
18763
18764        match self
18765            .find_all_references_task_sources
18766            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18767        {
18768            Ok(_) => {
18769                log::info!(
18770                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18771                );
18772                return None;
18773            }
18774            Err(i) => {
18775                self.find_all_references_task_sources.insert(i, head_anchor);
18776            }
18777        }
18778
18779        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18780        let workspace = self.workspace()?;
18781        let project = workspace.read(cx).project().clone();
18782        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18783        Some(cx.spawn_in(window, async move |editor, cx| {
18784            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18785                if let Ok(i) = editor
18786                    .find_all_references_task_sources
18787                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18788                {
18789                    editor.find_all_references_task_sources.remove(i);
18790                }
18791            });
18792
18793            let Some(locations) = references.await? else {
18794                return anyhow::Ok(Navigated::No);
18795            };
18796            let mut locations = cx.update(|_, cx| {
18797                locations
18798                    .into_iter()
18799                    .map(|location| {
18800                        let buffer = location.buffer.read(cx);
18801                        (location.buffer, location.range.to_point(buffer))
18802                    })
18803                    // if special-casing the single-match case, remove ranges
18804                    // that intersect current selection
18805                    .filter(|(location_buffer, location)| {
18806                        if always_open_multibuffer || &buffer != location_buffer {
18807                            return true;
18808                        }
18809
18810                        !location.contains_inclusive(&selection_point.range())
18811                    })
18812                    .into_group_map()
18813            })?;
18814            if locations.is_empty() {
18815                return anyhow::Ok(Navigated::No);
18816            }
18817            for ranges in locations.values_mut() {
18818                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18819                ranges.dedup();
18820            }
18821            let mut num_locations = 0;
18822            for ranges in locations.values_mut() {
18823                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18824                ranges.dedup();
18825                num_locations += ranges.len();
18826            }
18827
18828            if num_locations == 1 && !always_open_multibuffer {
18829                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18830                let target_range = target_ranges.first().unwrap().clone();
18831
18832                return editor.update_in(cx, |editor, window, cx| {
18833                    let range = target_range.to_point(target_buffer.read(cx));
18834                    let range = editor.range_for_match(&range);
18835                    let range = range.start..range.start;
18836
18837                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18838                        editor.go_to_singleton_buffer_range(range, window, cx);
18839                    } else {
18840                        let pane = workspace.read(cx).active_pane().clone();
18841                        window.defer(cx, move |window, cx| {
18842                            let target_editor: Entity<Self> =
18843                                workspace.update(cx, |workspace, cx| {
18844                                    let pane = workspace.active_pane().clone();
18845
18846                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18847                                    let keep_old_preview = preview_tabs_settings
18848                                        .enable_keep_preview_on_code_navigation;
18849                                    let allow_new_preview = preview_tabs_settings
18850                                        .enable_preview_file_from_code_navigation;
18851
18852                                    workspace.open_project_item(
18853                                        pane,
18854                                        target_buffer.clone(),
18855                                        true,
18856                                        true,
18857                                        keep_old_preview,
18858                                        allow_new_preview,
18859                                        window,
18860                                        cx,
18861                                    )
18862                                });
18863                            target_editor.update(cx, |target_editor, cx| {
18864                                // When selecting a definition in a different buffer, disable the nav history
18865                                // to avoid creating a history entry at the previous cursor location.
18866                                pane.update(cx, |pane, _| pane.disable_history());
18867                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18868                                pane.update(cx, |pane, _| pane.enable_history());
18869                            });
18870                        });
18871                    }
18872                    Navigated::No
18873                });
18874            }
18875
18876            workspace.update_in(cx, |workspace, window, cx| {
18877                let target = locations
18878                    .iter()
18879                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18880                    .map(|(buffer, location)| {
18881                        buffer
18882                            .read(cx)
18883                            .text_for_range(location.clone())
18884                            .collect::<String>()
18885                    })
18886                    .filter(|text| !text.contains('\n'))
18887                    .unique()
18888                    .take(3)
18889                    .join(", ");
18890                let title = if target.is_empty() {
18891                    "References".to_owned()
18892                } else {
18893                    format!("References to {target}")
18894                };
18895                let allow_preview = PreviewTabsSettings::get_global(cx)
18896                    .enable_preview_multibuffer_from_code_navigation;
18897                Self::open_locations_in_multibuffer(
18898                    workspace,
18899                    locations,
18900                    title,
18901                    false,
18902                    allow_preview,
18903                    MultibufferSelectionMode::First,
18904                    window,
18905                    cx,
18906                );
18907                Navigated::Yes
18908            })
18909        }))
18910    }
18911
18912    /// Opens a multibuffer with the given project locations in it.
18913    pub fn open_locations_in_multibuffer(
18914        workspace: &mut Workspace,
18915        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18916        title: String,
18917        split: bool,
18918        allow_preview: bool,
18919        multibuffer_selection_mode: MultibufferSelectionMode,
18920        window: &mut Window,
18921        cx: &mut Context<Workspace>,
18922    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18923        if locations.is_empty() {
18924            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18925            return None;
18926        }
18927
18928        let capability = workspace.project().read(cx).capability();
18929        let mut ranges = <Vec<Range<Anchor>>>::new();
18930
18931        // a key to find existing multibuffer editors with the same set of locations
18932        // to prevent us from opening more and more multibuffer tabs for searches and the like
18933        let mut key = (title.clone(), vec![]);
18934        let excerpt_buffer = cx.new(|cx| {
18935            let key = &mut key.1;
18936            let mut multibuffer = MultiBuffer::new(capability);
18937            for (buffer, mut ranges_for_buffer) in locations {
18938                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18939                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18940                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18941                    PathKey::for_buffer(&buffer, cx),
18942                    buffer.clone(),
18943                    ranges_for_buffer,
18944                    multibuffer_context_lines(cx),
18945                    cx,
18946                );
18947                ranges.extend(new_ranges)
18948            }
18949
18950            multibuffer.with_title(title)
18951        });
18952        let existing = workspace.active_pane().update(cx, |pane, cx| {
18953            pane.items()
18954                .filter_map(|item| item.downcast::<Editor>())
18955                .find(|editor| {
18956                    editor
18957                        .read(cx)
18958                        .lookup_key
18959                        .as_ref()
18960                        .and_then(|it| {
18961                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18962                        })
18963                        .is_some_and(|it| *it == key)
18964                })
18965        });
18966        let was_existing = existing.is_some();
18967        let editor = existing.unwrap_or_else(|| {
18968            cx.new(|cx| {
18969                let mut editor = Editor::for_multibuffer(
18970                    excerpt_buffer,
18971                    Some(workspace.project().clone()),
18972                    window,
18973                    cx,
18974                );
18975                editor.lookup_key = Some(Box::new(key));
18976                editor
18977            })
18978        });
18979        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18980            MultibufferSelectionMode::First => {
18981                if let Some(first_range) = ranges.first() {
18982                    editor.change_selections(
18983                        SelectionEffects::no_scroll(),
18984                        window,
18985                        cx,
18986                        |selections| {
18987                            selections.clear_disjoint();
18988                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18989                        },
18990                    );
18991                }
18992                editor.highlight_background(
18993                    HighlightKey::Editor,
18994                    &ranges,
18995                    |_, theme| theme.colors().editor_highlighted_line_background,
18996                    cx,
18997                );
18998            }
18999            MultibufferSelectionMode::All => {
19000                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
19001                    selections.clear_disjoint();
19002                    selections.select_anchor_ranges(ranges);
19003                });
19004            }
19005        });
19006
19007        let item = Box::new(editor.clone());
19008
19009        let pane = if split {
19010            workspace.adjacent_pane(window, cx)
19011        } else {
19012            workspace.active_pane().clone()
19013        };
19014        let activate_pane = split;
19015
19016        let mut destination_index = None;
19017        pane.update(cx, |pane, cx| {
19018            if allow_preview && !was_existing {
19019                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
19020            }
19021            if was_existing && !allow_preview {
19022                pane.unpreview_item_if_preview(item.item_id());
19023            }
19024            pane.add_item(item, activate_pane, true, destination_index, window, cx);
19025        });
19026
19027        Some((editor, pane))
19028    }
19029
19030    pub fn rename(
19031        &mut self,
19032        _: &Rename,
19033        window: &mut Window,
19034        cx: &mut Context<Self>,
19035    ) -> Option<Task<Result<()>>> {
19036        use language::ToOffset as _;
19037
19038        let provider = self.semantics_provider.clone()?;
19039        let selection = self.selections.newest_anchor().clone();
19040        let (cursor_buffer, cursor_buffer_position) = self
19041            .buffer
19042            .read(cx)
19043            .text_anchor_for_position(selection.head(), cx)?;
19044        let (tail_buffer, cursor_buffer_position_end) = self
19045            .buffer
19046            .read(cx)
19047            .text_anchor_for_position(selection.tail(), cx)?;
19048        if tail_buffer != cursor_buffer {
19049            return None;
19050        }
19051
19052        let snapshot = cursor_buffer.read(cx).snapshot();
19053        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
19054        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
19055        let prepare_rename = provider
19056            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
19057            .unwrap_or_else(|| Task::ready(Ok(None)));
19058        drop(snapshot);
19059
19060        Some(cx.spawn_in(window, async move |this, cx| {
19061            let rename_range = if let Some(range) = prepare_rename.await? {
19062                Some(range)
19063            } else {
19064                this.update(cx, |this, cx| {
19065                    let buffer = this.buffer.read(cx).snapshot(cx);
19066                    let mut buffer_highlights = this
19067                        .document_highlights_for_position(selection.head(), &buffer)
19068                        .filter(|highlight| {
19069                            highlight.start.excerpt_id == selection.head().excerpt_id
19070                                && highlight.end.excerpt_id == selection.head().excerpt_id
19071                        });
19072                    buffer_highlights
19073                        .next()
19074                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
19075                })?
19076            };
19077            if let Some(rename_range) = rename_range {
19078                this.update_in(cx, |this, window, cx| {
19079                    let snapshot = cursor_buffer.read(cx).snapshot();
19080                    let rename_buffer_range = rename_range.to_offset(&snapshot);
19081                    let cursor_offset_in_rename_range =
19082                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
19083                    let cursor_offset_in_rename_range_end =
19084                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
19085
19086                    this.take_rename(false, window, cx);
19087                    let buffer = this.buffer.read(cx).read(cx);
19088                    let cursor_offset = selection.head().to_offset(&buffer);
19089                    let rename_start =
19090                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
19091                    let rename_end = rename_start + rename_buffer_range.len();
19092                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
19093                    let mut old_highlight_id = None;
19094                    let old_name: Arc<str> = buffer
19095                        .chunks(rename_start..rename_end, true)
19096                        .map(|chunk| {
19097                            if old_highlight_id.is_none() {
19098                                old_highlight_id = chunk.syntax_highlight_id;
19099                            }
19100                            chunk.text
19101                        })
19102                        .collect::<String>()
19103                        .into();
19104
19105                    drop(buffer);
19106
19107                    // Position the selection in the rename editor so that it matches the current selection.
19108                    this.show_local_selections = false;
19109                    let rename_editor = cx.new(|cx| {
19110                        let mut editor = Editor::single_line(window, cx);
19111                        editor.buffer.update(cx, |buffer, cx| {
19112                            buffer.edit(
19113                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
19114                                None,
19115                                cx,
19116                            )
19117                        });
19118                        let cursor_offset_in_rename_range =
19119                            MultiBufferOffset(cursor_offset_in_rename_range);
19120                        let cursor_offset_in_rename_range_end =
19121                            MultiBufferOffset(cursor_offset_in_rename_range_end);
19122                        let rename_selection_range = match cursor_offset_in_rename_range
19123                            .cmp(&cursor_offset_in_rename_range_end)
19124                        {
19125                            Ordering::Equal => {
19126                                editor.select_all(&SelectAll, window, cx);
19127                                return editor;
19128                            }
19129                            Ordering::Less => {
19130                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
19131                            }
19132                            Ordering::Greater => {
19133                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
19134                            }
19135                        };
19136                        if rename_selection_range.end.0 > old_name.len() {
19137                            editor.select_all(&SelectAll, window, cx);
19138                        } else {
19139                            editor.change_selections(Default::default(), window, cx, |s| {
19140                                s.select_ranges([rename_selection_range]);
19141                            });
19142                        }
19143                        editor
19144                    });
19145                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19146                        if e == &EditorEvent::Focused {
19147                            cx.emit(EditorEvent::FocusedIn)
19148                        }
19149                    })
19150                    .detach();
19151
19152                    let write_highlights =
19153                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19154                    let read_highlights =
19155                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19156                    let ranges = write_highlights
19157                        .iter()
19158                        .flat_map(|(_, ranges)| ranges.iter())
19159                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19160                        .cloned()
19161                        .collect();
19162
19163                    this.highlight_text(
19164                        HighlightKey::Rename,
19165                        ranges,
19166                        HighlightStyle {
19167                            fade_out: Some(0.6),
19168                            ..Default::default()
19169                        },
19170                        cx,
19171                    );
19172                    let rename_focus_handle = rename_editor.focus_handle(cx);
19173                    window.focus(&rename_focus_handle, cx);
19174                    let block_id = this.insert_blocks(
19175                        [BlockProperties {
19176                            style: BlockStyle::Flex,
19177                            placement: BlockPlacement::Below(range.start),
19178                            height: Some(1),
19179                            render: Arc::new({
19180                                let rename_editor = rename_editor.clone();
19181                                move |cx: &mut BlockContext| {
19182                                    let mut text_style = cx.editor_style.text.clone();
19183                                    if let Some(highlight_style) = old_highlight_id
19184                                        .and_then(|h| cx.editor_style.syntax.get(h).cloned())
19185                                    {
19186                                        text_style = text_style.highlight(highlight_style);
19187                                    }
19188                                    div()
19189                                        .block_mouse_except_scroll()
19190                                        .pl(cx.anchor_x)
19191                                        .child(EditorElement::new(
19192                                            &rename_editor,
19193                                            EditorStyle {
19194                                                background: cx.theme().system().transparent,
19195                                                local_player: cx.editor_style.local_player,
19196                                                text: text_style,
19197                                                scrollbar_width: cx.editor_style.scrollbar_width,
19198                                                syntax: cx.editor_style.syntax.clone(),
19199                                                status: cx.editor_style.status.clone(),
19200                                                inlay_hints_style: HighlightStyle {
19201                                                    font_weight: Some(FontWeight::BOLD),
19202                                                    ..make_inlay_hints_style(cx.app)
19203                                                },
19204                                                edit_prediction_styles: make_suggestion_styles(
19205                                                    cx.app,
19206                                                ),
19207                                                ..EditorStyle::default()
19208                                            },
19209                                        ))
19210                                        .into_any_element()
19211                                }
19212                            }),
19213                            priority: 0,
19214                        }],
19215                        Some(Autoscroll::fit()),
19216                        cx,
19217                    )[0];
19218                    this.pending_rename = Some(RenameState {
19219                        range,
19220                        old_name,
19221                        editor: rename_editor,
19222                        block_id,
19223                    });
19224                })?;
19225            }
19226
19227            Ok(())
19228        }))
19229    }
19230
19231    pub fn confirm_rename(
19232        &mut self,
19233        _: &ConfirmRename,
19234        window: &mut Window,
19235        cx: &mut Context<Self>,
19236    ) -> Option<Task<Result<()>>> {
19237        let rename = self.take_rename(false, window, cx)?;
19238        let workspace = self.workspace()?.downgrade();
19239        let (buffer, start) = self
19240            .buffer
19241            .read(cx)
19242            .text_anchor_for_position(rename.range.start, cx)?;
19243        let (end_buffer, _) = self
19244            .buffer
19245            .read(cx)
19246            .text_anchor_for_position(rename.range.end, cx)?;
19247        if buffer != end_buffer {
19248            return None;
19249        }
19250
19251        let old_name = rename.old_name;
19252        let new_name = rename.editor.read(cx).text(cx);
19253
19254        let rename = self.semantics_provider.as_ref()?.perform_rename(
19255            &buffer,
19256            start,
19257            new_name.clone(),
19258            cx,
19259        )?;
19260
19261        Some(cx.spawn_in(window, async move |editor, cx| {
19262            let project_transaction = rename.await?;
19263            Self::open_project_transaction(
19264                &editor,
19265                workspace,
19266                project_transaction,
19267                format!("Rename: {}{}", old_name, new_name),
19268                cx,
19269            )
19270            .await?;
19271
19272            editor.update(cx, |editor, cx| {
19273                editor.refresh_document_highlights(cx);
19274            })?;
19275            Ok(())
19276        }))
19277    }
19278
19279    fn take_rename(
19280        &mut self,
19281        moving_cursor: bool,
19282        window: &mut Window,
19283        cx: &mut Context<Self>,
19284    ) -> Option<RenameState> {
19285        let rename = self.pending_rename.take()?;
19286        if rename.editor.focus_handle(cx).is_focused(window) {
19287            window.focus(&self.focus_handle, cx);
19288        }
19289
19290        self.remove_blocks(
19291            [rename.block_id].into_iter().collect(),
19292            Some(Autoscroll::fit()),
19293            cx,
19294        );
19295        self.clear_highlights(HighlightKey::Rename, cx);
19296        self.show_local_selections = true;
19297
19298        if moving_cursor {
19299            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19300                editor
19301                    .selections
19302                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19303                    .head()
19304            });
19305
19306            // Update the selection to match the position of the selection inside
19307            // the rename editor.
19308            let snapshot = self.buffer.read(cx).read(cx);
19309            let rename_range = rename.range.to_offset(&snapshot);
19310            let cursor_in_editor = snapshot
19311                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19312                .min(rename_range.end);
19313            drop(snapshot);
19314
19315            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19316                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19317            });
19318        } else {
19319            self.refresh_document_highlights(cx);
19320        }
19321
19322        Some(rename)
19323    }
19324
19325    pub fn pending_rename(&self) -> Option<&RenameState> {
19326        self.pending_rename.as_ref()
19327    }
19328
19329    fn format(
19330        &mut self,
19331        _: &Format,
19332        window: &mut Window,
19333        cx: &mut Context<Self>,
19334    ) -> Option<Task<Result<()>>> {
19335        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19336
19337        let project = match &self.project {
19338            Some(project) => project.clone(),
19339            None => return None,
19340        };
19341
19342        Some(self.perform_format(
19343            project,
19344            FormatTrigger::Manual,
19345            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19346            window,
19347            cx,
19348        ))
19349    }
19350
19351    fn format_selections(
19352        &mut self,
19353        _: &FormatSelections,
19354        window: &mut Window,
19355        cx: &mut Context<Self>,
19356    ) -> Option<Task<Result<()>>> {
19357        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19358
19359        let project = match &self.project {
19360            Some(project) => project.clone(),
19361            None => return None,
19362        };
19363
19364        let ranges = self
19365            .selections
19366            .all_adjusted(&self.display_snapshot(cx))
19367            .into_iter()
19368            .map(|selection| selection.range())
19369            .collect_vec();
19370
19371        Some(self.perform_format(
19372            project,
19373            FormatTrigger::Manual,
19374            FormatTarget::Ranges(ranges),
19375            window,
19376            cx,
19377        ))
19378    }
19379
19380    fn perform_format(
19381        &mut self,
19382        project: Entity<Project>,
19383        trigger: FormatTrigger,
19384        target: FormatTarget,
19385        window: &mut Window,
19386        cx: &mut Context<Self>,
19387    ) -> Task<Result<()>> {
19388        let buffer = self.buffer.clone();
19389        let (buffers, target) = match target {
19390            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19391            FormatTarget::Ranges(selection_ranges) => {
19392                let multi_buffer = buffer.read(cx);
19393                let snapshot = multi_buffer.read(cx);
19394                let mut buffers = HashSet::default();
19395                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19396                    BTreeMap::new();
19397                for selection_range in selection_ranges {
19398                    for (buffer, buffer_range, _) in
19399                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19400                    {
19401                        let buffer_id = buffer.remote_id();
19402                        let start = buffer.anchor_before(buffer_range.start);
19403                        let end = buffer.anchor_after(buffer_range.end);
19404                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19405                        buffer_id_to_ranges
19406                            .entry(buffer_id)
19407                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19408                            .or_insert_with(|| vec![start..end]);
19409                    }
19410                }
19411                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19412            }
19413        };
19414
19415        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19416        let selections_prev = transaction_id_prev
19417            .and_then(|transaction_id_prev| {
19418                // default to selections as they were after the last edit, if we have them,
19419                // instead of how they are now.
19420                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19421                // will take you back to where you made the last edit, instead of staying where you scrolled
19422                self.selection_history
19423                    .transaction(transaction_id_prev)
19424                    .map(|t| t.0.clone())
19425            })
19426            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19427
19428        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19429        let format = project.update(cx, |project, cx| {
19430            project.format(buffers, target, true, trigger, cx)
19431        });
19432
19433        cx.spawn_in(window, async move |editor, cx| {
19434            let transaction = futures::select_biased! {
19435                transaction = format.log_err().fuse() => transaction,
19436                () = timeout => {
19437                    log::warn!("timed out waiting for formatting");
19438                    None
19439                }
19440            };
19441
19442            buffer.update(cx, |buffer, cx| {
19443                if let Some(transaction) = transaction
19444                    && !buffer.is_singleton()
19445                {
19446                    buffer.push_transaction(&transaction.0, cx);
19447                }
19448                cx.notify();
19449            });
19450
19451            if let Some(transaction_id_now) =
19452                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19453            {
19454                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19455                if has_new_transaction {
19456                    editor
19457                        .update(cx, |editor, _| {
19458                            editor
19459                                .selection_history
19460                                .insert_transaction(transaction_id_now, selections_prev);
19461                        })
19462                        .ok();
19463                }
19464            }
19465
19466            Ok(())
19467        })
19468    }
19469
19470    fn organize_imports(
19471        &mut self,
19472        _: &OrganizeImports,
19473        window: &mut Window,
19474        cx: &mut Context<Self>,
19475    ) -> Option<Task<Result<()>>> {
19476        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19477        let project = match &self.project {
19478            Some(project) => project.clone(),
19479            None => return None,
19480        };
19481        Some(self.perform_code_action_kind(
19482            project,
19483            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19484            window,
19485            cx,
19486        ))
19487    }
19488
19489    fn perform_code_action_kind(
19490        &mut self,
19491        project: Entity<Project>,
19492        kind: CodeActionKind,
19493        window: &mut Window,
19494        cx: &mut Context<Self>,
19495    ) -> Task<Result<()>> {
19496        let buffer = self.buffer.clone();
19497        let buffers = buffer.read(cx).all_buffers();
19498        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19499        let apply_action = project.update(cx, |project, cx| {
19500            project.apply_code_action_kind(buffers, kind, true, cx)
19501        });
19502        cx.spawn_in(window, async move |_, cx| {
19503            let transaction = futures::select_biased! {
19504                () = timeout => {
19505                    log::warn!("timed out waiting for executing code action");
19506                    None
19507                }
19508                transaction = apply_action.log_err().fuse() => transaction,
19509            };
19510            buffer.update(cx, |buffer, cx| {
19511                // check if we need this
19512                if let Some(transaction) = transaction
19513                    && !buffer.is_singleton()
19514                {
19515                    buffer.push_transaction(&transaction.0, cx);
19516                }
19517                cx.notify();
19518            });
19519            Ok(())
19520        })
19521    }
19522
19523    pub fn restart_language_server(
19524        &mut self,
19525        _: &RestartLanguageServer,
19526        _: &mut Window,
19527        cx: &mut Context<Self>,
19528    ) {
19529        if let Some(project) = self.project.clone() {
19530            self.buffer.update(cx, |multi_buffer, cx| {
19531                project.update(cx, |project, cx| {
19532                    project.restart_language_servers_for_buffers(
19533                        multi_buffer.all_buffers().into_iter().collect(),
19534                        HashSet::default(),
19535                        cx,
19536                    );
19537                });
19538            })
19539        }
19540    }
19541
19542    pub fn stop_language_server(
19543        &mut self,
19544        _: &StopLanguageServer,
19545        _: &mut Window,
19546        cx: &mut Context<Self>,
19547    ) {
19548        if let Some(project) = self.project.clone() {
19549            self.buffer.update(cx, |multi_buffer, cx| {
19550                project.update(cx, |project, cx| {
19551                    project.stop_language_servers_for_buffers(
19552                        multi_buffer.all_buffers().into_iter().collect(),
19553                        HashSet::default(),
19554                        cx,
19555                    );
19556                });
19557            });
19558        }
19559    }
19560
19561    fn cancel_language_server_work(
19562        workspace: &mut Workspace,
19563        _: &actions::CancelLanguageServerWork,
19564        _: &mut Window,
19565        cx: &mut Context<Workspace>,
19566    ) {
19567        let project = workspace.project();
19568        let buffers = workspace
19569            .active_item(cx)
19570            .and_then(|item| item.act_as::<Editor>(cx))
19571            .map_or(HashSet::default(), |editor| {
19572                editor.read(cx).buffer.read(cx).all_buffers()
19573            });
19574        project.update(cx, |project, cx| {
19575            project.cancel_language_server_work_for_buffers(buffers, cx);
19576        });
19577    }
19578
19579    fn show_character_palette(
19580        &mut self,
19581        _: &ShowCharacterPalette,
19582        window: &mut Window,
19583        _: &mut Context<Self>,
19584    ) {
19585        window.show_character_palette();
19586    }
19587
19588    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19589        if !self.diagnostics_enabled() {
19590            return;
19591        }
19592
19593        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19594            let buffer = self.buffer.read(cx).snapshot(cx);
19595            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19596            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19597            let is_valid = buffer
19598                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19599                .any(|entry| {
19600                    entry.diagnostic.is_primary
19601                        && !entry.range.is_empty()
19602                        && entry.range.start == primary_range_start
19603                        && entry.diagnostic.message == active_diagnostics.active_message
19604                });
19605
19606            if !is_valid {
19607                self.dismiss_diagnostics(cx);
19608            }
19609        }
19610    }
19611
19612    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19613        match &self.active_diagnostics {
19614            ActiveDiagnostic::Group(group) => Some(group),
19615            _ => None,
19616        }
19617    }
19618
19619    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19620        if !self.diagnostics_enabled() {
19621            return;
19622        }
19623        self.dismiss_diagnostics(cx);
19624        self.active_diagnostics = ActiveDiagnostic::All;
19625    }
19626
19627    fn activate_diagnostics(
19628        &mut self,
19629        buffer_id: BufferId,
19630        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19631        window: &mut Window,
19632        cx: &mut Context<Self>,
19633    ) {
19634        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19635            return;
19636        }
19637        self.dismiss_diagnostics(cx);
19638        let snapshot = self.snapshot(window, cx);
19639        let buffer = self.buffer.read(cx).snapshot(cx);
19640        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19641            return;
19642        };
19643
19644        let diagnostic_group = buffer
19645            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19646            .collect::<Vec<_>>();
19647
19648        let language_registry = self
19649            .project()
19650            .map(|project| project.read(cx).languages().clone());
19651
19652        let blocks = renderer.render_group(
19653            diagnostic_group,
19654            buffer_id,
19655            snapshot,
19656            cx.weak_entity(),
19657            language_registry,
19658            cx,
19659        );
19660
19661        let blocks = self.display_map.update(cx, |display_map, cx| {
19662            display_map.insert_blocks(blocks, cx).into_iter().collect()
19663        });
19664        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19665            active_range: buffer.anchor_before(diagnostic.range.start)
19666                ..buffer.anchor_after(diagnostic.range.end),
19667            active_message: diagnostic.diagnostic.message.clone(),
19668            group_id: diagnostic.diagnostic.group_id,
19669            blocks,
19670        });
19671        cx.notify();
19672    }
19673
19674    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19675        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19676            return;
19677        };
19678
19679        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19680        if let ActiveDiagnostic::Group(group) = prev {
19681            self.display_map.update(cx, |display_map, cx| {
19682                display_map.remove_blocks(group.blocks, cx);
19683            });
19684            cx.notify();
19685        }
19686    }
19687
19688    /// Disable inline diagnostics rendering for this editor.
19689    pub fn disable_inline_diagnostics(&mut self) {
19690        self.inline_diagnostics_enabled = false;
19691        self.inline_diagnostics_update = Task::ready(());
19692        self.inline_diagnostics.clear();
19693    }
19694
19695    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19696        self.diagnostics_enabled = false;
19697        self.dismiss_diagnostics(cx);
19698        self.inline_diagnostics_update = Task::ready(());
19699        self.inline_diagnostics.clear();
19700    }
19701
19702    pub fn disable_word_completions(&mut self) {
19703        self.word_completions_enabled = false;
19704    }
19705
19706    pub fn diagnostics_enabled(&self) -> bool {
19707        self.diagnostics_enabled && self.lsp_data_enabled()
19708    }
19709
19710    pub fn inline_diagnostics_enabled(&self) -> bool {
19711        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19712    }
19713
19714    pub fn show_inline_diagnostics(&self) -> bool {
19715        self.show_inline_diagnostics
19716    }
19717
19718    pub fn toggle_inline_diagnostics(
19719        &mut self,
19720        _: &ToggleInlineDiagnostics,
19721        window: &mut Window,
19722        cx: &mut Context<Editor>,
19723    ) {
19724        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19725        self.refresh_inline_diagnostics(false, window, cx);
19726    }
19727
19728    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19729        self.diagnostics_max_severity = severity;
19730        self.display_map.update(cx, |display_map, _| {
19731            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19732        });
19733    }
19734
19735    pub fn toggle_diagnostics(
19736        &mut self,
19737        _: &ToggleDiagnostics,
19738        window: &mut Window,
19739        cx: &mut Context<Editor>,
19740    ) {
19741        if !self.diagnostics_enabled() {
19742            return;
19743        }
19744
19745        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19746            EditorSettings::get_global(cx)
19747                .diagnostics_max_severity
19748                .filter(|severity| severity != &DiagnosticSeverity::Off)
19749                .unwrap_or(DiagnosticSeverity::Hint)
19750        } else {
19751            DiagnosticSeverity::Off
19752        };
19753        self.set_max_diagnostics_severity(new_severity, cx);
19754        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19755            self.active_diagnostics = ActiveDiagnostic::None;
19756            self.inline_diagnostics_update = Task::ready(());
19757            self.inline_diagnostics.clear();
19758        } else {
19759            self.refresh_inline_diagnostics(false, window, cx);
19760        }
19761
19762        cx.notify();
19763    }
19764
19765    pub fn toggle_minimap(
19766        &mut self,
19767        _: &ToggleMinimap,
19768        window: &mut Window,
19769        cx: &mut Context<Editor>,
19770    ) {
19771        if self.supports_minimap(cx) {
19772            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19773        }
19774    }
19775
19776    fn refresh_inline_diagnostics(
19777        &mut self,
19778        debounce: bool,
19779        window: &mut Window,
19780        cx: &mut Context<Self>,
19781    ) {
19782        let max_severity = ProjectSettings::get_global(cx)
19783            .diagnostics
19784            .inline
19785            .max_severity
19786            .unwrap_or(self.diagnostics_max_severity);
19787
19788        if !self.inline_diagnostics_enabled()
19789            || !self.diagnostics_enabled()
19790            || !self.show_inline_diagnostics
19791            || max_severity == DiagnosticSeverity::Off
19792        {
19793            self.inline_diagnostics_update = Task::ready(());
19794            self.inline_diagnostics.clear();
19795            return;
19796        }
19797
19798        let debounce_ms = ProjectSettings::get_global(cx)
19799            .diagnostics
19800            .inline
19801            .update_debounce_ms;
19802        let debounce = if debounce && debounce_ms > 0 {
19803            Some(Duration::from_millis(debounce_ms))
19804        } else {
19805            None
19806        };
19807        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19808            if let Some(debounce) = debounce {
19809                cx.background_executor().timer(debounce).await;
19810            }
19811            let Some(snapshot) = editor.upgrade().map(|editor| {
19812                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19813            }) else {
19814                return;
19815            };
19816
19817            let new_inline_diagnostics = cx
19818                .background_spawn(async move {
19819                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19820                    for diagnostic_entry in
19821                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19822                    {
19823                        let message = diagnostic_entry
19824                            .diagnostic
19825                            .message
19826                            .split_once('\n')
19827                            .map(|(line, _)| line)
19828                            .map(SharedString::new)
19829                            .unwrap_or_else(|| {
19830                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19831                            });
19832                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19833                        let (Ok(i) | Err(i)) = inline_diagnostics
19834                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19835                        inline_diagnostics.insert(
19836                            i,
19837                            (
19838                                start_anchor,
19839                                InlineDiagnostic {
19840                                    message,
19841                                    group_id: diagnostic_entry.diagnostic.group_id,
19842                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19843                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19844                                    severity: diagnostic_entry.diagnostic.severity,
19845                                },
19846                            ),
19847                        );
19848                    }
19849                    inline_diagnostics
19850                })
19851                .await;
19852
19853            editor
19854                .update(cx, |editor, cx| {
19855                    editor.inline_diagnostics = new_inline_diagnostics;
19856                    cx.notify();
19857                })
19858                .ok();
19859        });
19860    }
19861
19862    fn pull_diagnostics(
19863        &mut self,
19864        buffer_id: BufferId,
19865        _window: &Window,
19866        cx: &mut Context<Self>,
19867    ) -> Option<()> {
19868        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19869        // skip any LSP updates for it.
19870
19871        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19872            return None;
19873        }
19874        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19875            .diagnostics
19876            .lsp_pull_diagnostics;
19877        if !pull_diagnostics_settings.enabled {
19878            return None;
19879        }
19880        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19881        let project = self.project()?.downgrade();
19882        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19883
19884        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19885            cx.background_executor().timer(debounce).await;
19886            if let Ok(task) = project.update(cx, |project, cx| {
19887                project.lsp_store().update(cx, |lsp_store, cx| {
19888                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19889                })
19890            }) {
19891                task.await.log_err();
19892            }
19893            project
19894                .update(cx, |project, cx| {
19895                    project.lsp_store().update(cx, |lsp_store, cx| {
19896                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19897                    })
19898                })
19899                .log_err();
19900        });
19901
19902        Some(())
19903    }
19904
19905    pub fn set_selections_from_remote(
19906        &mut self,
19907        selections: Vec<Selection<Anchor>>,
19908        pending_selection: Option<Selection<Anchor>>,
19909        window: &mut Window,
19910        cx: &mut Context<Self>,
19911    ) {
19912        let old_cursor_position = self.selections.newest_anchor().head();
19913        self.selections
19914            .change_with(&self.display_snapshot(cx), |s| {
19915                s.select_anchors(selections);
19916                if let Some(pending_selection) = pending_selection {
19917                    s.set_pending(pending_selection, SelectMode::Character);
19918                } else {
19919                    s.clear_pending();
19920                }
19921            });
19922        self.selections_did_change(
19923            false,
19924            &old_cursor_position,
19925            SelectionEffects::default(),
19926            window,
19927            cx,
19928        );
19929    }
19930
19931    pub fn transact(
19932        &mut self,
19933        window: &mut Window,
19934        cx: &mut Context<Self>,
19935        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19936    ) -> Option<TransactionId> {
19937        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19938            this.start_transaction_at(Instant::now(), window, cx);
19939            update(this, window, cx);
19940            this.end_transaction_at(Instant::now(), cx)
19941        })
19942    }
19943
19944    pub fn start_transaction_at(
19945        &mut self,
19946        now: Instant,
19947        window: &mut Window,
19948        cx: &mut Context<Self>,
19949    ) -> Option<TransactionId> {
19950        self.end_selection(window, cx);
19951        if let Some(tx_id) = self
19952            .buffer
19953            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19954        {
19955            self.selection_history
19956                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19957            cx.emit(EditorEvent::TransactionBegun {
19958                transaction_id: tx_id,
19959            });
19960            Some(tx_id)
19961        } else {
19962            None
19963        }
19964    }
19965
19966    pub fn end_transaction_at(
19967        &mut self,
19968        now: Instant,
19969        cx: &mut Context<Self>,
19970    ) -> Option<TransactionId> {
19971        if let Some(transaction_id) = self
19972            .buffer
19973            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19974        {
19975            if let Some((_, end_selections)) =
19976                self.selection_history.transaction_mut(transaction_id)
19977            {
19978                *end_selections = Some(self.selections.disjoint_anchors_arc());
19979            } else {
19980                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19981            }
19982
19983            cx.emit(EditorEvent::Edited { transaction_id });
19984            Some(transaction_id)
19985        } else {
19986            None
19987        }
19988    }
19989
19990    pub fn modify_transaction_selection_history(
19991        &mut self,
19992        transaction_id: TransactionId,
19993        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19994    ) -> bool {
19995        self.selection_history
19996            .transaction_mut(transaction_id)
19997            .map(modify)
19998            .is_some()
19999    }
20000
20001    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
20002        if self.selection_mark_mode {
20003            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20004                s.move_with(&mut |_, sel| {
20005                    sel.collapse_to(sel.head(), SelectionGoal::None);
20006                });
20007            })
20008        }
20009        self.selection_mark_mode = true;
20010        cx.notify();
20011    }
20012
20013    pub fn swap_selection_ends(
20014        &mut self,
20015        _: &actions::SwapSelectionEnds,
20016        window: &mut Window,
20017        cx: &mut Context<Self>,
20018    ) {
20019        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20020            s.move_with(&mut |_, sel| {
20021                if sel.start != sel.end {
20022                    sel.reversed = !sel.reversed
20023                }
20024            });
20025        });
20026        self.request_autoscroll(Autoscroll::newest(), cx);
20027        cx.notify();
20028    }
20029
20030    pub fn toggle_focus(
20031        workspace: &mut Workspace,
20032        _: &actions::ToggleFocus,
20033        window: &mut Window,
20034        cx: &mut Context<Workspace>,
20035    ) {
20036        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
20037            return;
20038        };
20039        workspace.activate_item(&item, true, true, window, cx);
20040    }
20041
20042    pub fn toggle_fold(
20043        &mut self,
20044        _: &actions::ToggleFold,
20045        window: &mut Window,
20046        cx: &mut Context<Self>,
20047    ) {
20048        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20049            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20050            let selection = self.selections.newest::<Point>(&display_map);
20051
20052            let range = if selection.is_empty() {
20053                let point = selection.head().to_display_point(&display_map);
20054                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20055                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20056                    .to_point(&display_map);
20057                start..end
20058            } else {
20059                selection.range()
20060            };
20061            if display_map.folds_in_range(range).next().is_some() {
20062                self.unfold_lines(&Default::default(), window, cx)
20063            } else {
20064                self.fold(&Default::default(), window, cx)
20065            }
20066        } else {
20067            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20068            let buffer_ids: HashSet<_> = self
20069                .selections
20070                .disjoint_anchor_ranges()
20071                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20072                .collect();
20073
20074            let should_unfold = buffer_ids
20075                .iter()
20076                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20077
20078            for buffer_id in buffer_ids {
20079                if should_unfold {
20080                    self.unfold_buffer(buffer_id, cx);
20081                } else {
20082                    self.fold_buffer(buffer_id, cx);
20083                }
20084            }
20085        }
20086    }
20087
20088    pub fn toggle_fold_recursive(
20089        &mut self,
20090        _: &actions::ToggleFoldRecursive,
20091        window: &mut Window,
20092        cx: &mut Context<Self>,
20093    ) {
20094        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
20095
20096        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20097        let range = if selection.is_empty() {
20098            let point = selection.head().to_display_point(&display_map);
20099            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20100            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20101                .to_point(&display_map);
20102            start..end
20103        } else {
20104            selection.range()
20105        };
20106        if display_map.folds_in_range(range).next().is_some() {
20107            self.unfold_recursive(&Default::default(), window, cx)
20108        } else {
20109            self.fold_recursive(&Default::default(), window, cx)
20110        }
20111    }
20112
20113    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
20114        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20115            let mut to_fold = Vec::new();
20116            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20117            let selections = self.selections.all_adjusted(&display_map);
20118
20119            for selection in selections {
20120                let range = selection.range().sorted();
20121                let buffer_start_row = range.start.row;
20122
20123                if range.start.row != range.end.row {
20124                    let mut found = false;
20125                    let mut row = range.start.row;
20126                    while row <= range.end.row {
20127                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20128                        {
20129                            found = true;
20130                            row = crease.range().end.row + 1;
20131                            to_fold.push(crease);
20132                        } else {
20133                            row += 1
20134                        }
20135                    }
20136                    if found {
20137                        continue;
20138                    }
20139                }
20140
20141                for row in (0..=range.start.row).rev() {
20142                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20143                        && crease.range().end.row >= buffer_start_row
20144                    {
20145                        to_fold.push(crease);
20146                        if row <= range.start.row {
20147                            break;
20148                        }
20149                    }
20150                }
20151            }
20152
20153            self.fold_creases(to_fold, true, window, cx);
20154        } else {
20155            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20156            let buffer_ids = self
20157                .selections
20158                .disjoint_anchor_ranges()
20159                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20160                .collect::<HashSet<_>>();
20161            for buffer_id in buffer_ids {
20162                self.fold_buffer(buffer_id, cx);
20163            }
20164        }
20165    }
20166
20167    pub fn toggle_fold_all(
20168        &mut self,
20169        _: &actions::ToggleFoldAll,
20170        window: &mut Window,
20171        cx: &mut Context<Self>,
20172    ) {
20173        let has_folds = if self.buffer.read(cx).is_singleton() {
20174            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20175            let has_folds = display_map
20176                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20177                .next()
20178                .is_some();
20179            has_folds
20180        } else {
20181            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20182            let has_folds = buffer_ids
20183                .iter()
20184                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20185            has_folds
20186        };
20187
20188        if has_folds {
20189            self.unfold_all(&actions::UnfoldAll, window, cx);
20190        } else {
20191            self.fold_all(&actions::FoldAll, window, cx);
20192        }
20193    }
20194
20195    fn fold_at_level(
20196        &mut self,
20197        fold_at: &FoldAtLevel,
20198        window: &mut Window,
20199        cx: &mut Context<Self>,
20200    ) {
20201        if !self.buffer.read(cx).is_singleton() {
20202            return;
20203        }
20204
20205        let fold_at_level = fold_at.0;
20206        let snapshot = self.buffer.read(cx).snapshot(cx);
20207        let mut to_fold = Vec::new();
20208        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20209
20210        let row_ranges_to_keep: Vec<Range<u32>> = self
20211            .selections
20212            .all::<Point>(&self.display_snapshot(cx))
20213            .into_iter()
20214            .map(|sel| sel.start.row..sel.end.row)
20215            .collect();
20216
20217        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20218            while start_row < end_row {
20219                match self
20220                    .snapshot(window, cx)
20221                    .crease_for_buffer_row(MultiBufferRow(start_row))
20222                {
20223                    Some(crease) => {
20224                        let nested_start_row = crease.range().start.row + 1;
20225                        let nested_end_row = crease.range().end.row;
20226
20227                        if current_level < fold_at_level {
20228                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20229                        } else if current_level == fold_at_level {
20230                            // Fold iff there is no selection completely contained within the fold region
20231                            if !row_ranges_to_keep.iter().any(|selection| {
20232                                selection.end >= nested_start_row
20233                                    && selection.start <= nested_end_row
20234                            }) {
20235                                to_fold.push(crease);
20236                            }
20237                        }
20238
20239                        start_row = nested_end_row + 1;
20240                    }
20241                    None => start_row += 1,
20242                }
20243            }
20244        }
20245
20246        self.fold_creases(to_fold, true, window, cx);
20247    }
20248
20249    pub fn fold_at_level_1(
20250        &mut self,
20251        _: &actions::FoldAtLevel1,
20252        window: &mut Window,
20253        cx: &mut Context<Self>,
20254    ) {
20255        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20256    }
20257
20258    pub fn fold_at_level_2(
20259        &mut self,
20260        _: &actions::FoldAtLevel2,
20261        window: &mut Window,
20262        cx: &mut Context<Self>,
20263    ) {
20264        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20265    }
20266
20267    pub fn fold_at_level_3(
20268        &mut self,
20269        _: &actions::FoldAtLevel3,
20270        window: &mut Window,
20271        cx: &mut Context<Self>,
20272    ) {
20273        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20274    }
20275
20276    pub fn fold_at_level_4(
20277        &mut self,
20278        _: &actions::FoldAtLevel4,
20279        window: &mut Window,
20280        cx: &mut Context<Self>,
20281    ) {
20282        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20283    }
20284
20285    pub fn fold_at_level_5(
20286        &mut self,
20287        _: &actions::FoldAtLevel5,
20288        window: &mut Window,
20289        cx: &mut Context<Self>,
20290    ) {
20291        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20292    }
20293
20294    pub fn fold_at_level_6(
20295        &mut self,
20296        _: &actions::FoldAtLevel6,
20297        window: &mut Window,
20298        cx: &mut Context<Self>,
20299    ) {
20300        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20301    }
20302
20303    pub fn fold_at_level_7(
20304        &mut self,
20305        _: &actions::FoldAtLevel7,
20306        window: &mut Window,
20307        cx: &mut Context<Self>,
20308    ) {
20309        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20310    }
20311
20312    pub fn fold_at_level_8(
20313        &mut self,
20314        _: &actions::FoldAtLevel8,
20315        window: &mut Window,
20316        cx: &mut Context<Self>,
20317    ) {
20318        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20319    }
20320
20321    pub fn fold_at_level_9(
20322        &mut self,
20323        _: &actions::FoldAtLevel9,
20324        window: &mut Window,
20325        cx: &mut Context<Self>,
20326    ) {
20327        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20328    }
20329
20330    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20331        if self.buffer.read(cx).is_singleton() {
20332            let mut fold_ranges = Vec::new();
20333            let snapshot = self.buffer.read(cx).snapshot(cx);
20334
20335            for row in 0..snapshot.max_row().0 {
20336                if let Some(foldable_range) = self
20337                    .snapshot(window, cx)
20338                    .crease_for_buffer_row(MultiBufferRow(row))
20339                {
20340                    fold_ranges.push(foldable_range);
20341                }
20342            }
20343
20344            self.fold_creases(fold_ranges, true, window, cx);
20345        } else {
20346            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20347                editor
20348                    .update_in(cx, |editor, _, cx| {
20349                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20350                            editor.fold_buffer(buffer_id, cx);
20351                        }
20352                    })
20353                    .ok();
20354            });
20355        }
20356    }
20357
20358    pub fn fold_function_bodies(
20359        &mut self,
20360        _: &actions::FoldFunctionBodies,
20361        window: &mut Window,
20362        cx: &mut Context<Self>,
20363    ) {
20364        let snapshot = self.buffer.read(cx).snapshot(cx);
20365
20366        let ranges = snapshot
20367            .text_object_ranges(
20368                MultiBufferOffset(0)..snapshot.len(),
20369                TreeSitterOptions::default(),
20370            )
20371            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20372            .collect::<Vec<_>>();
20373
20374        let creases = ranges
20375            .into_iter()
20376            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20377            .collect();
20378
20379        self.fold_creases(creases, true, window, cx);
20380    }
20381
20382    pub fn fold_recursive(
20383        &mut self,
20384        _: &actions::FoldRecursive,
20385        window: &mut Window,
20386        cx: &mut Context<Self>,
20387    ) {
20388        let mut to_fold = Vec::new();
20389        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20390        let selections = self.selections.all_adjusted(&display_map);
20391
20392        for selection in selections {
20393            let range = selection.range().sorted();
20394            let buffer_start_row = range.start.row;
20395
20396            if range.start.row != range.end.row {
20397                let mut found = false;
20398                for row in range.start.row..=range.end.row {
20399                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20400                        found = true;
20401                        to_fold.push(crease);
20402                    }
20403                }
20404                if found {
20405                    continue;
20406                }
20407            }
20408
20409            for row in (0..=range.start.row).rev() {
20410                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20411                    if crease.range().end.row >= buffer_start_row {
20412                        to_fold.push(crease);
20413                    } else {
20414                        break;
20415                    }
20416                }
20417            }
20418        }
20419
20420        self.fold_creases(to_fold, true, window, cx);
20421    }
20422
20423    pub fn fold_at(
20424        &mut self,
20425        buffer_row: MultiBufferRow,
20426        window: &mut Window,
20427        cx: &mut Context<Self>,
20428    ) {
20429        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20430
20431        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20432            let autoscroll = self
20433                .selections
20434                .all::<Point>(&display_map)
20435                .iter()
20436                .any(|selection| crease.range().overlaps(&selection.range()));
20437
20438            self.fold_creases(vec![crease], autoscroll, window, cx);
20439        }
20440    }
20441
20442    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20443        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20444            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20445            let buffer = display_map.buffer_snapshot();
20446            let selections = self.selections.all::<Point>(&display_map);
20447            let ranges = selections
20448                .iter()
20449                .map(|s| {
20450                    let range = s.display_range(&display_map).sorted();
20451                    let mut start = range.start.to_point(&display_map);
20452                    let mut end = range.end.to_point(&display_map);
20453                    start.column = 0;
20454                    end.column = buffer.line_len(MultiBufferRow(end.row));
20455                    start..end
20456                })
20457                .collect::<Vec<_>>();
20458
20459            self.unfold_ranges(&ranges, true, true, cx);
20460        } else {
20461            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20462            let buffer_ids = self
20463                .selections
20464                .disjoint_anchor_ranges()
20465                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20466                .collect::<HashSet<_>>();
20467            for buffer_id in buffer_ids {
20468                self.unfold_buffer(buffer_id, cx);
20469            }
20470        }
20471    }
20472
20473    pub fn unfold_recursive(
20474        &mut self,
20475        _: &UnfoldRecursive,
20476        _window: &mut Window,
20477        cx: &mut Context<Self>,
20478    ) {
20479        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20480        let selections = self.selections.all::<Point>(&display_map);
20481        let ranges = selections
20482            .iter()
20483            .map(|s| {
20484                let mut range = s.display_range(&display_map).sorted();
20485                *range.start.column_mut() = 0;
20486                *range.end.column_mut() = display_map.line_len(range.end.row());
20487                let start = range.start.to_point(&display_map);
20488                let end = range.end.to_point(&display_map);
20489                start..end
20490            })
20491            .collect::<Vec<_>>();
20492
20493        self.unfold_ranges(&ranges, true, true, cx);
20494    }
20495
20496    pub fn unfold_at(
20497        &mut self,
20498        buffer_row: MultiBufferRow,
20499        _window: &mut Window,
20500        cx: &mut Context<Self>,
20501    ) {
20502        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20503
20504        let intersection_range = Point::new(buffer_row.0, 0)
20505            ..Point::new(
20506                buffer_row.0,
20507                display_map.buffer_snapshot().line_len(buffer_row),
20508            );
20509
20510        let autoscroll = self
20511            .selections
20512            .all::<Point>(&display_map)
20513            .iter()
20514            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20515
20516        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20517    }
20518
20519    pub fn unfold_all(
20520        &mut self,
20521        _: &actions::UnfoldAll,
20522        _window: &mut Window,
20523        cx: &mut Context<Self>,
20524    ) {
20525        if self.buffer.read(cx).is_singleton() {
20526            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20527            self.unfold_ranges(
20528                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20529                true,
20530                true,
20531                cx,
20532            );
20533        } else {
20534            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20535                editor
20536                    .update(cx, |editor, cx| {
20537                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20538                            editor.unfold_buffer(buffer_id, cx);
20539                        }
20540                    })
20541                    .ok();
20542            });
20543        }
20544    }
20545
20546    pub fn fold_selected_ranges(
20547        &mut self,
20548        _: &FoldSelectedRanges,
20549        window: &mut Window,
20550        cx: &mut Context<Self>,
20551    ) {
20552        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20553        let selections = self.selections.all_adjusted(&display_map);
20554        let ranges = selections
20555            .into_iter()
20556            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20557            .collect::<Vec<_>>();
20558        self.fold_creases(ranges, true, window, cx);
20559    }
20560
20561    pub fn fold_ranges<T: ToOffset + Clone>(
20562        &mut self,
20563        ranges: Vec<Range<T>>,
20564        auto_scroll: bool,
20565        window: &mut Window,
20566        cx: &mut Context<Self>,
20567    ) {
20568        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20569        let ranges = ranges
20570            .into_iter()
20571            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20572            .collect::<Vec<_>>();
20573        self.fold_creases(ranges, auto_scroll, window, cx);
20574    }
20575
20576    pub fn fold_creases<T: ToOffset + Clone>(
20577        &mut self,
20578        creases: Vec<Crease<T>>,
20579        auto_scroll: bool,
20580        window: &mut Window,
20581        cx: &mut Context<Self>,
20582    ) {
20583        if creases.is_empty() {
20584            return;
20585        }
20586
20587        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20588
20589        if auto_scroll {
20590            self.request_autoscroll(Autoscroll::fit(), cx);
20591        }
20592
20593        cx.notify();
20594
20595        self.scrollbar_marker_state.dirty = true;
20596        self.update_data_on_scroll(window, cx);
20597        self.folds_did_change(cx);
20598    }
20599
20600    /// Removes any folds whose ranges intersect any of the given ranges.
20601    pub fn unfold_ranges<T: ToOffset + Clone>(
20602        &mut self,
20603        ranges: &[Range<T>],
20604        inclusive: bool,
20605        auto_scroll: bool,
20606        cx: &mut Context<Self>,
20607    ) {
20608        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20609            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20610        });
20611        self.folds_did_change(cx);
20612    }
20613
20614    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20615        self.fold_buffers([buffer_id], cx);
20616    }
20617
20618    pub fn fold_buffers(
20619        &mut self,
20620        buffer_ids: impl IntoIterator<Item = BufferId>,
20621        cx: &mut Context<Self>,
20622    ) {
20623        if self.buffer().read(cx).is_singleton() {
20624            return;
20625        }
20626
20627        let ids_to_fold: Vec<BufferId> = buffer_ids
20628            .into_iter()
20629            .filter(|id| !self.is_buffer_folded(*id, cx))
20630            .collect();
20631
20632        if ids_to_fold.is_empty() {
20633            return;
20634        }
20635
20636        let mut all_folded_excerpt_ids = Vec::new();
20637        for buffer_id in &ids_to_fold {
20638            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20639            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20640        }
20641
20642        self.display_map.update(cx, |display_map, cx| {
20643            display_map.fold_buffers(ids_to_fold.clone(), cx)
20644        });
20645
20646        let snapshot = self.display_snapshot(cx);
20647        self.selections.change_with(&snapshot, |selections| {
20648            for buffer_id in ids_to_fold {
20649                selections.remove_selections_from_buffer(buffer_id);
20650            }
20651        });
20652
20653        cx.emit(EditorEvent::BufferFoldToggled {
20654            ids: all_folded_excerpt_ids,
20655            folded: true,
20656        });
20657        cx.notify();
20658    }
20659
20660    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20661        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20662            return;
20663        }
20664        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20665        self.display_map.update(cx, |display_map, cx| {
20666            display_map.unfold_buffers([buffer_id], cx);
20667        });
20668        cx.emit(EditorEvent::BufferFoldToggled {
20669            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20670            folded: false,
20671        });
20672        cx.notify();
20673    }
20674
20675    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20676        self.display_map.read(cx).is_buffer_folded(buffer)
20677    }
20678
20679    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20680        if self.buffer().read(cx).is_singleton() {
20681            return false;
20682        }
20683        !self.folded_buffers(cx).is_empty()
20684    }
20685
20686    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20687        self.display_map.read(cx).folded_buffers()
20688    }
20689
20690    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20691        self.display_map.update(cx, |display_map, cx| {
20692            display_map.disable_header_for_buffer(buffer_id, cx);
20693        });
20694        cx.notify();
20695    }
20696
20697    /// Removes any folds with the given ranges.
20698    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20699        &mut self,
20700        ranges: &[Range<T>],
20701        type_id: TypeId,
20702        auto_scroll: bool,
20703        cx: &mut Context<Self>,
20704    ) {
20705        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20706            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20707        });
20708        self.folds_did_change(cx);
20709    }
20710
20711    fn remove_folds_with<T: ToOffset + Clone>(
20712        &mut self,
20713        ranges: &[Range<T>],
20714        auto_scroll: bool,
20715        cx: &mut Context<Self>,
20716        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20717    ) {
20718        if ranges.is_empty() {
20719            return;
20720        }
20721
20722        let mut buffers_affected = HashSet::default();
20723        let multi_buffer = self.buffer().read(cx);
20724        for range in ranges {
20725            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20726                buffers_affected.insert(buffer.read(cx).remote_id());
20727            };
20728        }
20729
20730        self.display_map.update(cx, update);
20731
20732        if auto_scroll {
20733            self.request_autoscroll(Autoscroll::fit(), cx);
20734        }
20735
20736        cx.notify();
20737        self.scrollbar_marker_state.dirty = true;
20738        self.active_indent_guides_state.dirty = true;
20739    }
20740
20741    pub fn update_renderer_widths(
20742        &mut self,
20743        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20744        cx: &mut Context<Self>,
20745    ) -> bool {
20746        self.display_map
20747            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20748    }
20749
20750    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20751        self.display_map.read(cx).fold_placeholder.clone()
20752    }
20753
20754    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20755        self.buffer.update(cx, |buffer, cx| {
20756            buffer.set_all_diff_hunks_expanded(cx);
20757        });
20758    }
20759
20760    pub fn expand_all_diff_hunks(
20761        &mut self,
20762        _: &ExpandAllDiffHunks,
20763        _window: &mut Window,
20764        cx: &mut Context<Self>,
20765    ) {
20766        self.buffer.update(cx, |buffer, cx| {
20767            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20768        });
20769    }
20770
20771    pub fn collapse_all_diff_hunks(
20772        &mut self,
20773        _: &CollapseAllDiffHunks,
20774        _window: &mut Window,
20775        cx: &mut Context<Self>,
20776    ) {
20777        self.buffer.update(cx, |buffer, cx| {
20778            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20779        });
20780    }
20781
20782    pub fn toggle_selected_diff_hunks(
20783        &mut self,
20784        _: &ToggleSelectedDiffHunks,
20785        _window: &mut Window,
20786        cx: &mut Context<Self>,
20787    ) {
20788        let ranges: Vec<_> = self
20789            .selections
20790            .disjoint_anchors()
20791            .iter()
20792            .map(|s| s.range())
20793            .collect();
20794        self.toggle_diff_hunks_in_ranges(ranges, cx);
20795    }
20796
20797    pub fn diff_hunks_in_ranges<'a>(
20798        &'a self,
20799        ranges: &'a [Range<Anchor>],
20800        buffer: &'a MultiBufferSnapshot,
20801    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20802        ranges.iter().flat_map(move |range| {
20803            let end_excerpt_id = range.end.excerpt_id;
20804            let range = range.to_point(buffer);
20805            let mut peek_end = range.end;
20806            if range.end.row < buffer.max_row().0 {
20807                peek_end = Point::new(range.end.row + 1, 0);
20808            }
20809            buffer
20810                .diff_hunks_in_range(range.start..peek_end)
20811                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20812        })
20813    }
20814
20815    pub fn has_stageable_diff_hunks_in_ranges(
20816        &self,
20817        ranges: &[Range<Anchor>],
20818        snapshot: &MultiBufferSnapshot,
20819    ) -> bool {
20820        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20821        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20822    }
20823
20824    pub fn toggle_staged_selected_diff_hunks(
20825        &mut self,
20826        _: &::git::ToggleStaged,
20827        _: &mut Window,
20828        cx: &mut Context<Self>,
20829    ) {
20830        let snapshot = self.buffer.read(cx).snapshot(cx);
20831        let ranges: Vec<_> = self
20832            .selections
20833            .disjoint_anchors()
20834            .iter()
20835            .map(|s| s.range())
20836            .collect();
20837        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20838        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20839    }
20840
20841    pub fn set_render_diff_hunk_controls(
20842        &mut self,
20843        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20844        cx: &mut Context<Self>,
20845    ) {
20846        self.render_diff_hunk_controls = render_diff_hunk_controls;
20847        cx.notify();
20848    }
20849
20850    pub fn stage_and_next(
20851        &mut self,
20852        _: &::git::StageAndNext,
20853        window: &mut Window,
20854        cx: &mut Context<Self>,
20855    ) {
20856        self.do_stage_or_unstage_and_next(true, window, cx);
20857    }
20858
20859    pub fn unstage_and_next(
20860        &mut self,
20861        _: &::git::UnstageAndNext,
20862        window: &mut Window,
20863        cx: &mut Context<Self>,
20864    ) {
20865        self.do_stage_or_unstage_and_next(false, window, cx);
20866    }
20867
20868    pub fn stage_or_unstage_diff_hunks(
20869        &mut self,
20870        stage: bool,
20871        ranges: Vec<Range<Anchor>>,
20872        cx: &mut Context<Self>,
20873    ) {
20874        if self.delegate_stage_and_restore {
20875            let snapshot = self.buffer.read(cx).snapshot(cx);
20876            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20877            if !hunks.is_empty() {
20878                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20879            }
20880            return;
20881        }
20882        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20883        cx.spawn(async move |this, cx| {
20884            task.await?;
20885            this.update(cx, |this, cx| {
20886                let snapshot = this.buffer.read(cx).snapshot(cx);
20887                let chunk_by = this
20888                    .diff_hunks_in_ranges(&ranges, &snapshot)
20889                    .chunk_by(|hunk| hunk.buffer_id);
20890                for (buffer_id, hunks) in &chunk_by {
20891                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20892                }
20893            })
20894        })
20895        .detach_and_log_err(cx);
20896    }
20897
20898    fn save_buffers_for_ranges_if_needed(
20899        &mut self,
20900        ranges: &[Range<Anchor>],
20901        cx: &mut Context<Editor>,
20902    ) -> Task<Result<()>> {
20903        let multibuffer = self.buffer.read(cx);
20904        let snapshot = multibuffer.read(cx);
20905        let buffer_ids: HashSet<_> = ranges
20906            .iter()
20907            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20908            .collect();
20909        drop(snapshot);
20910
20911        let mut buffers = HashSet::default();
20912        for buffer_id in buffer_ids {
20913            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20914                let buffer = buffer_entity.read(cx);
20915                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20916                {
20917                    buffers.insert(buffer_entity);
20918                }
20919            }
20920        }
20921
20922        if let Some(project) = &self.project {
20923            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20924        } else {
20925            Task::ready(Ok(()))
20926        }
20927    }
20928
20929    fn do_stage_or_unstage_and_next(
20930        &mut self,
20931        stage: bool,
20932        window: &mut Window,
20933        cx: &mut Context<Self>,
20934    ) {
20935        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20936
20937        if ranges.iter().any(|range| range.start != range.end) {
20938            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20939            return;
20940        }
20941
20942        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20943
20944        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20945        let wrap_around = !all_diff_hunks_expanded;
20946        let snapshot = self.snapshot(window, cx);
20947        let position = self
20948            .selections
20949            .newest::<Point>(&snapshot.display_snapshot)
20950            .head();
20951
20952        self.go_to_hunk_before_or_after_position(
20953            &snapshot,
20954            position,
20955            Direction::Next,
20956            wrap_around,
20957            window,
20958            cx,
20959        );
20960    }
20961
20962    pub(crate) fn do_stage_or_unstage(
20963        &self,
20964        stage: bool,
20965        buffer_id: BufferId,
20966        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20967        cx: &mut App,
20968    ) -> Option<()> {
20969        let project = self.project()?;
20970        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20971        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20972        let buffer_snapshot = buffer.read(cx).snapshot();
20973        let file_exists = buffer_snapshot
20974            .file()
20975            .is_some_and(|file| file.disk_state().exists());
20976        diff.update(cx, |diff, cx| {
20977            diff.stage_or_unstage_hunks(
20978                stage,
20979                &hunks
20980                    .map(|hunk| buffer_diff::DiffHunk {
20981                        buffer_range: hunk.buffer_range,
20982                        // We don't need to pass in word diffs here because they're only used for rendering and
20983                        // this function changes internal state
20984                        base_word_diffs: Vec::default(),
20985                        buffer_word_diffs: Vec::default(),
20986                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20987                            ..hunk.diff_base_byte_range.end.0,
20988                        secondary_status: hunk.status.secondary,
20989                        range: Point::zero()..Point::zero(), // unused
20990                    })
20991                    .collect::<Vec<_>>(),
20992                &buffer_snapshot,
20993                file_exists,
20994                cx,
20995            )
20996        });
20997        None
20998    }
20999
21000    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
21001        let ranges: Vec<_> = self
21002            .selections
21003            .disjoint_anchors()
21004            .iter()
21005            .map(|s| s.range())
21006            .collect();
21007        self.buffer
21008            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
21009    }
21010
21011    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
21012        self.buffer.update(cx, |buffer, cx| {
21013            let ranges = vec![Anchor::min()..Anchor::max()];
21014            if !buffer.all_diff_hunks_expanded()
21015                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
21016            {
21017                buffer.collapse_diff_hunks(ranges, cx);
21018                true
21019            } else {
21020                false
21021            }
21022        })
21023    }
21024
21025    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
21026        if self.buffer.read(cx).all_diff_hunks_expanded() {
21027            return true;
21028        }
21029        let ranges = vec![Anchor::min()..Anchor::max()];
21030        self.buffer
21031            .read(cx)
21032            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
21033    }
21034
21035    fn toggle_diff_hunks_in_ranges(
21036        &mut self,
21037        ranges: Vec<Range<Anchor>>,
21038        cx: &mut Context<Editor>,
21039    ) {
21040        self.buffer.update(cx, |buffer, cx| {
21041            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
21042            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
21043        })
21044    }
21045
21046    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
21047        self.buffer.update(cx, |buffer, cx| {
21048            buffer.toggle_single_diff_hunk(range, cx);
21049        })
21050    }
21051
21052    pub(crate) fn apply_all_diff_hunks(
21053        &mut self,
21054        _: &ApplyAllDiffHunks,
21055        window: &mut Window,
21056        cx: &mut Context<Self>,
21057    ) {
21058        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21059
21060        let buffers = self.buffer.read(cx).all_buffers();
21061        for branch_buffer in buffers {
21062            branch_buffer.update(cx, |branch_buffer, cx| {
21063                branch_buffer.merge_into_base(Vec::new(), cx);
21064            });
21065        }
21066
21067        if let Some(project) = self.project.clone() {
21068            self.save(
21069                SaveOptions {
21070                    format: true,
21071                    autosave: false,
21072                },
21073                project,
21074                window,
21075                cx,
21076            )
21077            .detach_and_log_err(cx);
21078        }
21079    }
21080
21081    pub(crate) fn apply_selected_diff_hunks(
21082        &mut self,
21083        _: &ApplyDiffHunk,
21084        window: &mut Window,
21085        cx: &mut Context<Self>,
21086    ) {
21087        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21088        let snapshot = self.snapshot(window, cx);
21089        let hunks = snapshot.hunks_for_ranges(
21090            self.selections
21091                .all(&snapshot.display_snapshot)
21092                .into_iter()
21093                .map(|selection| selection.range()),
21094        );
21095        let mut ranges_by_buffer = HashMap::default();
21096        self.transact(window, cx, |editor, _window, cx| {
21097            for hunk in hunks {
21098                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
21099                    ranges_by_buffer
21100                        .entry(buffer.clone())
21101                        .or_insert_with(Vec::new)
21102                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
21103                }
21104            }
21105
21106            for (buffer, ranges) in ranges_by_buffer {
21107                buffer.update(cx, |buffer, cx| {
21108                    buffer.merge_into_base(ranges, cx);
21109                });
21110            }
21111        });
21112
21113        if let Some(project) = self.project.clone() {
21114            self.save(
21115                SaveOptions {
21116                    format: true,
21117                    autosave: false,
21118                },
21119                project,
21120                window,
21121                cx,
21122            )
21123            .detach_and_log_err(cx);
21124        }
21125    }
21126
21127    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
21128        if hovered != self.gutter_hovered {
21129            self.gutter_hovered = hovered;
21130            cx.notify();
21131        }
21132    }
21133
21134    pub fn insert_blocks(
21135        &mut self,
21136        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
21137        autoscroll: Option<Autoscroll>,
21138        cx: &mut Context<Self>,
21139    ) -> Vec<CustomBlockId> {
21140        let blocks = self
21141            .display_map
21142            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
21143        if let Some(autoscroll) = autoscroll {
21144            self.request_autoscroll(autoscroll, cx);
21145        }
21146        cx.notify();
21147        blocks
21148    }
21149
21150    pub fn resize_blocks(
21151        &mut self,
21152        heights: HashMap<CustomBlockId, u32>,
21153        autoscroll: Option<Autoscroll>,
21154        cx: &mut Context<Self>,
21155    ) {
21156        self.display_map
21157            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21158        if let Some(autoscroll) = autoscroll {
21159            self.request_autoscroll(autoscroll, cx);
21160        }
21161        cx.notify();
21162    }
21163
21164    pub fn replace_blocks(
21165        &mut self,
21166        renderers: HashMap<CustomBlockId, RenderBlock>,
21167        autoscroll: Option<Autoscroll>,
21168        cx: &mut Context<Self>,
21169    ) {
21170        self.display_map
21171            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21172        if let Some(autoscroll) = autoscroll {
21173            self.request_autoscroll(autoscroll, cx);
21174        }
21175        cx.notify();
21176    }
21177
21178    pub fn remove_blocks(
21179        &mut self,
21180        block_ids: HashSet<CustomBlockId>,
21181        autoscroll: Option<Autoscroll>,
21182        cx: &mut Context<Self>,
21183    ) {
21184        self.display_map.update(cx, |display_map, cx| {
21185            display_map.remove_blocks(block_ids, cx)
21186        });
21187        if let Some(autoscroll) = autoscroll {
21188            self.request_autoscroll(autoscroll, cx);
21189        }
21190        cx.notify();
21191    }
21192
21193    pub fn row_for_block(
21194        &self,
21195        block_id: CustomBlockId,
21196        cx: &mut Context<Self>,
21197    ) -> Option<DisplayRow> {
21198        self.display_map
21199            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21200    }
21201
21202    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21203        self.focused_block = Some(focused_block);
21204    }
21205
21206    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21207        self.focused_block.take()
21208    }
21209
21210    pub fn insert_creases(
21211        &mut self,
21212        creases: impl IntoIterator<Item = Crease<Anchor>>,
21213        cx: &mut Context<Self>,
21214    ) -> Vec<CreaseId> {
21215        self.display_map
21216            .update(cx, |map, cx| map.insert_creases(creases, cx))
21217    }
21218
21219    pub fn remove_creases(
21220        &mut self,
21221        ids: impl IntoIterator<Item = CreaseId>,
21222        cx: &mut Context<Self>,
21223    ) -> Vec<(CreaseId, Range<Anchor>)> {
21224        self.display_map
21225            .update(cx, |map, cx| map.remove_creases(ids, cx))
21226    }
21227
21228    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21229        self.display_map
21230            .update(cx, |map, cx| map.snapshot(cx))
21231            .longest_row()
21232    }
21233
21234    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21235        self.display_map
21236            .update(cx, |map, cx| map.snapshot(cx))
21237            .max_point()
21238    }
21239
21240    pub fn text(&self, cx: &App) -> String {
21241        self.buffer.read(cx).read(cx).text()
21242    }
21243
21244    pub fn is_empty(&self, cx: &App) -> bool {
21245        self.buffer.read(cx).read(cx).is_empty()
21246    }
21247
21248    pub fn text_option(&self, cx: &App) -> Option<String> {
21249        let text = self.text(cx);
21250        let text = text.trim();
21251
21252        if text.is_empty() {
21253            return None;
21254        }
21255
21256        Some(text.to_string())
21257    }
21258
21259    pub fn set_text(
21260        &mut self,
21261        text: impl Into<Arc<str>>,
21262        window: &mut Window,
21263        cx: &mut Context<Self>,
21264    ) {
21265        self.transact(window, cx, |this, _, cx| {
21266            this.buffer
21267                .read(cx)
21268                .as_singleton()
21269                .expect("you can only call set_text on editors for singleton buffers")
21270                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21271        });
21272    }
21273
21274    pub fn display_text(&self, cx: &mut App) -> String {
21275        self.display_map
21276            .update(cx, |map, cx| map.snapshot(cx))
21277            .text()
21278    }
21279
21280    fn create_minimap(
21281        &self,
21282        minimap_settings: MinimapSettings,
21283        window: &mut Window,
21284        cx: &mut Context<Self>,
21285    ) -> Option<Entity<Self>> {
21286        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21287            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21288    }
21289
21290    fn initialize_new_minimap(
21291        &self,
21292        minimap_settings: MinimapSettings,
21293        window: &mut Window,
21294        cx: &mut Context<Self>,
21295    ) -> Entity<Self> {
21296        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21297        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21298
21299        let mut minimap = Editor::new_internal(
21300            EditorMode::Minimap {
21301                parent: cx.weak_entity(),
21302            },
21303            self.buffer.clone(),
21304            None,
21305            Some(self.display_map.clone()),
21306            window,
21307            cx,
21308        );
21309        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21310        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21311        minimap.scroll_manager.clone_state(
21312            &self.scroll_manager,
21313            &my_snapshot,
21314            &minimap_snapshot,
21315            cx,
21316        );
21317        minimap.set_text_style_refinement(TextStyleRefinement {
21318            font_size: Some(MINIMAP_FONT_SIZE),
21319            font_weight: Some(MINIMAP_FONT_WEIGHT),
21320            font_family: Some(MINIMAP_FONT_FAMILY),
21321            ..Default::default()
21322        });
21323        minimap.update_minimap_configuration(minimap_settings, cx);
21324        cx.new(|_| minimap)
21325    }
21326
21327    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21328        let current_line_highlight = minimap_settings
21329            .current_line_highlight
21330            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21331        self.set_current_line_highlight(Some(current_line_highlight));
21332    }
21333
21334    pub fn minimap(&self) -> Option<&Entity<Self>> {
21335        self.minimap
21336            .as_ref()
21337            .filter(|_| self.minimap_visibility.visible())
21338    }
21339
21340    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21341        let mut wrap_guides = smallvec![];
21342
21343        if self.show_wrap_guides == Some(false) {
21344            return wrap_guides;
21345        }
21346
21347        let settings = self.buffer.read(cx).language_settings(cx);
21348        if settings.show_wrap_guides {
21349            match self.soft_wrap_mode(cx) {
21350                SoftWrap::Column(soft_wrap) => {
21351                    wrap_guides.push((soft_wrap as usize, true));
21352                }
21353                SoftWrap::Bounded(soft_wrap) => {
21354                    wrap_guides.push((soft_wrap as usize, true));
21355                }
21356                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21357            }
21358            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21359        }
21360
21361        wrap_guides
21362    }
21363
21364    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21365        let settings = self.buffer.read(cx).language_settings(cx);
21366        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21367        match mode {
21368            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21369                SoftWrap::None
21370            }
21371            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21372            language_settings::SoftWrap::PreferredLineLength => {
21373                SoftWrap::Column(settings.preferred_line_length)
21374            }
21375            language_settings::SoftWrap::Bounded => {
21376                SoftWrap::Bounded(settings.preferred_line_length)
21377            }
21378        }
21379    }
21380
21381    pub fn set_soft_wrap_mode(
21382        &mut self,
21383        mode: language_settings::SoftWrap,
21384        cx: &mut Context<Self>,
21385    ) {
21386        self.soft_wrap_mode_override = Some(mode);
21387        cx.notify();
21388    }
21389
21390    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21391        self.hard_wrap = hard_wrap;
21392        cx.notify();
21393    }
21394
21395    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21396        self.text_style_refinement = Some(style);
21397    }
21398
21399    /// called by the Element so we know what style we were most recently rendered with.
21400    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21401        // We intentionally do not inform the display map about the minimap style
21402        // so that wrapping is not recalculated and stays consistent for the editor
21403        // and its linked minimap.
21404        if !self.mode.is_minimap() {
21405            let font = style.text.font();
21406            let font_size = style.text.font_size.to_pixels(window.rem_size());
21407            let display_map = self
21408                .placeholder_display_map
21409                .as_ref()
21410                .filter(|_| self.is_empty(cx))
21411                .unwrap_or(&self.display_map);
21412
21413            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21414        }
21415        self.style = Some(style);
21416    }
21417
21418    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21419        if self.style.is_none() {
21420            self.style = Some(self.create_style(cx));
21421        }
21422        self.style.as_ref().unwrap()
21423    }
21424
21425    // Called by the element. This method is not designed to be called outside of the editor
21426    // element's layout code because it does not notify when rewrapping is computed synchronously.
21427    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21428        if self.is_empty(cx) {
21429            self.placeholder_display_map
21430                .as_ref()
21431                .map_or(false, |display_map| {
21432                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21433                })
21434        } else {
21435            self.display_map
21436                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21437        }
21438    }
21439
21440    pub fn set_soft_wrap(&mut self) {
21441        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21442    }
21443
21444    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21445        if self.soft_wrap_mode_override.is_some() {
21446            self.soft_wrap_mode_override.take();
21447        } else {
21448            let soft_wrap = match self.soft_wrap_mode(cx) {
21449                SoftWrap::GitDiff => return,
21450                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21451                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21452                    language_settings::SoftWrap::None
21453                }
21454            };
21455            self.soft_wrap_mode_override = Some(soft_wrap);
21456        }
21457        cx.notify();
21458    }
21459
21460    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21461        let Some(workspace) = self.workspace() else {
21462            return;
21463        };
21464        let fs = workspace.read(cx).app_state().fs.clone();
21465        let current_show = TabBarSettings::get_global(cx).show;
21466        update_settings_file(fs, cx, move |setting, _| {
21467            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21468        });
21469    }
21470
21471    pub fn toggle_indent_guides(
21472        &mut self,
21473        _: &ToggleIndentGuides,
21474        _: &mut Window,
21475        cx: &mut Context<Self>,
21476    ) {
21477        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21478            self.buffer
21479                .read(cx)
21480                .language_settings(cx)
21481                .indent_guides
21482                .enabled
21483        });
21484        self.show_indent_guides = Some(!currently_enabled);
21485        cx.notify();
21486    }
21487
21488    fn should_show_indent_guides(&self) -> Option<bool> {
21489        self.show_indent_guides
21490    }
21491
21492    pub fn disable_indent_guides_for_buffer(
21493        &mut self,
21494        buffer_id: BufferId,
21495        cx: &mut Context<Self>,
21496    ) {
21497        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21498        cx.notify();
21499    }
21500
21501    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21502        self.buffers_with_disabled_indent_guides
21503            .contains(&buffer_id)
21504    }
21505
21506    pub fn toggle_line_numbers(
21507        &mut self,
21508        _: &ToggleLineNumbers,
21509        _: &mut Window,
21510        cx: &mut Context<Self>,
21511    ) {
21512        let mut editor_settings = EditorSettings::get_global(cx).clone();
21513        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21514        EditorSettings::override_global(editor_settings, cx);
21515    }
21516
21517    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21518        if let Some(show_line_numbers) = self.show_line_numbers {
21519            return show_line_numbers;
21520        }
21521        EditorSettings::get_global(cx).gutter.line_numbers
21522    }
21523
21524    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21525        match (
21526            self.use_relative_line_numbers,
21527            EditorSettings::get_global(cx).relative_line_numbers,
21528        ) {
21529            (None, setting) => setting,
21530            (Some(false), _) => RelativeLineNumbers::Disabled,
21531            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21532            (Some(true), _) => RelativeLineNumbers::Enabled,
21533        }
21534    }
21535
21536    pub fn toggle_relative_line_numbers(
21537        &mut self,
21538        _: &ToggleRelativeLineNumbers,
21539        _: &mut Window,
21540        cx: &mut Context<Self>,
21541    ) {
21542        let is_relative = self.relative_line_numbers(cx);
21543        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21544    }
21545
21546    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21547        self.use_relative_line_numbers = is_relative;
21548        cx.notify();
21549    }
21550
21551    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21552        self.show_gutter = show_gutter;
21553        cx.notify();
21554    }
21555
21556    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21557        self.show_scrollbars = ScrollbarAxes {
21558            horizontal: show,
21559            vertical: show,
21560        };
21561        cx.notify();
21562    }
21563
21564    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21565        self.show_scrollbars.vertical = show;
21566        cx.notify();
21567    }
21568
21569    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21570        self.show_scrollbars.horizontal = show;
21571        cx.notify();
21572    }
21573
21574    pub fn set_minimap_visibility(
21575        &mut self,
21576        minimap_visibility: MinimapVisibility,
21577        window: &mut Window,
21578        cx: &mut Context<Self>,
21579    ) {
21580        if self.minimap_visibility != minimap_visibility {
21581            if minimap_visibility.visible() && self.minimap.is_none() {
21582                let minimap_settings = EditorSettings::get_global(cx).minimap;
21583                self.minimap =
21584                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21585            }
21586            self.minimap_visibility = minimap_visibility;
21587            cx.notify();
21588        }
21589    }
21590
21591    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21592        self.set_show_scrollbars(false, cx);
21593        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21594    }
21595
21596    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21597        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21598    }
21599
21600    /// Normally the text in full mode and auto height editors is padded on the
21601    /// left side by roughly half a character width for improved hit testing.
21602    ///
21603    /// Use this method to disable this for cases where this is not wanted (e.g.
21604    /// if you want to align the editor text with some other text above or below)
21605    /// or if you want to add this padding to single-line editors.
21606    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21607        self.offset_content = offset_content;
21608        cx.notify();
21609    }
21610
21611    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21612        self.show_line_numbers = Some(show_line_numbers);
21613        cx.notify();
21614    }
21615
21616    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21617        self.disable_expand_excerpt_buttons = true;
21618        cx.notify();
21619    }
21620
21621    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21622        self.number_deleted_lines = number;
21623        cx.notify();
21624    }
21625
21626    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21627        self.delegate_expand_excerpts = delegate;
21628    }
21629
21630    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21631        self.delegate_stage_and_restore = delegate;
21632    }
21633
21634    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21635        self.delegate_open_excerpts = delegate;
21636    }
21637
21638    pub fn set_on_local_selections_changed(
21639        &mut self,
21640        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21641    ) {
21642        self.on_local_selections_changed = callback;
21643    }
21644
21645    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21646        self.suppress_selection_callback = suppress;
21647    }
21648
21649    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21650        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21651        cx.notify();
21652    }
21653
21654    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21655        self.show_code_actions = Some(show_code_actions);
21656        cx.notify();
21657    }
21658
21659    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21660        self.show_runnables = Some(show_runnables);
21661        cx.notify();
21662    }
21663
21664    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21665        self.show_breakpoints = Some(show_breakpoints);
21666        cx.notify();
21667    }
21668
21669    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21670        self.show_diff_review_button = show;
21671        cx.notify();
21672    }
21673
21674    pub fn show_diff_review_button(&self) -> bool {
21675        self.show_diff_review_button
21676    }
21677
21678    pub fn render_diff_review_button(
21679        &self,
21680        display_row: DisplayRow,
21681        width: Pixels,
21682        cx: &mut Context<Self>,
21683    ) -> impl IntoElement {
21684        let text_color = cx.theme().colors().text;
21685        let icon_color = cx.theme().colors().icon_accent;
21686
21687        h_flex()
21688            .id("diff_review_button")
21689            .cursor_pointer()
21690            .w(width - px(1.))
21691            .h(relative(0.9))
21692            .justify_center()
21693            .rounded_sm()
21694            .border_1()
21695            .border_color(text_color.opacity(0.1))
21696            .bg(text_color.opacity(0.15))
21697            .hover(|s| {
21698                s.bg(icon_color.opacity(0.4))
21699                    .border_color(icon_color.opacity(0.5))
21700            })
21701            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21702            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21703            .on_mouse_down(
21704                gpui::MouseButton::Left,
21705                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21706                    editor.start_diff_review_drag(display_row, window, cx);
21707                }),
21708            )
21709    }
21710
21711    pub fn start_diff_review_drag(
21712        &mut self,
21713        display_row: DisplayRow,
21714        window: &mut Window,
21715        cx: &mut Context<Self>,
21716    ) {
21717        let snapshot = self.snapshot(window, cx);
21718        let point = snapshot
21719            .display_snapshot
21720            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21721        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21722        self.diff_review_drag_state = Some(DiffReviewDragState {
21723            start_anchor: anchor,
21724            current_anchor: anchor,
21725        });
21726        cx.notify();
21727    }
21728
21729    pub fn update_diff_review_drag(
21730        &mut self,
21731        display_row: DisplayRow,
21732        window: &mut Window,
21733        cx: &mut Context<Self>,
21734    ) {
21735        if self.diff_review_drag_state.is_none() {
21736            return;
21737        }
21738        let snapshot = self.snapshot(window, cx);
21739        let point = snapshot
21740            .display_snapshot
21741            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21742        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21743        if let Some(drag_state) = &mut self.diff_review_drag_state {
21744            drag_state.current_anchor = anchor;
21745            cx.notify();
21746        }
21747    }
21748
21749    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21750        if let Some(drag_state) = self.diff_review_drag_state.take() {
21751            let snapshot = self.snapshot(window, cx);
21752            let range = drag_state.row_range(&snapshot.display_snapshot);
21753            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21754        }
21755        cx.notify();
21756    }
21757
21758    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21759        self.diff_review_drag_state = None;
21760        cx.notify();
21761    }
21762
21763    /// Calculates the appropriate block height for the diff review overlay.
21764    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21765    /// and 2 lines per comment when expanded.
21766    fn calculate_overlay_height(
21767        &self,
21768        hunk_key: &DiffHunkKey,
21769        comments_expanded: bool,
21770        snapshot: &MultiBufferSnapshot,
21771    ) -> u32 {
21772        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21773        let base_height: u32 = 2; // Input row with avatar and buttons
21774
21775        if comment_count == 0 {
21776            base_height
21777        } else if comments_expanded {
21778            // Header (1 line) + 2 lines per comment
21779            base_height + 1 + (comment_count as u32 * 2)
21780        } else {
21781            // Just header when collapsed
21782            base_height + 1
21783        }
21784    }
21785
21786    pub fn show_diff_review_overlay(
21787        &mut self,
21788        display_range: Range<DisplayRow>,
21789        window: &mut Window,
21790        cx: &mut Context<Self>,
21791    ) {
21792        let Range { start, end } = display_range.sorted();
21793
21794        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21795        let editor_snapshot = self.snapshot(window, cx);
21796
21797        // Convert display rows to multibuffer points
21798        let start_point = editor_snapshot
21799            .display_snapshot
21800            .display_point_to_point(start.as_display_point(), Bias::Left);
21801        let end_point = editor_snapshot
21802            .display_snapshot
21803            .display_point_to_point(end.as_display_point(), Bias::Left);
21804        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21805
21806        // Create anchor range for the selected lines (start of first line to end of last line)
21807        let line_end = Point::new(
21808            end_point.row,
21809            buffer_snapshot.line_len(end_multi_buffer_row),
21810        );
21811        let anchor_range =
21812            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21813
21814        // Compute the hunk key for this display row
21815        let file_path = buffer_snapshot
21816            .file_at(start_point)
21817            .map(|file: &Arc<dyn language::File>| file.path().clone())
21818            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21819        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21820        let new_hunk_key = DiffHunkKey {
21821            file_path,
21822            hunk_start_anchor,
21823        };
21824
21825        // Check if we already have an overlay for this hunk
21826        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21827            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21828        }) {
21829            // Just focus the existing overlay's prompt editor
21830            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21831            window.focus(&focus_handle, cx);
21832            return;
21833        }
21834
21835        // Dismiss overlays that have no comments for their hunks
21836        self.dismiss_overlays_without_comments(cx);
21837
21838        // Get the current user's avatar URI from the project's user_store
21839        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21840            let user_store = project.read(cx).user_store();
21841            user_store
21842                .read(cx)
21843                .current_user()
21844                .map(|user| user.avatar_uri.clone())
21845        });
21846
21847        // Create anchor at the end of the last row so the block appears immediately below it
21848        // Use multibuffer coordinates for anchor creation
21849        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21850        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21851
21852        // Use the hunk key we already computed
21853        let hunk_key = new_hunk_key;
21854
21855        // Create the prompt editor for the review input
21856        let prompt_editor = cx.new(|cx| {
21857            let mut editor = Editor::single_line(window, cx);
21858            editor.set_placeholder_text("Add a review comment...", window, cx);
21859            editor
21860        });
21861
21862        // Register the Newline action on the prompt editor to submit the review
21863        let parent_editor = cx.entity().downgrade();
21864        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21865            prompt_editor.register_action({
21866                let parent_editor = parent_editor.clone();
21867                move |_: &crate::actions::Newline, window, cx| {
21868                    if let Some(editor) = parent_editor.upgrade() {
21869                        editor.update(cx, |editor, cx| {
21870                            editor.submit_diff_review_comment(window, cx);
21871                        });
21872                    }
21873                }
21874            })
21875        });
21876
21877        // Calculate initial height based on existing comments for this hunk
21878        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21879
21880        // Create the overlay block
21881        let prompt_editor_for_render = prompt_editor.clone();
21882        let hunk_key_for_render = hunk_key.clone();
21883        let editor_handle = cx.entity().downgrade();
21884        let block = BlockProperties {
21885            style: BlockStyle::Sticky,
21886            placement: BlockPlacement::Below(anchor),
21887            height: Some(initial_height),
21888            render: Arc::new(move |cx| {
21889                Self::render_diff_review_overlay(
21890                    &prompt_editor_for_render,
21891                    &hunk_key_for_render,
21892                    &editor_handle,
21893                    cx,
21894                )
21895            }),
21896            priority: 0,
21897        };
21898
21899        let block_ids = self.insert_blocks([block], None, cx);
21900        let Some(block_id) = block_ids.into_iter().next() else {
21901            log::error!("Failed to insert diff review overlay block");
21902            return;
21903        };
21904
21905        self.diff_review_overlays.push(DiffReviewOverlay {
21906            anchor_range,
21907            block_id,
21908            prompt_editor: prompt_editor.clone(),
21909            hunk_key,
21910            comments_expanded: true,
21911            inline_edit_editors: HashMap::default(),
21912            inline_edit_subscriptions: HashMap::default(),
21913            user_avatar_uri,
21914            _subscription: subscription,
21915        });
21916
21917        // Focus the prompt editor
21918        let focus_handle = prompt_editor.focus_handle(cx);
21919        window.focus(&focus_handle, cx);
21920
21921        cx.notify();
21922    }
21923
21924    /// Dismisses all diff review overlays.
21925    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21926        if self.diff_review_overlays.is_empty() {
21927            return;
21928        }
21929        let block_ids: HashSet<_> = self
21930            .diff_review_overlays
21931            .drain(..)
21932            .map(|overlay| overlay.block_id)
21933            .collect();
21934        self.remove_blocks(block_ids, None, cx);
21935        cx.notify();
21936    }
21937
21938    /// Dismisses overlays that have no comments stored for their hunks.
21939    /// Keeps overlays that have at least one comment.
21940    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21941        let snapshot = self.buffer.read(cx).snapshot(cx);
21942
21943        // First, compute which overlays have comments (to avoid borrow issues with retain)
21944        let overlays_with_comments: Vec<bool> = self
21945            .diff_review_overlays
21946            .iter()
21947            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21948            .collect();
21949
21950        // Now collect block IDs to remove and retain overlays
21951        let mut block_ids_to_remove = HashSet::default();
21952        let mut index = 0;
21953        self.diff_review_overlays.retain(|overlay| {
21954            let has_comments = overlays_with_comments[index];
21955            index += 1;
21956            if !has_comments {
21957                block_ids_to_remove.insert(overlay.block_id);
21958            }
21959            has_comments
21960        });
21961
21962        if !block_ids_to_remove.is_empty() {
21963            self.remove_blocks(block_ids_to_remove, None, cx);
21964            cx.notify();
21965        }
21966    }
21967
21968    /// Refreshes the diff review overlay block to update its height and render function.
21969    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21970    fn refresh_diff_review_overlay_height(
21971        &mut self,
21972        hunk_key: &DiffHunkKey,
21973        _window: &mut Window,
21974        cx: &mut Context<Self>,
21975    ) {
21976        // Extract all needed data from overlay first to avoid borrow conflicts
21977        let snapshot = self.buffer.read(cx).snapshot(cx);
21978        let (comments_expanded, block_id, prompt_editor) = {
21979            let Some(overlay) = self
21980                .diff_review_overlays
21981                .iter()
21982                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21983            else {
21984                return;
21985            };
21986
21987            (
21988                overlay.comments_expanded,
21989                overlay.block_id,
21990                overlay.prompt_editor.clone(),
21991            )
21992        };
21993
21994        // Calculate new height
21995        let snapshot = self.buffer.read(cx).snapshot(cx);
21996        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21997
21998        // Update the block height using resize_blocks (avoids flicker)
21999        let mut heights = HashMap::default();
22000        heights.insert(block_id, new_height);
22001        self.resize_blocks(heights, None, cx);
22002
22003        // Update the render function using replace_blocks (avoids flicker)
22004        let hunk_key_for_render = hunk_key.clone();
22005        let editor_handle = cx.entity().downgrade();
22006        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
22007            Arc::new(move |cx| {
22008                Self::render_diff_review_overlay(
22009                    &prompt_editor,
22010                    &hunk_key_for_render,
22011                    &editor_handle,
22012                    cx,
22013                )
22014            });
22015
22016        let mut renderers = HashMap::default();
22017        renderers.insert(block_id, render);
22018        self.replace_blocks(renderers, None, cx);
22019    }
22020
22021    /// Action handler for SubmitDiffReviewComment.
22022    pub fn submit_diff_review_comment_action(
22023        &mut self,
22024        _: &SubmitDiffReviewComment,
22025        window: &mut Window,
22026        cx: &mut Context<Self>,
22027    ) {
22028        self.submit_diff_review_comment(window, cx);
22029    }
22030
22031    /// Stores the diff review comment locally.
22032    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
22033    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
22034        // Find the overlay that currently has focus
22035        let overlay_index = self
22036            .diff_review_overlays
22037            .iter()
22038            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
22039        let Some(overlay_index) = overlay_index else {
22040            return;
22041        };
22042        let overlay = &self.diff_review_overlays[overlay_index];
22043
22044        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
22045        if comment_text.is_empty() {
22046            return;
22047        }
22048
22049        let anchor_range = overlay.anchor_range.clone();
22050        let hunk_key = overlay.hunk_key.clone();
22051
22052        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
22053
22054        // Clear the prompt editor but keep the overlay open
22055        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
22056            overlay.prompt_editor.update(cx, |editor, cx| {
22057                editor.clear(window, cx);
22058            });
22059        }
22060
22061        // Refresh the overlay to update the block height for the new comment
22062        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22063
22064        cx.notify();
22065    }
22066
22067    /// Returns the prompt editor for the diff review overlay, if one is active.
22068    /// This is primarily used for testing.
22069    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
22070        self.diff_review_overlays
22071            .first()
22072            .map(|overlay| &overlay.prompt_editor)
22073    }
22074
22075    /// Returns the line range for the first diff review overlay, if one is active.
22076    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
22077    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
22078        let overlay = self.diff_review_overlays.first()?;
22079        let snapshot = self.buffer.read(cx).snapshot(cx);
22080        let start_point = overlay.anchor_range.start.to_point(&snapshot);
22081        let end_point = overlay.anchor_range.end.to_point(&snapshot);
22082        let start_row = snapshot
22083            .point_to_buffer_point(start_point)
22084            .map(|(_, p, _)| p.row)
22085            .unwrap_or(start_point.row);
22086        let end_row = snapshot
22087            .point_to_buffer_point(end_point)
22088            .map(|(_, p, _)| p.row)
22089            .unwrap_or(end_point.row);
22090        Some((start_row, end_row))
22091    }
22092
22093    /// Sets whether the comments section is expanded in the diff review overlay.
22094    /// This is primarily used for testing.
22095    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
22096        for overlay in &mut self.diff_review_overlays {
22097            overlay.comments_expanded = expanded;
22098        }
22099        cx.notify();
22100    }
22101
22102    /// Compares two DiffHunkKeys for equality by resolving their anchors.
22103    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
22104        a.file_path == b.file_path
22105            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
22106    }
22107
22108    /// Returns comments for a specific hunk, ordered by creation time.
22109    pub fn comments_for_hunk<'a>(
22110        &'a self,
22111        key: &DiffHunkKey,
22112        snapshot: &MultiBufferSnapshot,
22113    ) -> &'a [StoredReviewComment] {
22114        let key_point = key.hunk_start_anchor.to_point(snapshot);
22115        self.stored_review_comments
22116            .iter()
22117            .find(|(k, _)| {
22118                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22119            })
22120            .map(|(_, comments)| comments.as_slice())
22121            .unwrap_or(&[])
22122    }
22123
22124    /// Returns the total count of stored review comments across all hunks.
22125    pub fn total_review_comment_count(&self) -> usize {
22126        self.stored_review_comments
22127            .iter()
22128            .map(|(_, v)| v.len())
22129            .sum()
22130    }
22131
22132    /// Returns the count of comments for a specific hunk.
22133    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
22134        let key_point = key.hunk_start_anchor.to_point(snapshot);
22135        self.stored_review_comments
22136            .iter()
22137            .find(|(k, _)| {
22138                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22139            })
22140            .map(|(_, v)| v.len())
22141            .unwrap_or(0)
22142    }
22143
22144    /// Adds a new review comment to a specific hunk.
22145    pub fn add_review_comment(
22146        &mut self,
22147        hunk_key: DiffHunkKey,
22148        comment: String,
22149        anchor_range: Range<Anchor>,
22150        cx: &mut Context<Self>,
22151    ) -> usize {
22152        let id = self.next_review_comment_id;
22153        self.next_review_comment_id += 1;
22154
22155        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22156
22157        let snapshot = self.buffer.read(cx).snapshot(cx);
22158        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22159
22160        // Find existing entry for this hunk or add a new one
22161        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22162            k.file_path == hunk_key.file_path
22163                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22164        }) {
22165            comments.push(stored_comment);
22166        } else {
22167            self.stored_review_comments
22168                .push((hunk_key, vec![stored_comment]));
22169        }
22170
22171        cx.emit(EditorEvent::ReviewCommentsChanged {
22172            total_count: self.total_review_comment_count(),
22173        });
22174        cx.notify();
22175        id
22176    }
22177
22178    /// Removes a review comment by ID from any hunk.
22179    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22180        for (_, comments) in self.stored_review_comments.iter_mut() {
22181            if let Some(index) = comments.iter().position(|c| c.id == id) {
22182                comments.remove(index);
22183                cx.emit(EditorEvent::ReviewCommentsChanged {
22184                    total_count: self.total_review_comment_count(),
22185                });
22186                cx.notify();
22187                return true;
22188            }
22189        }
22190        false
22191    }
22192
22193    /// Updates a review comment's text by ID.
22194    pub fn update_review_comment(
22195        &mut self,
22196        id: usize,
22197        new_comment: String,
22198        cx: &mut Context<Self>,
22199    ) -> bool {
22200        for (_, comments) in self.stored_review_comments.iter_mut() {
22201            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22202                comment.comment = new_comment;
22203                comment.is_editing = false;
22204                cx.emit(EditorEvent::ReviewCommentsChanged {
22205                    total_count: self.total_review_comment_count(),
22206                });
22207                cx.notify();
22208                return true;
22209            }
22210        }
22211        false
22212    }
22213
22214    /// Sets a comment's editing state.
22215    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22216        for (_, comments) in self.stored_review_comments.iter_mut() {
22217            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22218                comment.is_editing = is_editing;
22219                cx.notify();
22220                return;
22221            }
22222        }
22223    }
22224
22225    /// Takes all stored comments from all hunks, clearing the storage.
22226    /// Returns a Vec of (hunk_key, comments) pairs.
22227    pub fn take_all_review_comments(
22228        &mut self,
22229        cx: &mut Context<Self>,
22230    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22231        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22232        self.dismiss_all_diff_review_overlays(cx);
22233        let comments = std::mem::take(&mut self.stored_review_comments);
22234        // Reset the ID counter since all comments have been taken
22235        self.next_review_comment_id = 0;
22236        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22237        cx.notify();
22238        comments
22239    }
22240
22241    /// Removes review comments whose anchors are no longer valid or whose
22242    /// associated diff hunks no longer exist.
22243    ///
22244    /// This should be called when the buffer changes to prevent orphaned comments
22245    /// from accumulating.
22246    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22247        let snapshot = self.buffer.read(cx).snapshot(cx);
22248        let original_count = self.total_review_comment_count();
22249
22250        // Remove comments with invalid hunk anchors
22251        self.stored_review_comments
22252            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22253
22254        // Also clean up individual comments with invalid anchor ranges
22255        for (_, comments) in &mut self.stored_review_comments {
22256            comments.retain(|comment| {
22257                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22258            });
22259        }
22260
22261        // Remove empty hunk entries
22262        self.stored_review_comments
22263            .retain(|(_, comments)| !comments.is_empty());
22264
22265        let new_count = self.total_review_comment_count();
22266        if new_count != original_count {
22267            cx.emit(EditorEvent::ReviewCommentsChanged {
22268                total_count: new_count,
22269            });
22270            cx.notify();
22271        }
22272    }
22273
22274    /// Toggles the expanded state of the comments section in the overlay.
22275    pub fn toggle_review_comments_expanded(
22276        &mut self,
22277        _: &ToggleReviewCommentsExpanded,
22278        window: &mut Window,
22279        cx: &mut Context<Self>,
22280    ) {
22281        // Find the overlay that currently has focus, or use the first one
22282        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22283            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22284                overlay.comments_expanded = !overlay.comments_expanded;
22285                Some(overlay.hunk_key.clone())
22286            } else {
22287                None
22288            }
22289        });
22290
22291        // If no focused overlay found, toggle the first one
22292        let hunk_key = overlay_info.or_else(|| {
22293            self.diff_review_overlays.first_mut().map(|overlay| {
22294                overlay.comments_expanded = !overlay.comments_expanded;
22295                overlay.hunk_key.clone()
22296            })
22297        });
22298
22299        if let Some(hunk_key) = hunk_key {
22300            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22301            cx.notify();
22302        }
22303    }
22304
22305    /// Handles the EditReviewComment action - sets a comment into editing mode.
22306    pub fn edit_review_comment(
22307        &mut self,
22308        action: &EditReviewComment,
22309        window: &mut Window,
22310        cx: &mut Context<Self>,
22311    ) {
22312        let comment_id = action.id;
22313
22314        // Set the comment to editing mode
22315        self.set_comment_editing(comment_id, true, cx);
22316
22317        // Find the overlay that contains this comment and create an inline editor if needed
22318        // First, find which hunk this comment belongs to
22319        let hunk_key = self
22320            .stored_review_comments
22321            .iter()
22322            .find_map(|(key, comments)| {
22323                if comments.iter().any(|c| c.id == comment_id) {
22324                    Some(key.clone())
22325                } else {
22326                    None
22327                }
22328            });
22329
22330        let snapshot = self.buffer.read(cx).snapshot(cx);
22331        if let Some(hunk_key) = hunk_key {
22332            if let Some(overlay) = self
22333                .diff_review_overlays
22334                .iter_mut()
22335                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22336            {
22337                if let std::collections::hash_map::Entry::Vacant(entry) =
22338                    overlay.inline_edit_editors.entry(comment_id)
22339                {
22340                    // Find the comment text
22341                    let comment_text = self
22342                        .stored_review_comments
22343                        .iter()
22344                        .flat_map(|(_, comments)| comments)
22345                        .find(|c| c.id == comment_id)
22346                        .map(|c| c.comment.clone())
22347                        .unwrap_or_default();
22348
22349                    // Create inline editor
22350                    let parent_editor = cx.entity().downgrade();
22351                    let inline_editor = cx.new(|cx| {
22352                        let mut editor = Editor::single_line(window, cx);
22353                        editor.set_text(&*comment_text, window, cx);
22354                        // Select all text for easy replacement
22355                        editor.select_all(&crate::actions::SelectAll, window, cx);
22356                        editor
22357                    });
22358
22359                    // Register the Newline action to confirm the edit
22360                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22361                        inline_editor.register_action({
22362                            let parent_editor = parent_editor.clone();
22363                            move |_: &crate::actions::Newline, window, cx| {
22364                                if let Some(editor) = parent_editor.upgrade() {
22365                                    editor.update(cx, |editor, cx| {
22366                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22367                                    });
22368                                }
22369                            }
22370                        })
22371                    });
22372
22373                    // Store the subscription to keep the action handler alive
22374                    overlay
22375                        .inline_edit_subscriptions
22376                        .insert(comment_id, subscription);
22377
22378                    // Focus the inline editor
22379                    let focus_handle = inline_editor.focus_handle(cx);
22380                    window.focus(&focus_handle, cx);
22381
22382                    entry.insert(inline_editor);
22383                }
22384            }
22385        }
22386
22387        cx.notify();
22388    }
22389
22390    /// Confirms an inline edit of a review comment.
22391    pub fn confirm_edit_review_comment(
22392        &mut self,
22393        comment_id: usize,
22394        _window: &mut Window,
22395        cx: &mut Context<Self>,
22396    ) {
22397        // Get the new text from the inline editor
22398        // Find the overlay containing this comment's inline editor
22399        let snapshot = self.buffer.read(cx).snapshot(cx);
22400        let hunk_key = self
22401            .stored_review_comments
22402            .iter()
22403            .find_map(|(key, comments)| {
22404                if comments.iter().any(|c| c.id == comment_id) {
22405                    Some(key.clone())
22406                } else {
22407                    None
22408                }
22409            });
22410
22411        let new_text = hunk_key
22412            .as_ref()
22413            .and_then(|hunk_key| {
22414                self.diff_review_overlays
22415                    .iter()
22416                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22417            })
22418            .as_ref()
22419            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22420            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22421
22422        if let Some(new_text) = new_text {
22423            if !new_text.is_empty() {
22424                self.update_review_comment(comment_id, new_text, cx);
22425            }
22426        }
22427
22428        // Remove the inline editor and its subscription
22429        if let Some(hunk_key) = hunk_key {
22430            if let Some(overlay) = self
22431                .diff_review_overlays
22432                .iter_mut()
22433                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22434            {
22435                overlay.inline_edit_editors.remove(&comment_id);
22436                overlay.inline_edit_subscriptions.remove(&comment_id);
22437            }
22438        }
22439
22440        // Clear editing state
22441        self.set_comment_editing(comment_id, false, cx);
22442    }
22443
22444    /// Cancels an inline edit of a review comment.
22445    pub fn cancel_edit_review_comment(
22446        &mut self,
22447        comment_id: usize,
22448        _window: &mut Window,
22449        cx: &mut Context<Self>,
22450    ) {
22451        // Find which hunk this comment belongs to
22452        let hunk_key = self
22453            .stored_review_comments
22454            .iter()
22455            .find_map(|(key, comments)| {
22456                if comments.iter().any(|c| c.id == comment_id) {
22457                    Some(key.clone())
22458                } else {
22459                    None
22460                }
22461            });
22462
22463        // Remove the inline editor and its subscription
22464        if let Some(hunk_key) = hunk_key {
22465            let snapshot = self.buffer.read(cx).snapshot(cx);
22466            if let Some(overlay) = self
22467                .diff_review_overlays
22468                .iter_mut()
22469                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22470            {
22471                overlay.inline_edit_editors.remove(&comment_id);
22472                overlay.inline_edit_subscriptions.remove(&comment_id);
22473            }
22474        }
22475
22476        // Clear editing state
22477        self.set_comment_editing(comment_id, false, cx);
22478    }
22479
22480    /// Action handler for ConfirmEditReviewComment.
22481    pub fn confirm_edit_review_comment_action(
22482        &mut self,
22483        action: &ConfirmEditReviewComment,
22484        window: &mut Window,
22485        cx: &mut Context<Self>,
22486    ) {
22487        self.confirm_edit_review_comment(action.id, window, cx);
22488    }
22489
22490    /// Action handler for CancelEditReviewComment.
22491    pub fn cancel_edit_review_comment_action(
22492        &mut self,
22493        action: &CancelEditReviewComment,
22494        window: &mut Window,
22495        cx: &mut Context<Self>,
22496    ) {
22497        self.cancel_edit_review_comment(action.id, window, cx);
22498    }
22499
22500    /// Handles the DeleteReviewComment action - removes a comment.
22501    pub fn delete_review_comment(
22502        &mut self,
22503        action: &DeleteReviewComment,
22504        window: &mut Window,
22505        cx: &mut Context<Self>,
22506    ) {
22507        // Get the hunk key before removing the comment
22508        // Find the hunk key from the comment itself
22509        let comment_id = action.id;
22510        let hunk_key = self
22511            .stored_review_comments
22512            .iter()
22513            .find_map(|(key, comments)| {
22514                if comments.iter().any(|c| c.id == comment_id) {
22515                    Some(key.clone())
22516                } else {
22517                    None
22518                }
22519            });
22520
22521        // Also get it from the overlay for refresh purposes
22522        let overlay_hunk_key = self
22523            .diff_review_overlays
22524            .first()
22525            .map(|o| o.hunk_key.clone());
22526
22527        self.remove_review_comment(action.id, cx);
22528
22529        // Refresh the overlay height after removing a comment
22530        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22531            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22532        }
22533    }
22534
22535    fn render_diff_review_overlay(
22536        prompt_editor: &Entity<Editor>,
22537        hunk_key: &DiffHunkKey,
22538        editor_handle: &WeakEntity<Editor>,
22539        cx: &mut BlockContext,
22540    ) -> AnyElement {
22541        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22542            if ranges.is_empty() {
22543                return None;
22544            }
22545            let formatted: Vec<String> = ranges
22546                .iter()
22547                .map(|(start, end)| {
22548                    let start_line = start + 1;
22549                    let end_line = end + 1;
22550                    if start_line == end_line {
22551                        format!("Line {start_line}")
22552                    } else {
22553                        format!("Lines {start_line}-{end_line}")
22554                    }
22555                })
22556                .collect();
22557            // Don't show label for single line in single excerpt
22558            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22559                return None;
22560            }
22561            Some(formatted.join(""))
22562        }
22563
22564        let theme = cx.theme();
22565        let colors = theme.colors();
22566
22567        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22568            editor_handle
22569                .upgrade()
22570                .map(|editor| {
22571                    let editor = editor.read(cx);
22572                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22573                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22574                    let (expanded, editors, avatar_uri, line_ranges) = editor
22575                        .diff_review_overlays
22576                        .iter()
22577                        .find(|overlay| {
22578                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22579                        })
22580                        .map(|o| {
22581                            let start_point = o.anchor_range.start.to_point(&snapshot);
22582                            let end_point = o.anchor_range.end.to_point(&snapshot);
22583                            // Get line ranges per excerpt to detect discontinuities
22584                            let buffer_ranges =
22585                                snapshot.range_to_buffer_ranges(start_point..end_point);
22586                            let ranges: Vec<(u32, u32)> = buffer_ranges
22587                                .iter()
22588                                .map(|(buffer, range, _)| {
22589                                    let start = buffer.offset_to_point(range.start.0).row;
22590                                    let end = buffer.offset_to_point(range.end.0).row;
22591                                    (start, end)
22592                                })
22593                                .collect();
22594                            (
22595                                o.comments_expanded,
22596                                o.inline_edit_editors.clone(),
22597                                o.user_avatar_uri.clone(),
22598                                if ranges.is_empty() {
22599                                    None
22600                                } else {
22601                                    Some(ranges)
22602                                },
22603                            )
22604                        })
22605                        .unwrap_or((true, HashMap::default(), None, None));
22606                    (comments, expanded, editors, avatar_uri, line_ranges)
22607                })
22608                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22609
22610        let comment_count = comments.len();
22611        let avatar_size = px(20.);
22612        let action_icon_size = IconSize::XSmall;
22613
22614        v_flex()
22615            .w_full()
22616            .bg(colors.editor_background)
22617            .border_b_1()
22618            .border_color(colors.border)
22619            .px_2()
22620            .pb_2()
22621            .gap_2()
22622            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22623            .when_some(line_ranges, |el, ranges| {
22624                let label = format_line_ranges(&ranges);
22625                if let Some(label) = label {
22626                    el.child(
22627                        h_flex()
22628                            .w_full()
22629                            .px_2()
22630                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22631                    )
22632                } else {
22633                    el
22634                }
22635            })
22636            // Top row: editable input with user's avatar
22637            .child(
22638                h_flex()
22639                    .w_full()
22640                    .items_center()
22641                    .gap_2()
22642                    .px_2()
22643                    .py_1p5()
22644                    .rounded_md()
22645                    .bg(colors.surface_background)
22646                    .child(
22647                        div()
22648                            .size(avatar_size)
22649                            .flex_shrink_0()
22650                            .rounded_full()
22651                            .overflow_hidden()
22652                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22653                                Avatar::new(avatar_uri.clone())
22654                                    .size(avatar_size)
22655                                    .into_any_element()
22656                            } else {
22657                                Icon::new(IconName::Person)
22658                                    .size(IconSize::Small)
22659                                    .color(ui::Color::Muted)
22660                                    .into_any_element()
22661                            }),
22662                    )
22663                    .child(
22664                        div()
22665                            .flex_1()
22666                            .border_1()
22667                            .border_color(colors.border)
22668                            .rounded_md()
22669                            .bg(colors.editor_background)
22670                            .px_2()
22671                            .py_1()
22672                            .child(prompt_editor.clone()),
22673                    )
22674                    .child(
22675                        h_flex()
22676                            .flex_shrink_0()
22677                            .gap_1()
22678                            .child(
22679                                IconButton::new("diff-review-close", IconName::Close)
22680                                    .icon_color(ui::Color::Muted)
22681                                    .icon_size(action_icon_size)
22682                                    .tooltip(Tooltip::text("Close"))
22683                                    .on_click(|_, window, cx| {
22684                                        window
22685                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22686                                    }),
22687                            )
22688                            .child(
22689                                IconButton::new("diff-review-add", IconName::Return)
22690                                    .icon_color(ui::Color::Muted)
22691                                    .icon_size(action_icon_size)
22692                                    .tooltip(Tooltip::text("Add comment"))
22693                                    .on_click(|_, window, cx| {
22694                                        window.dispatch_action(
22695                                            Box::new(crate::actions::SubmitDiffReviewComment),
22696                                            cx,
22697                                        );
22698                                    }),
22699                            ),
22700                    ),
22701            )
22702            // Expandable comments section (only shown when there are comments)
22703            .when(comment_count > 0, |el| {
22704                el.child(Self::render_comments_section(
22705                    comments,
22706                    comments_expanded,
22707                    inline_editors,
22708                    user_avatar_uri,
22709                    avatar_size,
22710                    action_icon_size,
22711                    colors,
22712                ))
22713            })
22714            .into_any_element()
22715    }
22716
22717    fn render_comments_section(
22718        comments: Vec<StoredReviewComment>,
22719        expanded: bool,
22720        inline_editors: HashMap<usize, Entity<Editor>>,
22721        user_avatar_uri: Option<SharedUri>,
22722        avatar_size: Pixels,
22723        action_icon_size: IconSize,
22724        colors: &theme::ThemeColors,
22725    ) -> impl IntoElement {
22726        let comment_count = comments.len();
22727
22728        v_flex()
22729            .w_full()
22730            .gap_1()
22731            // Header with expand/collapse toggle
22732            .child(
22733                h_flex()
22734                    .id("review-comments-header")
22735                    .w_full()
22736                    .items_center()
22737                    .gap_1()
22738                    .px_2()
22739                    .py_1()
22740                    .cursor_pointer()
22741                    .rounded_md()
22742                    .hover(|style| style.bg(colors.ghost_element_hover))
22743                    .on_click(|_, window: &mut Window, cx| {
22744                        window.dispatch_action(
22745                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22746                            cx,
22747                        );
22748                    })
22749                    .child(
22750                        Icon::new(if expanded {
22751                            IconName::ChevronDown
22752                        } else {
22753                            IconName::ChevronRight
22754                        })
22755                        .size(IconSize::Small)
22756                        .color(ui::Color::Muted),
22757                    )
22758                    .child(
22759                        Label::new(format!(
22760                            "{} Comment{}",
22761                            comment_count,
22762                            if comment_count == 1 { "" } else { "s" }
22763                        ))
22764                        .size(LabelSize::Small)
22765                        .color(Color::Muted),
22766                    ),
22767            )
22768            // Comments list (when expanded)
22769            .when(expanded, |el| {
22770                el.children(comments.into_iter().map(|comment| {
22771                    let inline_editor = inline_editors.get(&comment.id).cloned();
22772                    Self::render_comment_row(
22773                        comment,
22774                        inline_editor,
22775                        user_avatar_uri.clone(),
22776                        avatar_size,
22777                        action_icon_size,
22778                        colors,
22779                    )
22780                }))
22781            })
22782    }
22783
22784    fn render_comment_row(
22785        comment: StoredReviewComment,
22786        inline_editor: Option<Entity<Editor>>,
22787        user_avatar_uri: Option<SharedUri>,
22788        avatar_size: Pixels,
22789        action_icon_size: IconSize,
22790        colors: &theme::ThemeColors,
22791    ) -> impl IntoElement {
22792        let comment_id = comment.id;
22793        let is_editing = inline_editor.is_some();
22794
22795        h_flex()
22796            .w_full()
22797            .items_center()
22798            .gap_2()
22799            .px_2()
22800            .py_1p5()
22801            .rounded_md()
22802            .bg(colors.surface_background)
22803            .child(
22804                div()
22805                    .size(avatar_size)
22806                    .flex_shrink_0()
22807                    .rounded_full()
22808                    .overflow_hidden()
22809                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22810                        Avatar::new(avatar_uri.clone())
22811                            .size(avatar_size)
22812                            .into_any_element()
22813                    } else {
22814                        Icon::new(IconName::Person)
22815                            .size(IconSize::Small)
22816                            .color(ui::Color::Muted)
22817                            .into_any_element()
22818                    }),
22819            )
22820            .child(if let Some(editor) = inline_editor {
22821                // Inline edit mode: show an editable text field
22822                div()
22823                    .flex_1()
22824                    .border_1()
22825                    .border_color(colors.border)
22826                    .rounded_md()
22827                    .bg(colors.editor_background)
22828                    .px_2()
22829                    .py_1()
22830                    .child(editor)
22831                    .into_any_element()
22832            } else {
22833                // Display mode: show the comment text
22834                div()
22835                    .flex_1()
22836                    .text_sm()
22837                    .text_color(colors.text)
22838                    .child(comment.comment)
22839                    .into_any_element()
22840            })
22841            .child(if is_editing {
22842                // Editing mode: show close and confirm buttons
22843                h_flex()
22844                    .gap_1()
22845                    .child(
22846                        IconButton::new(
22847                            format!("diff-review-cancel-edit-{comment_id}"),
22848                            IconName::Close,
22849                        )
22850                        .icon_color(ui::Color::Muted)
22851                        .icon_size(action_icon_size)
22852                        .tooltip(Tooltip::text("Cancel"))
22853                        .on_click(move |_, window, cx| {
22854                            window.dispatch_action(
22855                                Box::new(crate::actions::CancelEditReviewComment {
22856                                    id: comment_id,
22857                                }),
22858                                cx,
22859                            );
22860                        }),
22861                    )
22862                    .child(
22863                        IconButton::new(
22864                            format!("diff-review-confirm-edit-{comment_id}"),
22865                            IconName::Return,
22866                        )
22867                        .icon_color(ui::Color::Muted)
22868                        .icon_size(action_icon_size)
22869                        .tooltip(Tooltip::text("Confirm"))
22870                        .on_click(move |_, window, cx| {
22871                            window.dispatch_action(
22872                                Box::new(crate::actions::ConfirmEditReviewComment {
22873                                    id: comment_id,
22874                                }),
22875                                cx,
22876                            );
22877                        }),
22878                    )
22879                    .into_any_element()
22880            } else {
22881                // Display mode: no action buttons for now (edit/delete not yet implemented)
22882                gpui::Empty.into_any_element()
22883            })
22884    }
22885
22886    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22887        if self.display_map.read(cx).masked != masked {
22888            self.display_map.update(cx, |map, _| map.masked = masked);
22889        }
22890        cx.notify()
22891    }
22892
22893    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22894        self.show_wrap_guides = Some(show_wrap_guides);
22895        cx.notify();
22896    }
22897
22898    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22899        self.show_indent_guides = Some(show_indent_guides);
22900        cx.notify();
22901    }
22902
22903    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22904        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22905            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22906                && let Some(dir) = file.abs_path(cx).parent()
22907            {
22908                return Some(dir.to_owned());
22909            }
22910        }
22911
22912        None
22913    }
22914
22915    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22916        self.active_excerpt(cx)?
22917            .1
22918            .read(cx)
22919            .file()
22920            .and_then(|f| f.as_local())
22921    }
22922
22923    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22924        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22925            let buffer = buffer.read(cx);
22926            if let Some(project_path) = buffer.project_path(cx) {
22927                let project = self.project()?.read(cx);
22928                project.absolute_path(&project_path, cx)
22929            } else {
22930                buffer
22931                    .file()
22932                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22933            }
22934        })
22935    }
22936
22937    pub fn reveal_in_finder(
22938        &mut self,
22939        _: &RevealInFileManager,
22940        _window: &mut Window,
22941        cx: &mut Context<Self>,
22942    ) {
22943        if let Some(path) = self.target_file_abs_path(cx) {
22944            if let Some(project) = self.project() {
22945                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22946            } else {
22947                cx.reveal_path(&path);
22948            }
22949        }
22950    }
22951
22952    pub fn copy_path(
22953        &mut self,
22954        _: &zed_actions::workspace::CopyPath,
22955        _window: &mut Window,
22956        cx: &mut Context<Self>,
22957    ) {
22958        if let Some(path) = self.target_file_abs_path(cx)
22959            && let Some(path) = path.to_str()
22960        {
22961            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22962        } else {
22963            cx.propagate();
22964        }
22965    }
22966
22967    pub fn copy_relative_path(
22968        &mut self,
22969        _: &zed_actions::workspace::CopyRelativePath,
22970        _window: &mut Window,
22971        cx: &mut Context<Self>,
22972    ) {
22973        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22974            let project = self.project()?.read(cx);
22975            let path = buffer.read(cx).file()?.path();
22976            let path = path.display(project.path_style(cx));
22977            Some(path)
22978        }) {
22979            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22980        } else {
22981            cx.propagate();
22982        }
22983    }
22984
22985    /// Returns the project path for the editor's buffer, if any buffer is
22986    /// opened in the editor.
22987    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22988        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22989            buffer.read(cx).project_path(cx)
22990        } else {
22991            None
22992        }
22993    }
22994
22995    // Returns true if the editor handled a go-to-line request
22996    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22997        maybe!({
22998            let breakpoint_store = self.breakpoint_store.as_ref()?;
22999
23000            let (active_stack_frame, debug_line_pane_id) = {
23001                let store = breakpoint_store.read(cx);
23002                let active_stack_frame = store.active_position().cloned();
23003                let debug_line_pane_id = store.active_debug_line_pane_id();
23004                (active_stack_frame, debug_line_pane_id)
23005            };
23006
23007            let Some(active_stack_frame) = active_stack_frame else {
23008                self.clear_row_highlights::<ActiveDebugLine>();
23009                return None;
23010            };
23011
23012            if let Some(debug_line_pane_id) = debug_line_pane_id {
23013                if let Some(workspace) = self
23014                    .workspace
23015                    .as_ref()
23016                    .and_then(|(workspace, _)| workspace.upgrade())
23017                {
23018                    let editor_pane_id = workspace
23019                        .read(cx)
23020                        .pane_for_item_id(cx.entity_id())
23021                        .map(|pane| pane.entity_id());
23022
23023                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
23024                        self.clear_row_highlights::<ActiveDebugLine>();
23025                        return None;
23026                    }
23027                }
23028            }
23029
23030            let position = active_stack_frame.position;
23031            let buffer_id = position.buffer_id?;
23032            let snapshot = self
23033                .project
23034                .as_ref()?
23035                .read(cx)
23036                .buffer_for_id(buffer_id, cx)?
23037                .read(cx)
23038                .snapshot();
23039
23040            let mut handled = false;
23041            for (id, _, ExcerptRange { context, .. }) in
23042                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
23043            {
23044                if context.start.cmp(&position, &snapshot).is_ge()
23045                    || context.end.cmp(&position, &snapshot).is_lt()
23046                {
23047                    continue;
23048                }
23049                let snapshot = self.buffer.read(cx).snapshot(cx);
23050                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
23051
23052                handled = true;
23053                self.clear_row_highlights::<ActiveDebugLine>();
23054
23055                self.go_to_line::<ActiveDebugLine>(
23056                    multibuffer_anchor,
23057                    Some(cx.theme().colors().editor_debugger_active_line_background),
23058                    window,
23059                    cx,
23060                );
23061
23062                cx.notify();
23063            }
23064
23065            handled.then_some(())
23066        })
23067        .is_some()
23068    }
23069
23070    pub fn copy_file_name_without_extension(
23071        &mut self,
23072        _: &CopyFileNameWithoutExtension,
23073        _: &mut Window,
23074        cx: &mut Context<Self>,
23075    ) {
23076        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23077            let file = buffer.read(cx).file()?;
23078            file.path().file_stem()
23079        }) {
23080            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
23081        }
23082    }
23083
23084    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
23085        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23086            let file = buffer.read(cx).file()?;
23087            Some(file.file_name(cx))
23088        }) {
23089            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
23090        }
23091    }
23092
23093    pub fn toggle_git_blame(
23094        &mut self,
23095        _: &::git::Blame,
23096        window: &mut Window,
23097        cx: &mut Context<Self>,
23098    ) {
23099        self.show_git_blame_gutter = !self.show_git_blame_gutter;
23100
23101        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
23102            self.start_git_blame(true, window, cx);
23103        }
23104
23105        cx.notify();
23106    }
23107
23108    pub fn toggle_git_blame_inline(
23109        &mut self,
23110        _: &ToggleGitBlameInline,
23111        window: &mut Window,
23112        cx: &mut Context<Self>,
23113    ) {
23114        self.toggle_git_blame_inline_internal(true, window, cx);
23115        cx.notify();
23116    }
23117
23118    pub fn open_git_blame_commit(
23119        &mut self,
23120        _: &OpenGitBlameCommit,
23121        window: &mut Window,
23122        cx: &mut Context<Self>,
23123    ) {
23124        self.open_git_blame_commit_internal(window, cx);
23125    }
23126
23127    fn open_git_blame_commit_internal(
23128        &mut self,
23129        window: &mut Window,
23130        cx: &mut Context<Self>,
23131    ) -> Option<()> {
23132        let blame = self.blame.as_ref()?;
23133        let snapshot = self.snapshot(window, cx);
23134        let cursor = self
23135            .selections
23136            .newest::<Point>(&snapshot.display_snapshot)
23137            .head();
23138        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
23139        let (_, blame_entry) = blame
23140            .update(cx, |blame, cx| {
23141                blame
23142                    .blame_for_rows(
23143                        &[RowInfo {
23144                            buffer_id: Some(buffer.remote_id()),
23145                            buffer_row: Some(point.row),
23146                            ..Default::default()
23147                        }],
23148                        cx,
23149                    )
23150                    .next()
23151            })
23152            .flatten()?;
23153        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23154        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23155        let workspace = self.workspace()?.downgrade();
23156        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23157        None
23158    }
23159
23160    pub fn git_blame_inline_enabled(&self) -> bool {
23161        self.git_blame_inline_enabled
23162    }
23163
23164    pub fn toggle_selection_menu(
23165        &mut self,
23166        _: &ToggleSelectionMenu,
23167        _: &mut Window,
23168        cx: &mut Context<Self>,
23169    ) {
23170        self.show_selection_menu = self
23171            .show_selection_menu
23172            .map(|show_selections_menu| !show_selections_menu)
23173            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23174
23175        cx.notify();
23176    }
23177
23178    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23179        self.show_selection_menu
23180            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23181    }
23182
23183    fn start_git_blame(
23184        &mut self,
23185        user_triggered: bool,
23186        window: &mut Window,
23187        cx: &mut Context<Self>,
23188    ) {
23189        if let Some(project) = self.project() {
23190            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23191                && buffer.read(cx).file().is_none()
23192            {
23193                return;
23194            }
23195
23196            let focused = self.focus_handle(cx).contains_focused(window, cx);
23197
23198            let project = project.clone();
23199            let blame = cx
23200                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23201            self.blame_subscription =
23202                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23203            self.blame = Some(blame);
23204        }
23205    }
23206
23207    fn toggle_git_blame_inline_internal(
23208        &mut self,
23209        user_triggered: bool,
23210        window: &mut Window,
23211        cx: &mut Context<Self>,
23212    ) {
23213        if self.git_blame_inline_enabled {
23214            self.git_blame_inline_enabled = false;
23215            self.show_git_blame_inline = false;
23216            self.show_git_blame_inline_delay_task.take();
23217        } else {
23218            self.git_blame_inline_enabled = true;
23219            self.start_git_blame_inline(user_triggered, window, cx);
23220        }
23221
23222        cx.notify();
23223    }
23224
23225    fn start_git_blame_inline(
23226        &mut self,
23227        user_triggered: bool,
23228        window: &mut Window,
23229        cx: &mut Context<Self>,
23230    ) {
23231        self.start_git_blame(user_triggered, window, cx);
23232
23233        if ProjectSettings::get_global(cx)
23234            .git
23235            .inline_blame_delay()
23236            .is_some()
23237        {
23238            self.start_inline_blame_timer(window, cx);
23239        } else {
23240            self.show_git_blame_inline = true
23241        }
23242    }
23243
23244    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23245        self.blame.as_ref()
23246    }
23247
23248    pub fn show_git_blame_gutter(&self) -> bool {
23249        self.show_git_blame_gutter
23250    }
23251
23252    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23253        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23254    }
23255
23256    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23257        self.show_git_blame_inline
23258            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23259            && !self.newest_selection_head_on_empty_line(cx)
23260            && self.has_blame_entries(cx)
23261    }
23262
23263    fn has_blame_entries(&self, cx: &App) -> bool {
23264        self.blame()
23265            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23266    }
23267
23268    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23269        let cursor_anchor = self.selections.newest_anchor().head();
23270
23271        let snapshot = self.buffer.read(cx).snapshot(cx);
23272        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23273
23274        snapshot.line_len(buffer_row) == 0
23275    }
23276
23277    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23278        let buffer_and_selection = maybe!({
23279            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23280            let selection_range = selection.range();
23281
23282            let multi_buffer = self.buffer().read(cx);
23283            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23284            let buffer_ranges = multi_buffer_snapshot
23285                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23286
23287            let (buffer, range, _) = if selection.reversed {
23288                buffer_ranges.first()
23289            } else {
23290                buffer_ranges.last()
23291            }?;
23292
23293            let buffer_range = range.to_point(buffer);
23294
23295            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23296                return Some((
23297                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23298                    buffer_range.start.row..buffer_range.end.row,
23299                ));
23300            };
23301
23302            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23303            let start =
23304                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23305            let end =
23306                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23307
23308            Some((
23309                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23310                start.row..end.row,
23311            ))
23312        });
23313
23314        let Some((buffer, selection)) = buffer_and_selection else {
23315            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23316        };
23317
23318        let Some(project) = self.project() else {
23319            return Task::ready(Err(anyhow!("editor does not have project")));
23320        };
23321
23322        project.update(cx, |project, cx| {
23323            project.get_permalink_to_line(&buffer, selection, cx)
23324        })
23325    }
23326
23327    pub fn copy_permalink_to_line(
23328        &mut self,
23329        _: &CopyPermalinkToLine,
23330        window: &mut Window,
23331        cx: &mut Context<Self>,
23332    ) {
23333        let permalink_task = self.get_permalink_to_line(cx);
23334        let workspace = self.workspace();
23335
23336        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23337            Ok(permalink) => {
23338                cx.update(|_, cx| {
23339                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23340                })
23341                .ok();
23342            }
23343            Err(err) => {
23344                let message = format!("Failed to copy permalink: {err}");
23345
23346                anyhow::Result::<()>::Err(err).log_err();
23347
23348                if let Some(workspace) = workspace {
23349                    workspace
23350                        .update_in(cx, |workspace, _, cx| {
23351                            struct CopyPermalinkToLine;
23352
23353                            workspace.show_toast(
23354                                Toast::new(
23355                                    NotificationId::unique::<CopyPermalinkToLine>(),
23356                                    message,
23357                                ),
23358                                cx,
23359                            )
23360                        })
23361                        .ok();
23362                }
23363            }
23364        })
23365        .detach();
23366    }
23367
23368    pub fn copy_file_location(
23369        &mut self,
23370        _: &CopyFileLocation,
23371        _: &mut Window,
23372        cx: &mut Context<Self>,
23373    ) {
23374        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23375
23376        let start_line = selection.start.row + 1;
23377        let end_line = selection.end.row + 1;
23378
23379        let end_line = if selection.end.column == 0 && end_line > start_line {
23380            end_line - 1
23381        } else {
23382            end_line
23383        };
23384
23385        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23386            let project = self.project()?.read(cx);
23387            let file = buffer.read(cx).file()?;
23388            let path = file.path().display(project.path_style(cx));
23389
23390            let location = if start_line == end_line {
23391                format!("{path}:{start_line}")
23392            } else {
23393                format!("{path}:{start_line}-{end_line}")
23394            };
23395            Some(location)
23396        }) {
23397            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23398        }
23399    }
23400
23401    pub fn open_permalink_to_line(
23402        &mut self,
23403        _: &OpenPermalinkToLine,
23404        window: &mut Window,
23405        cx: &mut Context<Self>,
23406    ) {
23407        let permalink_task = self.get_permalink_to_line(cx);
23408        let workspace = self.workspace();
23409
23410        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23411            Ok(permalink) => {
23412                cx.update(|_, cx| {
23413                    cx.open_url(permalink.as_ref());
23414                })
23415                .ok();
23416            }
23417            Err(err) => {
23418                let message = format!("Failed to open permalink: {err}");
23419
23420                anyhow::Result::<()>::Err(err).log_err();
23421
23422                if let Some(workspace) = workspace {
23423                    workspace.update(cx, |workspace, cx| {
23424                        struct OpenPermalinkToLine;
23425
23426                        workspace.show_toast(
23427                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23428                            cx,
23429                        )
23430                    });
23431                }
23432            }
23433        })
23434        .detach();
23435    }
23436
23437    pub fn insert_uuid_v4(
23438        &mut self,
23439        _: &InsertUuidV4,
23440        window: &mut Window,
23441        cx: &mut Context<Self>,
23442    ) {
23443        self.insert_uuid(UuidVersion::V4, window, cx);
23444    }
23445
23446    pub fn insert_uuid_v7(
23447        &mut self,
23448        _: &InsertUuidV7,
23449        window: &mut Window,
23450        cx: &mut Context<Self>,
23451    ) {
23452        self.insert_uuid(UuidVersion::V7, window, cx);
23453    }
23454
23455    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23456        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23457        self.transact(window, cx, |this, window, cx| {
23458            let edits = this
23459                .selections
23460                .all::<Point>(&this.display_snapshot(cx))
23461                .into_iter()
23462                .map(|selection| {
23463                    let uuid = match version {
23464                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23465                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23466                    };
23467
23468                    (selection.range(), uuid.to_string())
23469                });
23470            this.edit(edits, cx);
23471            this.refresh_edit_prediction(true, false, window, cx);
23472        });
23473    }
23474
23475    pub fn open_selections_in_multibuffer(
23476        &mut self,
23477        _: &OpenSelectionsInMultibuffer,
23478        window: &mut Window,
23479        cx: &mut Context<Self>,
23480    ) {
23481        let multibuffer = self.buffer.read(cx);
23482
23483        let Some(buffer) = multibuffer.as_singleton() else {
23484            return;
23485        };
23486
23487        let Some(workspace) = self.workspace() else {
23488            return;
23489        };
23490
23491        let title = multibuffer.title(cx).to_string();
23492
23493        let locations = self
23494            .selections
23495            .all_anchors(&self.display_snapshot(cx))
23496            .iter()
23497            .map(|selection| {
23498                (
23499                    buffer.clone(),
23500                    (selection.start.text_anchor..selection.end.text_anchor)
23501                        .to_point(buffer.read(cx)),
23502                )
23503            })
23504            .into_group_map();
23505
23506        cx.spawn_in(window, async move |_, cx| {
23507            workspace.update_in(cx, |workspace, window, cx| {
23508                Self::open_locations_in_multibuffer(
23509                    workspace,
23510                    locations,
23511                    format!("Selections for '{title}'"),
23512                    false,
23513                    false,
23514                    MultibufferSelectionMode::All,
23515                    window,
23516                    cx,
23517                );
23518            })
23519        })
23520        .detach();
23521    }
23522
23523    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23524    /// last highlight added will be used.
23525    ///
23526    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23527    pub fn highlight_rows<T: 'static>(
23528        &mut self,
23529        range: Range<Anchor>,
23530        color: Hsla,
23531        options: RowHighlightOptions,
23532        cx: &mut Context<Self>,
23533    ) {
23534        let snapshot = self.buffer().read(cx).snapshot(cx);
23535        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23536        let ix = row_highlights.binary_search_by(|highlight| {
23537            Ordering::Equal
23538                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23539                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23540        });
23541
23542        if let Err(mut ix) = ix {
23543            let index = post_inc(&mut self.highlight_order);
23544
23545            // If this range intersects with the preceding highlight, then merge it with
23546            // the preceding highlight. Otherwise insert a new highlight.
23547            let mut merged = false;
23548            if ix > 0 {
23549                let prev_highlight = &mut row_highlights[ix - 1];
23550                if prev_highlight
23551                    .range
23552                    .end
23553                    .cmp(&range.start, &snapshot)
23554                    .is_ge()
23555                {
23556                    ix -= 1;
23557                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23558                        prev_highlight.range.end = range.end;
23559                    }
23560                    merged = true;
23561                    prev_highlight.index = index;
23562                    prev_highlight.color = color;
23563                    prev_highlight.options = options;
23564                }
23565            }
23566
23567            if !merged {
23568                row_highlights.insert(
23569                    ix,
23570                    RowHighlight {
23571                        range,
23572                        index,
23573                        color,
23574                        options,
23575                        type_id: TypeId::of::<T>(),
23576                    },
23577                );
23578            }
23579
23580            // If any of the following highlights intersect with this one, merge them.
23581            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23582                let highlight = &row_highlights[ix];
23583                if next_highlight
23584                    .range
23585                    .start
23586                    .cmp(&highlight.range.end, &snapshot)
23587                    .is_le()
23588                {
23589                    if next_highlight
23590                        .range
23591                        .end
23592                        .cmp(&highlight.range.end, &snapshot)
23593                        .is_gt()
23594                    {
23595                        row_highlights[ix].range.end = next_highlight.range.end;
23596                    }
23597                    row_highlights.remove(ix + 1);
23598                } else {
23599                    break;
23600                }
23601            }
23602        }
23603    }
23604
23605    /// Remove any highlighted row ranges of the given type that intersect the
23606    /// given ranges.
23607    pub fn remove_highlighted_rows<T: 'static>(
23608        &mut self,
23609        ranges_to_remove: Vec<Range<Anchor>>,
23610        cx: &mut Context<Self>,
23611    ) {
23612        let snapshot = self.buffer().read(cx).snapshot(cx);
23613        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23614        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23615        row_highlights.retain(|highlight| {
23616            while let Some(range_to_remove) = ranges_to_remove.peek() {
23617                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23618                    Ordering::Less | Ordering::Equal => {
23619                        ranges_to_remove.next();
23620                    }
23621                    Ordering::Greater => {
23622                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23623                            Ordering::Less | Ordering::Equal => {
23624                                return false;
23625                            }
23626                            Ordering::Greater => break,
23627                        }
23628                    }
23629                }
23630            }
23631
23632            true
23633        })
23634    }
23635
23636    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23637    pub fn clear_row_highlights<T: 'static>(&mut self) {
23638        self.highlighted_rows.remove(&TypeId::of::<T>());
23639    }
23640
23641    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23642    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23643        self.highlighted_rows
23644            .get(&TypeId::of::<T>())
23645            .map_or(&[] as &[_], |vec| vec.as_slice())
23646            .iter()
23647            .map(|highlight| (highlight.range.clone(), highlight.color))
23648    }
23649
23650    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23651    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23652    /// Allows to ignore certain kinds of highlights.
23653    pub fn highlighted_display_rows(
23654        &self,
23655        window: &mut Window,
23656        cx: &mut App,
23657    ) -> BTreeMap<DisplayRow, LineHighlight> {
23658        let snapshot = self.snapshot(window, cx);
23659        let mut used_highlight_orders = HashMap::default();
23660        self.highlighted_rows
23661            .iter()
23662            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23663            .fold(
23664                BTreeMap::<DisplayRow, LineHighlight>::new(),
23665                |mut unique_rows, highlight| {
23666                    let start = highlight.range.start.to_display_point(&snapshot);
23667                    let end = highlight.range.end.to_display_point(&snapshot);
23668                    let start_row = start.row().0;
23669                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23670                    {
23671                        end.row().0.saturating_sub(1)
23672                    } else {
23673                        end.row().0
23674                    };
23675                    for row in start_row..=end_row {
23676                        let used_index =
23677                            used_highlight_orders.entry(row).or_insert(highlight.index);
23678                        if highlight.index >= *used_index {
23679                            *used_index = highlight.index;
23680                            unique_rows.insert(
23681                                DisplayRow(row),
23682                                LineHighlight {
23683                                    include_gutter: highlight.options.include_gutter,
23684                                    border: None,
23685                                    background: highlight.color.into(),
23686                                    type_id: Some(highlight.type_id),
23687                                },
23688                            );
23689                        }
23690                    }
23691                    unique_rows
23692                },
23693            )
23694    }
23695
23696    pub fn highlighted_display_row_for_autoscroll(
23697        &self,
23698        snapshot: &DisplaySnapshot,
23699    ) -> Option<DisplayRow> {
23700        self.highlighted_rows
23701            .values()
23702            .flat_map(|highlighted_rows| highlighted_rows.iter())
23703            .filter_map(|highlight| {
23704                if highlight.options.autoscroll {
23705                    Some(highlight.range.start.to_display_point(snapshot).row())
23706                } else {
23707                    None
23708                }
23709            })
23710            .min()
23711    }
23712
23713    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23714        self.highlight_background(
23715            HighlightKey::SearchWithinRange,
23716            ranges,
23717            |_, colors| colors.colors().editor_document_highlight_read_background,
23718            cx,
23719        )
23720    }
23721
23722    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23723        self.breadcrumb_header = Some(new_header);
23724    }
23725
23726    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23727        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23728    }
23729
23730    pub fn highlight_background(
23731        &mut self,
23732        key: HighlightKey,
23733        ranges: &[Range<Anchor>],
23734        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23735        cx: &mut Context<Self>,
23736    ) {
23737        self.background_highlights
23738            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23739        self.scrollbar_marker_state.dirty = true;
23740        cx.notify();
23741    }
23742
23743    pub fn clear_background_highlights(
23744        &mut self,
23745        key: HighlightKey,
23746        cx: &mut Context<Self>,
23747    ) -> Option<BackgroundHighlight> {
23748        let text_highlights = self.background_highlights.remove(&key)?;
23749        if !text_highlights.1.is_empty() {
23750            self.scrollbar_marker_state.dirty = true;
23751            cx.notify();
23752        }
23753        Some(text_highlights)
23754    }
23755
23756    pub fn highlight_gutter<T: 'static>(
23757        &mut self,
23758        ranges: impl Into<Vec<Range<Anchor>>>,
23759        color_fetcher: fn(&App) -> Hsla,
23760        cx: &mut Context<Self>,
23761    ) {
23762        self.gutter_highlights
23763            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23764        cx.notify();
23765    }
23766
23767    pub fn clear_gutter_highlights<T: 'static>(
23768        &mut self,
23769        cx: &mut Context<Self>,
23770    ) -> Option<GutterHighlight> {
23771        cx.notify();
23772        self.gutter_highlights.remove(&TypeId::of::<T>())
23773    }
23774
23775    pub fn insert_gutter_highlight<T: 'static>(
23776        &mut self,
23777        range: Range<Anchor>,
23778        color_fetcher: fn(&App) -> Hsla,
23779        cx: &mut Context<Self>,
23780    ) {
23781        let snapshot = self.buffer().read(cx).snapshot(cx);
23782        let mut highlights = self
23783            .gutter_highlights
23784            .remove(&TypeId::of::<T>())
23785            .map(|(_, highlights)| highlights)
23786            .unwrap_or_default();
23787        let ix = highlights.binary_search_by(|highlight| {
23788            Ordering::Equal
23789                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23790                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23791        });
23792        if let Err(ix) = ix {
23793            highlights.insert(ix, range);
23794        }
23795        self.gutter_highlights
23796            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23797    }
23798
23799    pub fn remove_gutter_highlights<T: 'static>(
23800        &mut self,
23801        ranges_to_remove: Vec<Range<Anchor>>,
23802        cx: &mut Context<Self>,
23803    ) {
23804        let snapshot = self.buffer().read(cx).snapshot(cx);
23805        let Some((color_fetcher, mut gutter_highlights)) =
23806            self.gutter_highlights.remove(&TypeId::of::<T>())
23807        else {
23808            return;
23809        };
23810        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23811        gutter_highlights.retain(|highlight| {
23812            while let Some(range_to_remove) = ranges_to_remove.peek() {
23813                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23814                    Ordering::Less | Ordering::Equal => {
23815                        ranges_to_remove.next();
23816                    }
23817                    Ordering::Greater => {
23818                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23819                            Ordering::Less | Ordering::Equal => {
23820                                return false;
23821                            }
23822                            Ordering::Greater => break,
23823                        }
23824                    }
23825                }
23826            }
23827
23828            true
23829        });
23830        self.gutter_highlights
23831            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23832    }
23833
23834    #[cfg(any(test, feature = "test-support"))]
23835    pub fn all_text_highlights(
23836        &self,
23837        window: &mut Window,
23838        cx: &mut Context<Self>,
23839    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23840        let snapshot = self.snapshot(window, cx);
23841        self.display_map.update(cx, |display_map, _| {
23842            display_map
23843                .all_text_highlights()
23844                .map(|(_, highlight)| {
23845                    let (style, ranges) = highlight.as_ref();
23846                    (
23847                        *style,
23848                        ranges
23849                            .iter()
23850                            .map(|range| range.clone().to_display_points(&snapshot))
23851                            .collect(),
23852                    )
23853                })
23854                .collect()
23855        })
23856    }
23857
23858    #[cfg(any(test, feature = "test-support"))]
23859    pub fn all_text_background_highlights(
23860        &self,
23861        window: &mut Window,
23862        cx: &mut Context<Self>,
23863    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23864        let snapshot = self.snapshot(window, cx);
23865        let buffer = &snapshot.buffer_snapshot();
23866        let start = buffer.anchor_before(MultiBufferOffset(0));
23867        let end = buffer.anchor_after(buffer.len());
23868        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23869    }
23870
23871    #[cfg(any(test, feature = "test-support"))]
23872    pub fn sorted_background_highlights_in_range(
23873        &self,
23874        search_range: Range<Anchor>,
23875        display_snapshot: &DisplaySnapshot,
23876        theme: &Theme,
23877    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23878        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23879        res.sort_by(|a, b| {
23880            a.0.start
23881                .cmp(&b.0.start)
23882                .then_with(|| a.0.end.cmp(&b.0.end))
23883                .then_with(|| a.1.cmp(&b.1))
23884        });
23885        res
23886    }
23887
23888    #[cfg(any(test, feature = "test-support"))]
23889    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23890        let snapshot = self.buffer().read(cx).snapshot(cx);
23891
23892        let highlights = self
23893            .background_highlights
23894            .get(&HighlightKey::BufferSearchHighlights);
23895
23896        if let Some((_color, ranges)) = highlights {
23897            ranges
23898                .iter()
23899                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23900                .collect_vec()
23901        } else {
23902            vec![]
23903        }
23904    }
23905
23906    fn document_highlights_for_position<'a>(
23907        &'a self,
23908        position: Anchor,
23909        buffer: &'a MultiBufferSnapshot,
23910    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23911        let read_highlights = self
23912            .background_highlights
23913            .get(&HighlightKey::DocumentHighlightRead)
23914            .map(|h| &h.1);
23915        let write_highlights = self
23916            .background_highlights
23917            .get(&HighlightKey::DocumentHighlightWrite)
23918            .map(|h| &h.1);
23919        let left_position = position.bias_left(buffer);
23920        let right_position = position.bias_right(buffer);
23921        read_highlights
23922            .into_iter()
23923            .chain(write_highlights)
23924            .flat_map(move |ranges| {
23925                let start_ix = match ranges.binary_search_by(|probe| {
23926                    let cmp = probe.end.cmp(&left_position, buffer);
23927                    if cmp.is_ge() {
23928                        Ordering::Greater
23929                    } else {
23930                        Ordering::Less
23931                    }
23932                }) {
23933                    Ok(i) | Err(i) => i,
23934                };
23935
23936                ranges[start_ix..]
23937                    .iter()
23938                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23939            })
23940    }
23941
23942    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23943        self.background_highlights
23944            .get(&key)
23945            .is_some_and(|(_, highlights)| !highlights.is_empty())
23946    }
23947
23948    /// Returns all background highlights for a given range.
23949    ///
23950    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23951    pub fn background_highlights_in_range(
23952        &self,
23953        search_range: Range<Anchor>,
23954        display_snapshot: &DisplaySnapshot,
23955        theme: &Theme,
23956    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23957        let mut results = Vec::new();
23958        for (color_fetcher, ranges) in self.background_highlights.values() {
23959            let start_ix = match ranges.binary_search_by(|probe| {
23960                let cmp = probe
23961                    .end
23962                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23963                if cmp.is_gt() {
23964                    Ordering::Greater
23965                } else {
23966                    Ordering::Less
23967                }
23968            }) {
23969                Ok(i) | Err(i) => i,
23970            };
23971            for (index, range) in ranges[start_ix..].iter().enumerate() {
23972                if range
23973                    .start
23974                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23975                    .is_ge()
23976                {
23977                    break;
23978                }
23979
23980                let color = color_fetcher(&(start_ix + index), theme);
23981                let start = range.start.to_display_point(display_snapshot);
23982                let end = range.end.to_display_point(display_snapshot);
23983                results.push((start..end, color))
23984            }
23985        }
23986        results
23987    }
23988
23989    pub fn gutter_highlights_in_range(
23990        &self,
23991        search_range: Range<Anchor>,
23992        display_snapshot: &DisplaySnapshot,
23993        cx: &App,
23994    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23995        let mut results = Vec::new();
23996        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23997            let color = color_fetcher(cx);
23998            let start_ix = match ranges.binary_search_by(|probe| {
23999                let cmp = probe
24000                    .end
24001                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
24002                if cmp.is_gt() {
24003                    Ordering::Greater
24004                } else {
24005                    Ordering::Less
24006                }
24007            }) {
24008                Ok(i) | Err(i) => i,
24009            };
24010            for range in &ranges[start_ix..] {
24011                if range
24012                    .start
24013                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
24014                    .is_ge()
24015                {
24016                    break;
24017                }
24018
24019                let start = range.start.to_display_point(display_snapshot);
24020                let end = range.end.to_display_point(display_snapshot);
24021                results.push((start..end, color))
24022            }
24023        }
24024        results
24025    }
24026
24027    /// Get the text ranges corresponding to the redaction query
24028    pub fn redacted_ranges(
24029        &self,
24030        search_range: Range<Anchor>,
24031        display_snapshot: &DisplaySnapshot,
24032        cx: &App,
24033    ) -> Vec<Range<DisplayPoint>> {
24034        display_snapshot
24035            .buffer_snapshot()
24036            .redacted_ranges(search_range, |file| {
24037                if let Some(file) = file {
24038                    file.is_private()
24039                        && EditorSettings::get(
24040                            Some(SettingsLocation {
24041                                worktree_id: file.worktree_id(cx),
24042                                path: file.path().as_ref(),
24043                            }),
24044                            cx,
24045                        )
24046                        .redact_private_values
24047                } else {
24048                    false
24049                }
24050            })
24051            .map(|range| {
24052                range.start.to_display_point(display_snapshot)
24053                    ..range.end.to_display_point(display_snapshot)
24054            })
24055            .collect()
24056    }
24057
24058    pub fn highlight_text_key(
24059        &mut self,
24060        key: HighlightKey,
24061        ranges: Vec<Range<Anchor>>,
24062        style: HighlightStyle,
24063        merge: bool,
24064        cx: &mut Context<Self>,
24065    ) {
24066        self.display_map.update(cx, |map, cx| {
24067            map.highlight_text(key, ranges, style, merge, cx);
24068        });
24069        cx.notify();
24070    }
24071
24072    pub fn highlight_text(
24073        &mut self,
24074        key: HighlightKey,
24075        ranges: Vec<Range<Anchor>>,
24076        style: HighlightStyle,
24077        cx: &mut Context<Self>,
24078    ) {
24079        self.display_map.update(cx, |map, cx| {
24080            map.highlight_text(key, ranges, style, false, cx)
24081        });
24082        cx.notify();
24083    }
24084
24085    pub fn text_highlights<'a>(
24086        &'a self,
24087        key: HighlightKey,
24088        cx: &'a App,
24089    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
24090        self.display_map.read(cx).text_highlights(key)
24091    }
24092
24093    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
24094        let cleared = self
24095            .display_map
24096            .update(cx, |map, _| map.clear_highlights(key));
24097        if cleared {
24098            cx.notify();
24099        }
24100    }
24101
24102    pub fn clear_highlights_with(
24103        &mut self,
24104        f: &mut dyn FnMut(&HighlightKey) -> bool,
24105        cx: &mut Context<Self>,
24106    ) {
24107        let cleared = self
24108            .display_map
24109            .update(cx, |map, _| map.clear_highlights_with(f));
24110        if cleared {
24111            cx.notify();
24112        }
24113    }
24114
24115    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
24116        (self.read_only(cx) || self.blink_manager.read(cx).visible())
24117            && self.focus_handle.is_focused(window)
24118    }
24119
24120    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
24121        self.show_cursor_when_unfocused = is_enabled;
24122        cx.notify();
24123    }
24124
24125    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
24126        cx.notify();
24127    }
24128
24129    fn on_debug_session_event(
24130        &mut self,
24131        _session: Entity<Session>,
24132        event: &SessionEvent,
24133        cx: &mut Context<Self>,
24134    ) {
24135        if let SessionEvent::InvalidateInlineValue = event {
24136            self.refresh_inline_values(cx);
24137        }
24138    }
24139
24140    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
24141        let Some(semantics) = self.semantics_provider.clone() else {
24142            return;
24143        };
24144
24145        if !self.inline_value_cache.enabled {
24146            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24147            self.splice_inlays(&inlays, Vec::new(), cx);
24148            return;
24149        }
24150
24151        let current_execution_position = self
24152            .highlighted_rows
24153            .get(&TypeId::of::<ActiveDebugLine>())
24154            .and_then(|lines| lines.last().map(|line| line.range.end));
24155
24156        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24157            let inline_values = editor
24158                .update(cx, |editor, cx| {
24159                    let Some(current_execution_position) = current_execution_position else {
24160                        return Some(Task::ready(Ok(Vec::new())));
24161                    };
24162
24163                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24164                        let snapshot = buffer.snapshot(cx);
24165
24166                        let excerpt = snapshot.excerpt_containing(
24167                            current_execution_position..current_execution_position,
24168                        )?;
24169
24170                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24171                    })?;
24172
24173                    if current_execution_position
24174                        .text_anchor
24175                        .buffer_id
24176                        .is_some_and(|id| id != buffer.read(cx).remote_id())
24177                    {
24178                        return Some(Task::ready(Ok(Vec::new())));
24179                    }
24180
24181                    let range =
24182                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24183
24184                    semantics.inline_values(buffer, range, cx)
24185                })
24186                .ok()
24187                .flatten()?
24188                .await
24189                .context("refreshing debugger inlays")
24190                .log_err()?;
24191
24192            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24193
24194            for (buffer_id, inline_value) in inline_values
24195                .into_iter()
24196                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24197            {
24198                buffer_inline_values
24199                    .entry(buffer_id)
24200                    .or_default()
24201                    .push(inline_value);
24202            }
24203
24204            editor
24205                .update(cx, |editor, cx| {
24206                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24207                    let mut new_inlays = Vec::default();
24208
24209                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24210                        let buffer_id = buffer_snapshot.remote_id();
24211                        buffer_inline_values
24212                            .get(&buffer_id)
24213                            .into_iter()
24214                            .flatten()
24215                            .for_each(|hint| {
24216                                let inlay = Inlay::debugger(
24217                                    post_inc(&mut editor.next_inlay_id),
24218                                    Anchor::in_buffer(excerpt_id, hint.position),
24219                                    hint.text(),
24220                                );
24221                                if !inlay.text().chars().contains(&'\n') {
24222                                    new_inlays.push(inlay);
24223                                }
24224                            });
24225                    }
24226
24227                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24228                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24229
24230                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24231                })
24232                .ok()?;
24233            Some(())
24234        });
24235    }
24236
24237    fn on_buffer_event(
24238        &mut self,
24239        multibuffer: &Entity<MultiBuffer>,
24240        event: &multi_buffer::Event,
24241        window: &mut Window,
24242        cx: &mut Context<Self>,
24243    ) {
24244        match event {
24245            multi_buffer::Event::Edited {
24246                edited_buffer,
24247                is_local,
24248            } => {
24249                self.scrollbar_marker_state.dirty = true;
24250                self.active_indent_guides_state.dirty = true;
24251                self.refresh_active_diagnostics(cx);
24252                self.refresh_code_actions(window, cx);
24253                self.refresh_single_line_folds(window, cx);
24254                let snapshot = self.snapshot(window, cx);
24255                self.refresh_matching_bracket_highlights(&snapshot, cx);
24256                self.refresh_outline_symbols_at_cursor(cx);
24257                self.refresh_sticky_headers(&snapshot, cx);
24258                if *is_local && self.has_active_edit_prediction() {
24259                    self.update_visible_edit_prediction(window, cx);
24260                }
24261
24262                // Clean up orphaned review comments after edits
24263                self.cleanup_orphaned_review_comments(cx);
24264
24265                if let Some(buffer) = edited_buffer {
24266                    if buffer.read(cx).file().is_none() {
24267                        cx.emit(EditorEvent::TitleChanged);
24268                    }
24269
24270                    if self.project.is_some() {
24271                        let buffer_id = buffer.read(cx).remote_id();
24272                        self.register_buffer(buffer_id, cx);
24273                        self.update_lsp_data(Some(buffer_id), window, cx);
24274                        self.refresh_inlay_hints(
24275                            InlayHintRefreshReason::BufferEdited(buffer_id),
24276                            cx,
24277                        );
24278                    }
24279                }
24280
24281                cx.emit(EditorEvent::BufferEdited);
24282                cx.emit(SearchEvent::MatchesInvalidated);
24283
24284                let Some(project) = &self.project else { return };
24285                let (telemetry, is_via_ssh) = {
24286                    let project = project.read(cx);
24287                    let telemetry = project.client().telemetry().clone();
24288                    let is_via_ssh = project.is_via_remote_server();
24289                    (telemetry, is_via_ssh)
24290                };
24291                telemetry.log_edit_event("editor", is_via_ssh);
24292            }
24293            multi_buffer::Event::ExcerptsAdded {
24294                buffer,
24295                predecessor,
24296                excerpts,
24297            } => {
24298                let buffer_id = buffer.read(cx).remote_id();
24299                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24300                    && let Some(project) = &self.project
24301                {
24302                    update_uncommitted_diff_for_buffer(
24303                        cx.entity(),
24304                        project,
24305                        [buffer.clone()],
24306                        self.buffer.clone(),
24307                        cx,
24308                    )
24309                    .detach();
24310                }
24311                self.semantic_token_state
24312                    .invalidate_buffer(&buffer.read(cx).remote_id());
24313                self.update_lsp_data(Some(buffer_id), window, cx);
24314                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24315                self.refresh_runnables(None, window, cx);
24316                self.colorize_brackets(false, cx);
24317                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24318                cx.emit(EditorEvent::ExcerptsAdded {
24319                    buffer: buffer.clone(),
24320                    predecessor: *predecessor,
24321                    excerpts: excerpts.clone(),
24322                });
24323            }
24324            multi_buffer::Event::ExcerptsRemoved {
24325                ids,
24326                removed_buffer_ids,
24327            } => {
24328                if let Some(inlay_hints) = &mut self.inlay_hints {
24329                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24330                }
24331                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24332                for buffer_id in removed_buffer_ids {
24333                    self.registered_buffers.remove(buffer_id);
24334                    self.clear_runnables(Some(*buffer_id));
24335                    self.semantic_token_state.invalidate_buffer(buffer_id);
24336                    self.display_map.update(cx, |display_map, cx| {
24337                        display_map.invalidate_semantic_highlights(*buffer_id);
24338                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24339                    });
24340                }
24341
24342                self.display_map.update(cx, |display_map, cx| {
24343                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24344                });
24345
24346                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24347                cx.emit(EditorEvent::ExcerptsRemoved {
24348                    ids: ids.clone(),
24349                    removed_buffer_ids: removed_buffer_ids.clone(),
24350                });
24351            }
24352            multi_buffer::Event::ExcerptsEdited {
24353                excerpt_ids,
24354                buffer_ids,
24355            } => {
24356                self.display_map.update(cx, |map, cx| {
24357                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24358                });
24359                cx.emit(EditorEvent::ExcerptsEdited {
24360                    ids: excerpt_ids.clone(),
24361                });
24362            }
24363            multi_buffer::Event::ExcerptsExpanded { ids } => {
24364                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24365                self.refresh_document_highlights(cx);
24366                let snapshot = multibuffer.read(cx).snapshot(cx);
24367                for id in ids {
24368                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24369                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24370                        self.semantic_token_state
24371                            .invalidate_buffer(&buffer.remote_id());
24372                    }
24373                }
24374                self.colorize_brackets(false, cx);
24375                self.update_lsp_data(None, window, cx);
24376                self.refresh_runnables(None, window, cx);
24377                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24378            }
24379            multi_buffer::Event::Reparsed(buffer_id) => {
24380                self.refresh_runnables(Some(*buffer_id), window, cx);
24381                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24382                self.colorize_brackets(true, cx);
24383                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24384
24385                cx.emit(EditorEvent::Reparsed(*buffer_id));
24386            }
24387            multi_buffer::Event::DiffHunksToggled => {
24388                self.refresh_runnables(None, window, cx);
24389            }
24390            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24391                if !is_fresh_language {
24392                    self.registered_buffers.remove(&buffer_id);
24393                }
24394                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24395                cx.emit(EditorEvent::Reparsed(*buffer_id));
24396                self.update_edit_prediction_settings(cx);
24397                cx.notify();
24398            }
24399            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24400            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24401            multi_buffer::Event::FileHandleChanged
24402            | multi_buffer::Event::Reloaded
24403            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24404            multi_buffer::Event::DiagnosticsUpdated => {
24405                self.update_diagnostics_state(window, cx);
24406            }
24407            _ => {}
24408        };
24409    }
24410
24411    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24412        if !self.diagnostics_enabled() {
24413            return;
24414        }
24415        self.refresh_active_diagnostics(cx);
24416        self.refresh_inline_diagnostics(true, window, cx);
24417        self.scrollbar_marker_state.dirty = true;
24418        cx.notify();
24419    }
24420
24421    pub fn start_temporary_diff_override(&mut self) {
24422        self.load_diff_task.take();
24423        self.temporary_diff_override = true;
24424    }
24425
24426    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24427        self.temporary_diff_override = false;
24428        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24429        self.buffer.update(cx, |buffer, cx| {
24430            buffer.set_all_diff_hunks_collapsed(cx);
24431        });
24432
24433        if let Some(project) = self.project.clone() {
24434            self.load_diff_task = Some(
24435                update_uncommitted_diff_for_buffer(
24436                    cx.entity(),
24437                    &project,
24438                    self.buffer.read(cx).all_buffers(),
24439                    self.buffer.clone(),
24440                    cx,
24441                )
24442                .shared(),
24443            );
24444        }
24445    }
24446
24447    fn on_display_map_changed(
24448        &mut self,
24449        _: Entity<DisplayMap>,
24450        _: &mut Window,
24451        cx: &mut Context<Self>,
24452    ) {
24453        cx.notify();
24454    }
24455
24456    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24457        if !self.mode.is_full() {
24458            return None;
24459        }
24460
24461        let theme_settings = theme_settings::ThemeSettings::get_global(cx);
24462        let theme = cx.theme();
24463        let accent_colors = theme.accents().clone();
24464
24465        let accent_overrides = theme_settings
24466            .theme_overrides
24467            .get(theme.name.as_ref())
24468            .map(|theme_style| &theme_style.accents)
24469            .into_iter()
24470            .flatten()
24471            .chain(
24472                theme_settings
24473                    .experimental_theme_overrides
24474                    .as_ref()
24475                    .map(|overrides| &overrides.accents)
24476                    .into_iter()
24477                    .flatten(),
24478            )
24479            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24480            .collect();
24481
24482        Some(AccentData {
24483            colors: accent_colors,
24484            overrides: accent_overrides,
24485        })
24486    }
24487
24488    fn fetch_applicable_language_settings(
24489        &self,
24490        cx: &App,
24491    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24492        if !self.mode.is_full() {
24493            return HashMap::default();
24494        }
24495
24496        self.buffer().read(cx).all_buffers().into_iter().fold(
24497            HashMap::default(),
24498            |mut acc, buffer| {
24499                let buffer = buffer.read(cx);
24500                let language = buffer.language().map(|language| language.name());
24501                if let hash_map::Entry::Vacant(v) = acc.entry(language) {
24502                    v.insert(LanguageSettings::for_buffer(&buffer, cx).into_owned());
24503                }
24504                acc
24505            },
24506        )
24507    }
24508
24509    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24510        let new_language_settings = self.fetch_applicable_language_settings(cx);
24511        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24512        self.applicable_language_settings = new_language_settings;
24513
24514        let new_accents = self.fetch_accent_data(cx);
24515        let accents_changed = new_accents != self.accent_data;
24516        self.accent_data = new_accents;
24517
24518        if self.diagnostics_enabled() {
24519            let new_severity = EditorSettings::get_global(cx)
24520                .diagnostics_max_severity
24521                .unwrap_or(DiagnosticSeverity::Hint);
24522            self.set_max_diagnostics_severity(new_severity, cx);
24523        }
24524        self.refresh_runnables(None, window, cx);
24525        self.update_edit_prediction_settings(cx);
24526        self.refresh_edit_prediction(true, false, window, cx);
24527        self.refresh_inline_values(cx);
24528
24529        let old_cursor_shape = self.cursor_shape;
24530        let old_show_breadcrumbs = self.show_breadcrumbs;
24531
24532        {
24533            let editor_settings = EditorSettings::get_global(cx);
24534            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24535            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24536            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24537            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24538        }
24539
24540        if old_cursor_shape != self.cursor_shape {
24541            cx.emit(EditorEvent::CursorShapeChanged);
24542        }
24543
24544        if old_show_breadcrumbs != self.show_breadcrumbs {
24545            cx.emit(EditorEvent::BreadcrumbsChanged);
24546        }
24547
24548        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24549            let project_settings = ProjectSettings::get_global(cx);
24550            (
24551                project_settings.session.restore_unsaved_buffers,
24552                project_settings.diagnostics.inline.enabled,
24553                project_settings.git.inline_blame.enabled,
24554            )
24555        };
24556        self.buffer_serialization = self
24557            .should_serialize_buffer()
24558            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24559
24560        if self.mode.is_full() {
24561            if self.show_inline_diagnostics != show_inline_diagnostics {
24562                self.show_inline_diagnostics = show_inline_diagnostics;
24563                self.refresh_inline_diagnostics(false, window, cx);
24564            }
24565
24566            if self.git_blame_inline_enabled != inline_blame_enabled {
24567                self.toggle_git_blame_inline_internal(false, window, cx);
24568            }
24569
24570            let minimap_settings = EditorSettings::get_global(cx).minimap;
24571            if self.minimap_visibility != MinimapVisibility::Disabled {
24572                if self.minimap_visibility.settings_visibility()
24573                    != minimap_settings.minimap_enabled()
24574                {
24575                    self.set_minimap_visibility(
24576                        MinimapVisibility::for_mode(self.mode(), cx),
24577                        window,
24578                        cx,
24579                    );
24580                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24581                    minimap_entity.update(cx, |minimap_editor, cx| {
24582                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24583                    })
24584                }
24585            }
24586
24587            if language_settings_changed || accents_changed {
24588                self.colorize_brackets(true, cx);
24589            }
24590
24591            if language_settings_changed {
24592                self.clear_disabled_lsp_folding_ranges(window, cx);
24593                self.refresh_document_symbols(None, cx);
24594            }
24595
24596            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24597                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24598            }) {
24599                if !inlay_splice.is_empty() {
24600                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24601                }
24602                self.refresh_document_colors(None, window, cx);
24603            }
24604
24605            self.refresh_inlay_hints(
24606                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24607                    self.selections.newest_anchor().head(),
24608                    &self.buffer.read(cx).snapshot(cx),
24609                    cx,
24610                )),
24611                cx,
24612            );
24613
24614            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24615                .global_lsp_settings
24616                .semantic_token_rules
24617                .clone();
24618            let semantic_token_rules_changed = self
24619                .semantic_token_state
24620                .update_rules(new_semantic_token_rules);
24621            if language_settings_changed || semantic_token_rules_changed {
24622                self.invalidate_semantic_tokens(None);
24623                self.refresh_semantic_tokens(None, None, cx);
24624            }
24625        }
24626
24627        cx.notify();
24628    }
24629
24630    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24631        if !self.mode.is_full() {
24632            return;
24633        }
24634
24635        let new_accents = self.fetch_accent_data(cx);
24636        if new_accents != self.accent_data {
24637            self.accent_data = new_accents;
24638            self.colorize_brackets(true, cx);
24639        }
24640
24641        self.invalidate_semantic_tokens(None);
24642        self.refresh_semantic_tokens(None, None, cx);
24643    }
24644
24645    pub fn set_searchable(&mut self, searchable: bool) {
24646        self.searchable = searchable;
24647    }
24648
24649    pub fn searchable(&self) -> bool {
24650        self.searchable
24651    }
24652
24653    pub fn open_excerpts_in_split(
24654        &mut self,
24655        _: &OpenExcerptsSplit,
24656        window: &mut Window,
24657        cx: &mut Context<Self>,
24658    ) {
24659        self.open_excerpts_common(None, true, window, cx)
24660    }
24661
24662    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24663        self.open_excerpts_common(None, false, window, cx)
24664    }
24665
24666    pub(crate) fn open_excerpts_common(
24667        &mut self,
24668        jump_data: Option<JumpData>,
24669        split: bool,
24670        window: &mut Window,
24671        cx: &mut Context<Self>,
24672    ) {
24673        if self.buffer.read(cx).is_singleton() {
24674            cx.propagate();
24675            return;
24676        }
24677
24678        let mut new_selections_by_buffer = HashMap::default();
24679        match &jump_data {
24680            Some(JumpData::MultiBufferPoint {
24681                excerpt_id,
24682                position,
24683                anchor,
24684                line_offset_from_top,
24685            }) => {
24686                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24687                if let Some(buffer) = multi_buffer_snapshot
24688                    .buffer_id_for_excerpt(*excerpt_id)
24689                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24690                {
24691                    let buffer_snapshot = buffer.read(cx).snapshot();
24692                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24693                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24694                    } else {
24695                        buffer_snapshot.clip_point(*position, Bias::Left)
24696                    };
24697                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24698                    new_selections_by_buffer.insert(
24699                        buffer,
24700                        (
24701                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24702                            Some(*line_offset_from_top),
24703                        ),
24704                    );
24705                }
24706            }
24707            Some(JumpData::MultiBufferRow {
24708                row,
24709                line_offset_from_top,
24710            }) => {
24711                let point = MultiBufferPoint::new(row.0, 0);
24712                if let Some((buffer, buffer_point, _)) =
24713                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24714                {
24715                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24716                    new_selections_by_buffer
24717                        .entry(buffer)
24718                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24719                        .0
24720                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24721                }
24722            }
24723            None => {
24724                let selections = self
24725                    .selections
24726                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24727                let multi_buffer = self.buffer.read(cx);
24728                for selection in selections {
24729                    for (snapshot, range, _, anchor) in multi_buffer
24730                        .snapshot(cx)
24731                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24732                    {
24733                        if let Some(anchor) = anchor {
24734                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24735                            else {
24736                                continue;
24737                            };
24738                            let offset = text::ToOffset::to_offset(
24739                                &anchor.text_anchor,
24740                                &buffer_handle.read(cx).snapshot(),
24741                            );
24742                            let range = BufferOffset(offset)..BufferOffset(offset);
24743                            new_selections_by_buffer
24744                                .entry(buffer_handle)
24745                                .or_insert((Vec::new(), None))
24746                                .0
24747                                .push(range)
24748                        } else {
24749                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24750                            else {
24751                                continue;
24752                            };
24753                            new_selections_by_buffer
24754                                .entry(buffer_handle)
24755                                .or_insert((Vec::new(), None))
24756                                .0
24757                                .push(range)
24758                        }
24759                    }
24760                }
24761            }
24762        }
24763
24764        if self.delegate_open_excerpts {
24765            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24766                .into_iter()
24767                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24768                .collect();
24769            if !selections_by_buffer.is_empty() {
24770                cx.emit(EditorEvent::OpenExcerptsRequested {
24771                    selections_by_buffer,
24772                    split,
24773                });
24774            }
24775            return;
24776        }
24777
24778        let Some(workspace) = self.workspace() else {
24779            cx.propagate();
24780            return;
24781        };
24782
24783        new_selections_by_buffer
24784            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24785
24786        if new_selections_by_buffer.is_empty() {
24787            return;
24788        }
24789
24790        Self::open_buffers_in_workspace(
24791            workspace.downgrade(),
24792            new_selections_by_buffer,
24793            split,
24794            window,
24795            cx,
24796        );
24797    }
24798
24799    pub(crate) fn open_buffers_in_workspace(
24800        workspace: WeakEntity<Workspace>,
24801        new_selections_by_buffer: HashMap<
24802            Entity<language::Buffer>,
24803            (Vec<Range<BufferOffset>>, Option<u32>),
24804        >,
24805        split: bool,
24806        window: &mut Window,
24807        cx: &mut App,
24808    ) {
24809        // We defer the pane interaction because we ourselves are a workspace item
24810        // and activating a new item causes the pane to call a method on us reentrantly,
24811        // which panics if we're on the stack.
24812        window.defer(cx, move |window, cx| {
24813            workspace
24814                .update(cx, |workspace, cx| {
24815                    let pane = if split {
24816                        workspace.adjacent_pane(window, cx)
24817                    } else {
24818                        workspace.active_pane().clone()
24819                    };
24820
24821                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24822                        let buffer_read = buffer.read(cx);
24823                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24824                            (true, project::File::from_dyn(Some(file)).is_some())
24825                        } else {
24826                            (false, false)
24827                        };
24828
24829                        // If project file is none workspace.open_project_item will fail to open the excerpt
24830                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24831                        // so we check if there's a tab match in that case first
24832                        let editor = (!has_file || !is_project_file)
24833                            .then(|| {
24834                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24835                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24836                                // Instead, we try to activate the existing editor in the pane first.
24837                                let (editor, pane_item_index, pane_item_id) =
24838                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24839                                        let editor = item.downcast::<Editor>()?;
24840                                        let singleton_buffer =
24841                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24842                                        if singleton_buffer == buffer {
24843                                            Some((editor, i, item.item_id()))
24844                                        } else {
24845                                            None
24846                                        }
24847                                    })?;
24848                                pane.update(cx, |pane, cx| {
24849                                    pane.activate_item(pane_item_index, true, true, window, cx);
24850                                    if !PreviewTabsSettings::get_global(cx)
24851                                        .enable_preview_from_multibuffer
24852                                    {
24853                                        pane.unpreview_item_if_preview(pane_item_id);
24854                                    }
24855                                });
24856                                Some(editor)
24857                            })
24858                            .flatten()
24859                            .unwrap_or_else(|| {
24860                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24861                                    .enable_keep_preview_on_code_navigation;
24862                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24863                                    .enable_preview_from_multibuffer;
24864                                workspace.open_project_item::<Self>(
24865                                    pane.clone(),
24866                                    buffer,
24867                                    true,
24868                                    true,
24869                                    keep_old_preview,
24870                                    allow_new_preview,
24871                                    window,
24872                                    cx,
24873                                )
24874                            });
24875
24876                        editor.update(cx, |editor, cx| {
24877                            if has_file && !is_project_file {
24878                                editor.set_read_only(true);
24879                            }
24880                            let autoscroll = match scroll_offset {
24881                                Some(scroll_offset) => {
24882                                    Autoscroll::top_relative(scroll_offset as usize)
24883                                }
24884                                None => Autoscroll::newest(),
24885                            };
24886                            let nav_history = editor.nav_history.take();
24887                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24888                            let Some((excerpt_id, _, buffer_snapshot)) =
24889                                multibuffer_snapshot.as_singleton()
24890                            else {
24891                                return;
24892                            };
24893                            editor.change_selections(
24894                                SelectionEffects::scroll(autoscroll),
24895                                window,
24896                                cx,
24897                                |s| {
24898                                    s.select_ranges(ranges.into_iter().map(|range| {
24899                                        let range = buffer_snapshot.anchor_before(range.start)
24900                                            ..buffer_snapshot.anchor_after(range.end);
24901                                        multibuffer_snapshot
24902                                            .anchor_range_in_excerpt(excerpt_id, range)
24903                                            .unwrap()
24904                                    }));
24905                                },
24906                            );
24907                            editor.nav_history = nav_history;
24908                        });
24909                    }
24910                })
24911                .ok();
24912        });
24913    }
24914
24915    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24916        let snapshot = self.buffer.read(cx).read(cx);
24917        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24918        Some(
24919            ranges
24920                .iter()
24921                .map(move |range| {
24922                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24923                })
24924                .collect(),
24925        )
24926    }
24927
24928    fn selection_replacement_ranges(
24929        &self,
24930        range: Range<MultiBufferOffsetUtf16>,
24931        cx: &mut App,
24932    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24933        let selections = self
24934            .selections
24935            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24936        let newest_selection = selections
24937            .iter()
24938            .max_by_key(|selection| selection.id)
24939            .unwrap();
24940        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24941        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24942        let snapshot = self.buffer.read(cx).read(cx);
24943        selections
24944            .into_iter()
24945            .map(|mut selection| {
24946                selection.start.0.0 =
24947                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24948                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24949                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24950                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24951            })
24952            .collect()
24953    }
24954
24955    fn report_editor_event(
24956        &self,
24957        reported_event: ReportEditorEvent,
24958        file_extension: Option<String>,
24959        cx: &App,
24960    ) {
24961        if cfg!(any(test, feature = "test-support")) {
24962            return;
24963        }
24964
24965        let Some(project) = &self.project else { return };
24966
24967        // If None, we are in a file without an extension
24968        let file = self
24969            .buffer
24970            .read(cx)
24971            .as_singleton()
24972            .and_then(|b| b.read(cx).file());
24973        let file_extension = file_extension.or(file
24974            .as_ref()
24975            .and_then(|file| Path::new(file.file_name(cx)).extension())
24976            .and_then(|e| e.to_str())
24977            .map(|a| a.to_string()));
24978
24979        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24980            .map(|vim_mode| vim_mode.0)
24981            .unwrap_or(false);
24982
24983        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24984        let copilot_enabled = edit_predictions_provider
24985            == language::language_settings::EditPredictionProvider::Copilot;
24986        let copilot_enabled_for_language = self
24987            .buffer
24988            .read(cx)
24989            .language_settings(cx)
24990            .show_edit_predictions;
24991
24992        let project = project.read(cx);
24993        let event_type = reported_event.event_type();
24994
24995        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24996            telemetry::event!(
24997                event_type,
24998                type = if auto_saved {"autosave"} else {"manual"},
24999                file_extension,
25000                vim_mode,
25001                copilot_enabled,
25002                copilot_enabled_for_language,
25003                edit_predictions_provider,
25004                is_via_ssh = project.is_via_remote_server(),
25005            );
25006        } else {
25007            telemetry::event!(
25008                event_type,
25009                file_extension,
25010                vim_mode,
25011                copilot_enabled,
25012                copilot_enabled_for_language,
25013                edit_predictions_provider,
25014                is_via_ssh = project.is_via_remote_server(),
25015            );
25016        };
25017    }
25018
25019    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
25020    /// with each line being an array of {text, highlight} objects.
25021    fn copy_highlight_json(
25022        &mut self,
25023        _: &CopyHighlightJson,
25024        window: &mut Window,
25025        cx: &mut Context<Self>,
25026    ) {
25027        #[derive(Serialize)]
25028        struct Chunk<'a> {
25029            text: String,
25030            highlight: Option<&'a str>,
25031        }
25032
25033        let snapshot = self.buffer.read(cx).snapshot(cx);
25034        let range = self
25035            .selected_text_range(false, window, cx)
25036            .and_then(|selection| {
25037                if selection.range.is_empty() {
25038                    None
25039                } else {
25040                    Some(
25041                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25042                            selection.range.start,
25043                        )))
25044                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25045                                selection.range.end,
25046                            ))),
25047                    )
25048                }
25049            })
25050            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
25051
25052        let chunks = snapshot.chunks(range, true);
25053        let mut lines = Vec::new();
25054        let mut line: VecDeque<Chunk> = VecDeque::new();
25055
25056        let Some(style) = self.style.as_ref() else {
25057            return;
25058        };
25059
25060        for chunk in chunks {
25061            let highlight = chunk
25062                .syntax_highlight_id
25063                .and_then(|id| style.syntax.get_capture_name(id));
25064
25065            let mut chunk_lines = chunk.text.split('\n').peekable();
25066            while let Some(text) = chunk_lines.next() {
25067                let mut merged_with_last_token = false;
25068                if let Some(last_token) = line.back_mut()
25069                    && last_token.highlight == highlight
25070                {
25071                    last_token.text.push_str(text);
25072                    merged_with_last_token = true;
25073                }
25074
25075                if !merged_with_last_token {
25076                    line.push_back(Chunk {
25077                        text: text.into(),
25078                        highlight,
25079                    });
25080                }
25081
25082                if chunk_lines.peek().is_some() {
25083                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
25084                        line.pop_front();
25085                    }
25086                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
25087                        line.pop_back();
25088                    }
25089
25090                    lines.push(mem::take(&mut line));
25091                }
25092            }
25093        }
25094
25095        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
25096            return;
25097        };
25098        cx.write_to_clipboard(ClipboardItem::new_string(lines));
25099    }
25100
25101    pub fn open_context_menu(
25102        &mut self,
25103        _: &OpenContextMenu,
25104        window: &mut Window,
25105        cx: &mut Context<Self>,
25106    ) {
25107        self.request_autoscroll(Autoscroll::newest(), cx);
25108        let position = self
25109            .selections
25110            .newest_display(&self.display_snapshot(cx))
25111            .start;
25112        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
25113    }
25114
25115    pub fn replay_insert_event(
25116        &mut self,
25117        text: &str,
25118        relative_utf16_range: Option<Range<isize>>,
25119        window: &mut Window,
25120        cx: &mut Context<Self>,
25121    ) {
25122        if !self.input_enabled {
25123            cx.emit(EditorEvent::InputIgnored { text: text.into() });
25124            return;
25125        }
25126        if let Some(relative_utf16_range) = relative_utf16_range {
25127            let selections = self
25128                .selections
25129                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
25130            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25131                let new_ranges = selections.into_iter().map(|range| {
25132                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
25133                        range
25134                            .head()
25135                            .0
25136                            .0
25137                            .saturating_add_signed(relative_utf16_range.start),
25138                    ));
25139                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
25140                        range
25141                            .head()
25142                            .0
25143                            .0
25144                            .saturating_add_signed(relative_utf16_range.end),
25145                    ));
25146                    start..end
25147                });
25148                s.select_ranges(new_ranges);
25149            });
25150        }
25151
25152        self.handle_input(text, window, cx);
25153    }
25154
25155    pub fn is_focused(&self, window: &Window) -> bool {
25156        self.focus_handle.is_focused(window)
25157    }
25158
25159    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25160        cx.emit(EditorEvent::Focused);
25161
25162        if let Some(descendant) = self
25163            .last_focused_descendant
25164            .take()
25165            .and_then(|descendant| descendant.upgrade())
25166        {
25167            window.focus(&descendant, cx);
25168        } else {
25169            if let Some(blame) = self.blame.as_ref() {
25170                blame.update(cx, GitBlame::focus)
25171            }
25172
25173            self.blink_manager.update(cx, BlinkManager::enable);
25174            self.show_cursor_names(window, cx);
25175            self.buffer.update(cx, |buffer, cx| {
25176                buffer.finalize_last_transaction(cx);
25177                if self.leader_id.is_none() {
25178                    buffer.set_active_selections(
25179                        &self.selections.disjoint_anchors_arc(),
25180                        self.selections.line_mode(),
25181                        self.cursor_shape,
25182                        cx,
25183                    );
25184                }
25185            });
25186
25187            if let Some(position_map) = self.last_position_map.clone()
25188                && !self.mouse_cursor_hidden
25189            {
25190                EditorElement::mouse_moved(
25191                    self,
25192                    &MouseMoveEvent {
25193                        position: window.mouse_position(),
25194                        pressed_button: None,
25195                        modifiers: window.modifiers(),
25196                    },
25197                    &position_map,
25198                    None,
25199                    window,
25200                    cx,
25201                );
25202            }
25203        }
25204    }
25205
25206    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25207        cx.emit(EditorEvent::FocusedIn)
25208    }
25209
25210    fn handle_focus_out(
25211        &mut self,
25212        event: FocusOutEvent,
25213        _window: &mut Window,
25214        cx: &mut Context<Self>,
25215    ) {
25216        if event.blurred != self.focus_handle {
25217            self.last_focused_descendant = Some(event.blurred);
25218        }
25219        self.selection_drag_state = SelectionDragState::None;
25220        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25221    }
25222
25223    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25224        self.blink_manager.update(cx, BlinkManager::disable);
25225        self.buffer
25226            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25227
25228        if let Some(blame) = self.blame.as_ref() {
25229            blame.update(cx, GitBlame::blur)
25230        }
25231        if !self.hover_state.focused(window, cx) {
25232            hide_hover(self, cx);
25233        }
25234        if !self
25235            .context_menu
25236            .borrow()
25237            .as_ref()
25238            .is_some_and(|context_menu| context_menu.focused(window, cx))
25239        {
25240            self.hide_context_menu(window, cx);
25241        }
25242        self.take_active_edit_prediction(true, cx);
25243        cx.emit(EditorEvent::Blurred);
25244        cx.notify();
25245    }
25246
25247    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25248        let mut pending: String = window
25249            .pending_input_keystrokes()
25250            .into_iter()
25251            .flatten()
25252            .filter_map(|keystroke| keystroke.key_char.clone())
25253            .collect();
25254
25255        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25256            pending = "".to_string();
25257        }
25258
25259        let existing_pending = self
25260            .text_highlights(HighlightKey::PendingInput, cx)
25261            .map(|(_, ranges)| ranges.to_vec());
25262        if existing_pending.is_none() && pending.is_empty() {
25263            return;
25264        }
25265        let transaction =
25266            self.transact(window, cx, |this, window, cx| {
25267                let selections = this
25268                    .selections
25269                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25270                let edits = selections
25271                    .iter()
25272                    .map(|selection| (selection.end..selection.end, pending.clone()));
25273                this.edit(edits, cx);
25274                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25275                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25276                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25277                    }));
25278                });
25279                if let Some(existing_ranges) = existing_pending {
25280                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25281                    this.edit(edits, cx);
25282                }
25283            });
25284
25285        let snapshot = self.snapshot(window, cx);
25286        let ranges = self
25287            .selections
25288            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25289            .into_iter()
25290            .map(|selection| {
25291                snapshot.buffer_snapshot().anchor_after(selection.end)
25292                    ..snapshot
25293                        .buffer_snapshot()
25294                        .anchor_before(selection.end + pending.len())
25295            })
25296            .collect();
25297
25298        if pending.is_empty() {
25299            self.clear_highlights(HighlightKey::PendingInput, cx);
25300        } else {
25301            self.highlight_text(
25302                HighlightKey::PendingInput,
25303                ranges,
25304                HighlightStyle {
25305                    underline: Some(UnderlineStyle {
25306                        thickness: px(1.),
25307                        color: None,
25308                        wavy: false,
25309                    }),
25310                    ..Default::default()
25311                },
25312                cx,
25313            );
25314        }
25315
25316        self.ime_transaction = self.ime_transaction.or(transaction);
25317        if let Some(transaction) = self.ime_transaction {
25318            self.buffer.update(cx, |buffer, cx| {
25319                buffer.group_until_transaction(transaction, cx);
25320            });
25321        }
25322
25323        if self
25324            .text_highlights(HighlightKey::PendingInput, cx)
25325            .is_none()
25326        {
25327            self.ime_transaction.take();
25328        }
25329    }
25330
25331    pub fn register_action_renderer(
25332        &mut self,
25333        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25334    ) -> Subscription {
25335        let id = self.next_editor_action_id.post_inc();
25336        self.editor_actions
25337            .borrow_mut()
25338            .insert(id, Box::new(listener));
25339
25340        let editor_actions = self.editor_actions.clone();
25341        Subscription::new(move || {
25342            editor_actions.borrow_mut().remove(&id);
25343        })
25344    }
25345
25346    pub fn register_action<A: Action>(
25347        &mut self,
25348        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25349    ) -> Subscription {
25350        let id = self.next_editor_action_id.post_inc();
25351        let listener = Arc::new(listener);
25352        self.editor_actions.borrow_mut().insert(
25353            id,
25354            Box::new(move |_, window, _| {
25355                let listener = listener.clone();
25356                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25357                    let action = action.downcast_ref().unwrap();
25358                    if phase == DispatchPhase::Bubble {
25359                        listener(action, window, cx)
25360                    }
25361                })
25362            }),
25363        );
25364
25365        let editor_actions = self.editor_actions.clone();
25366        Subscription::new(move || {
25367            editor_actions.borrow_mut().remove(&id);
25368        })
25369    }
25370
25371    pub fn file_header_size(&self) -> u32 {
25372        FILE_HEADER_HEIGHT
25373    }
25374
25375    pub fn restore(
25376        &mut self,
25377        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25378        window: &mut Window,
25379        cx: &mut Context<Self>,
25380    ) {
25381        self.buffer().update(cx, |multi_buffer, cx| {
25382            for (buffer_id, changes) in revert_changes {
25383                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25384                    buffer.update(cx, |buffer, cx| {
25385                        buffer.edit(
25386                            changes
25387                                .into_iter()
25388                                .map(|(range, text)| (range, text.to_string())),
25389                            None,
25390                            cx,
25391                        );
25392                    });
25393                }
25394            }
25395        });
25396        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25397            selections.refresh()
25398        });
25399    }
25400
25401    pub fn to_pixel_point(
25402        &mut self,
25403        source: Anchor,
25404        editor_snapshot: &EditorSnapshot,
25405        window: &mut Window,
25406        cx: &mut App,
25407    ) -> Option<gpui::Point<Pixels>> {
25408        let source_point = source.to_display_point(editor_snapshot);
25409        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25410    }
25411
25412    pub fn display_to_pixel_point(
25413        &mut self,
25414        source: DisplayPoint,
25415        editor_snapshot: &EditorSnapshot,
25416        window: &mut Window,
25417        cx: &mut App,
25418    ) -> Option<gpui::Point<Pixels>> {
25419        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25420        let text_layout_details = self.text_layout_details(window, cx);
25421        let scroll_top = text_layout_details
25422            .scroll_anchor
25423            .scroll_position(editor_snapshot)
25424            .y;
25425
25426        if source.row().as_f64() < scroll_top.floor() {
25427            return None;
25428        }
25429        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25430        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25431        Some(gpui::Point::new(source_x, source_y))
25432    }
25433
25434    pub fn has_visible_completions_menu(&self) -> bool {
25435        !self.edit_prediction_preview_is_active()
25436            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25437                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25438            })
25439    }
25440
25441    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25442        if self.mode.is_minimap() {
25443            return;
25444        }
25445        self.addons
25446            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25447    }
25448
25449    pub fn unregister_addon<T: Addon>(&mut self) {
25450        self.addons.remove(&std::any::TypeId::of::<T>());
25451    }
25452
25453    pub fn addon<T: Addon>(&self) -> Option<&T> {
25454        let type_id = std::any::TypeId::of::<T>();
25455        self.addons
25456            .get(&type_id)
25457            .and_then(|item| item.to_any().downcast_ref::<T>())
25458    }
25459
25460    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25461        let type_id = std::any::TypeId::of::<T>();
25462        self.addons
25463            .get_mut(&type_id)
25464            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25465    }
25466
25467    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25468        let text_layout_details = self.text_layout_details(window, cx);
25469        let style = &text_layout_details.editor_style;
25470        let font_id = window.text_system().resolve_font(&style.text.font());
25471        let font_size = style.text.font_size.to_pixels(window.rem_size());
25472        let line_height = style.text.line_height_in_pixels(window.rem_size());
25473        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25474        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25475
25476        CharacterDimensions {
25477            em_width,
25478            em_advance,
25479            line_height,
25480        }
25481    }
25482
25483    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25484        self.load_diff_task.clone()
25485    }
25486
25487    fn read_metadata_from_db(
25488        &mut self,
25489        item_id: u64,
25490        workspace_id: WorkspaceId,
25491        window: &mut Window,
25492        cx: &mut Context<Editor>,
25493    ) {
25494        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25495            && !self.mode.is_minimap()
25496            && WorkspaceSettings::get(None, cx).restore_on_startup
25497                != RestoreOnStartupBehavior::EmptyTab
25498        {
25499            let buffer_snapshot = OnceCell::new();
25500
25501            // Get file path for path-based fold lookup
25502            let file_path: Option<Arc<Path>> =
25503                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25504                    project::File::from_dyn(buffer.read(cx).file())
25505                        .map(|file| Arc::from(file.abs_path(cx)))
25506                });
25507
25508            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25509            let db = EditorDb::global(cx);
25510            let (folds, needs_migration) = if let Some(ref path) = file_path {
25511                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
25512                    && !folds.is_empty()
25513                {
25514                    (Some(folds), false)
25515                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
25516                    && !folds.is_empty()
25517                {
25518                    // Found old editor_folds data, will migrate to file_folds
25519                    (Some(folds), true)
25520                } else {
25521                    (None, false)
25522                }
25523            } else {
25524                // No file path, try editor_folds as fallback
25525                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
25526                (folds.filter(|f| !f.is_empty()), false)
25527            };
25528
25529            if let Some(folds) = folds {
25530                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25531                let snapshot_len = snapshot.len().0;
25532
25533                // Helper: search for fingerprint in buffer, return offset if found
25534                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25535                    // Ensure we start at a character boundary (defensive)
25536                    let search_start = snapshot
25537                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25538                        .0;
25539                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25540
25541                    let mut byte_offset = search_start;
25542                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25543                        if byte_offset > search_end {
25544                            break;
25545                        }
25546                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25547                            return Some(byte_offset);
25548                        }
25549                        byte_offset += ch.len_utf8();
25550                    }
25551                    None
25552                };
25553
25554                // Track search position to handle duplicate fingerprints correctly.
25555                // Folds are stored in document order, so we advance after each match.
25556                let mut search_start = 0usize;
25557
25558                // Collect db_folds for migration (only folds with valid fingerprints)
25559                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25560
25561                let valid_folds: Vec<_> = folds
25562                    .into_iter()
25563                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25564                        // Skip folds without fingerprints (old data before migration)
25565                        let sfp = start_fp?;
25566                        let efp = end_fp?;
25567                        let efp_len = efp.len();
25568
25569                        // Fast path: check if fingerprints match at stored offsets
25570                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25571                        let start_matches = stored_start < snapshot_len
25572                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25573                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25574                        let end_matches = efp_check_pos >= stored_start
25575                            && stored_end <= snapshot_len
25576                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25577
25578                        let (new_start, new_end) = if start_matches && end_matches {
25579                            // Offsets unchanged, use stored values
25580                            (stored_start, stored_end)
25581                        } else if sfp == efp {
25582                            // Short fold: identical fingerprints can only match once per search
25583                            // Use stored fold length to compute new_end
25584                            let new_start = find_fingerprint(&sfp, search_start)?;
25585                            let fold_len = stored_end - stored_start;
25586                            let new_end = new_start + fold_len;
25587                            (new_start, new_end)
25588                        } else {
25589                            // Slow path: search for fingerprints in buffer
25590                            let new_start = find_fingerprint(&sfp, search_start)?;
25591                            // Search for end_fp after start, then add efp_len to get actual fold end
25592                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25593                            let new_end = efp_pos + efp_len;
25594                            (new_start, new_end)
25595                        };
25596
25597                        // Advance search position for next fold
25598                        search_start = new_end;
25599
25600                        // Validate fold makes sense (end must be after start)
25601                        if new_end <= new_start {
25602                            return None;
25603                        }
25604
25605                        // Collect for migration if needed
25606                        if needs_migration {
25607                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25608                        }
25609
25610                        Some(
25611                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25612                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25613                        )
25614                    })
25615                    .collect();
25616
25617                if !valid_folds.is_empty() {
25618                    self.fold_ranges(valid_folds, false, window, cx);
25619
25620                    // Migrate from editor_folds to file_folds if we loaded from old table
25621                    if needs_migration {
25622                        if let Some(ref path) = file_path {
25623                            let path = path.clone();
25624                            let db = EditorDb::global(cx);
25625                            cx.spawn(async move |_, _| {
25626                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
25627                                    .await
25628                                    .log_err();
25629                            })
25630                            .detach();
25631                        }
25632                    }
25633                }
25634            }
25635
25636            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
25637                && !selections.is_empty()
25638            {
25639                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25640                // skip adding the initial selection to selection history
25641                self.selection_history.mode = SelectionHistoryMode::Skipping;
25642                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25643                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25644                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25645                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25646                    }));
25647                });
25648                self.selection_history.mode = SelectionHistoryMode::Normal;
25649            };
25650        }
25651
25652        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25653    }
25654
25655    /// Load folds from the file_folds database table by file path.
25656    /// Used when manually opening a file that was previously closed.
25657    fn load_folds_from_db(
25658        &mut self,
25659        workspace_id: WorkspaceId,
25660        file_path: PathBuf,
25661        window: &mut Window,
25662        cx: &mut Context<Editor>,
25663    ) {
25664        if self.mode.is_minimap()
25665            || WorkspaceSettings::get(None, cx).restore_on_startup
25666                == RestoreOnStartupBehavior::EmptyTab
25667        {
25668            return;
25669        }
25670
25671        let Some(folds) = EditorDb::global(cx)
25672            .get_file_folds(workspace_id, &file_path)
25673            .log_err()
25674        else {
25675            return;
25676        };
25677        if folds.is_empty() {
25678            return;
25679        }
25680
25681        let snapshot = self.buffer.read(cx).snapshot(cx);
25682        let snapshot_len = snapshot.len().0;
25683
25684        // Helper: search for fingerprint in buffer, return offset if found
25685        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25686            let search_start = snapshot
25687                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25688                .0;
25689            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25690
25691            let mut byte_offset = search_start;
25692            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25693                if byte_offset > search_end {
25694                    break;
25695                }
25696                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25697                    return Some(byte_offset);
25698                }
25699                byte_offset += ch.len_utf8();
25700            }
25701            None
25702        };
25703
25704        let mut search_start = 0usize;
25705
25706        let valid_folds: Vec<_> = folds
25707            .into_iter()
25708            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25709                let sfp = start_fp?;
25710                let efp = end_fp?;
25711                let efp_len = efp.len();
25712
25713                let start_matches = stored_start < snapshot_len
25714                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25715                let efp_check_pos = stored_end.saturating_sub(efp_len);
25716                let end_matches = efp_check_pos >= stored_start
25717                    && stored_end <= snapshot_len
25718                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25719
25720                let (new_start, new_end) = if start_matches && end_matches {
25721                    (stored_start, stored_end)
25722                } else if sfp == efp {
25723                    let new_start = find_fingerprint(&sfp, search_start)?;
25724                    let fold_len = stored_end - stored_start;
25725                    let new_end = new_start + fold_len;
25726                    (new_start, new_end)
25727                } else {
25728                    let new_start = find_fingerprint(&sfp, search_start)?;
25729                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25730                    let new_end = efp_pos + efp_len;
25731                    (new_start, new_end)
25732                };
25733
25734                search_start = new_end;
25735
25736                if new_end <= new_start {
25737                    return None;
25738                }
25739
25740                Some(
25741                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25742                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25743                )
25744            })
25745            .collect();
25746
25747        if !valid_folds.is_empty() {
25748            self.fold_ranges(valid_folds, false, window, cx);
25749        }
25750    }
25751
25752    fn lsp_data_enabled(&self) -> bool {
25753        self.enable_lsp_data && self.mode().is_full()
25754    }
25755
25756    fn update_lsp_data(
25757        &mut self,
25758        for_buffer: Option<BufferId>,
25759        window: &mut Window,
25760        cx: &mut Context<'_, Self>,
25761    ) {
25762        if !self.lsp_data_enabled() {
25763            return;
25764        }
25765
25766        if let Some(buffer_id) = for_buffer {
25767            self.pull_diagnostics(buffer_id, window, cx);
25768        }
25769        self.refresh_semantic_tokens(for_buffer, None, cx);
25770        self.refresh_document_colors(for_buffer, window, cx);
25771        self.refresh_folding_ranges(for_buffer, window, cx);
25772        self.refresh_document_symbols(for_buffer, cx);
25773    }
25774
25775    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25776        if !self.lsp_data_enabled() {
25777            return;
25778        }
25779        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25780            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25781        }
25782    }
25783
25784    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25785        if !self.lsp_data_enabled() {
25786            return;
25787        }
25788
25789        if !self.registered_buffers.contains_key(&buffer_id)
25790            && let Some(project) = self.project.as_ref()
25791        {
25792            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25793                project.update(cx, |project, cx| {
25794                    self.registered_buffers.insert(
25795                        buffer_id,
25796                        project.register_buffer_with_language_servers(&buffer, cx),
25797                    );
25798                });
25799            } else {
25800                self.registered_buffers.remove(&buffer_id);
25801            }
25802        }
25803    }
25804
25805    fn create_style(&self, cx: &App) -> EditorStyle {
25806        let settings = ThemeSettings::get_global(cx);
25807
25808        let mut text_style = match self.mode {
25809            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25810                color: cx.theme().colors().editor_foreground,
25811                font_family: settings.ui_font.family.clone(),
25812                font_features: settings.ui_font.features.clone(),
25813                font_fallbacks: settings.ui_font.fallbacks.clone(),
25814                font_size: rems(0.875).into(),
25815                font_weight: settings.ui_font.weight,
25816                line_height: relative(settings.buffer_line_height.value()),
25817                ..Default::default()
25818            },
25819            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25820                color: cx.theme().colors().editor_foreground,
25821                font_family: settings.buffer_font.family.clone(),
25822                font_features: settings.buffer_font.features.clone(),
25823                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25824                font_size: settings.buffer_font_size(cx).into(),
25825                font_weight: settings.buffer_font.weight,
25826                line_height: relative(settings.buffer_line_height.value()),
25827                ..Default::default()
25828            },
25829        };
25830        if let Some(text_style_refinement) = &self.text_style_refinement {
25831            text_style.refine(text_style_refinement)
25832        }
25833
25834        let background = match self.mode {
25835            EditorMode::SingleLine => cx.theme().system().transparent,
25836            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25837            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25838            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25839        };
25840
25841        EditorStyle {
25842            background,
25843            border: cx.theme().colors().border,
25844            local_player: cx.theme().players().local(),
25845            text: text_style,
25846            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25847            syntax: cx.theme().syntax().clone(),
25848            status: cx.theme().status().clone(),
25849            inlay_hints_style: make_inlay_hints_style(cx),
25850            edit_prediction_styles: make_suggestion_styles(cx),
25851            unnecessary_code_fade: settings.unnecessary_code_fade,
25852            show_underlines: self.diagnostics_enabled(),
25853        }
25854    }
25855
25856    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25857        let multibuffer = self.buffer().read(cx);
25858        let is_singleton = multibuffer.is_singleton();
25859        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25860        let buffer = multibuffer.buffer(*buffer_id)?;
25861
25862        let buffer = buffer.read(cx);
25863        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25864        let mut breadcrumbs = if is_singleton {
25865            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25866                buffer
25867                    .snapshot()
25868                    .resolve_file_path(
25869                        self.project
25870                            .as_ref()
25871                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25872                            .unwrap_or_default(),
25873                        cx,
25874                    )
25875                    .unwrap_or_else(|| {
25876                        if multibuffer.is_singleton() {
25877                            multibuffer.title(cx).to_string()
25878                        } else {
25879                            "untitled".to_string()
25880                        }
25881                    })
25882            });
25883            vec![HighlightedText {
25884                text: text.into(),
25885                highlights: vec![],
25886            }]
25887        } else {
25888            vec![]
25889        };
25890
25891        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25892            text: symbol.text.clone().into(),
25893            highlights: symbol.highlight_ranges.clone(),
25894        }));
25895        Some(breadcrumbs)
25896    }
25897
25898    fn disable_lsp_data(&mut self) {
25899        self.enable_lsp_data = false;
25900    }
25901
25902    fn disable_runnables(&mut self) {
25903        self.enable_runnables = false;
25904    }
25905
25906    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25907        self.register_visible_buffers(cx);
25908        self.colorize_brackets(false, cx);
25909        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25910        if !self.buffer().read(cx).is_singleton() {
25911            self.update_lsp_data(None, window, cx);
25912            self.refresh_runnables(None, window, cx);
25913        }
25914    }
25915}
25916
25917fn edit_for_markdown_paste<'a>(
25918    buffer: &MultiBufferSnapshot,
25919    range: Range<MultiBufferOffset>,
25920    to_insert: &'a str,
25921    url: Option<url::Url>,
25922) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25923    if url.is_none() {
25924        return (range, Cow::Borrowed(to_insert));
25925    };
25926
25927    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25928
25929    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25930        Cow::Borrowed(to_insert)
25931    } else {
25932        Cow::Owned(format!("[{old_text}]({to_insert})"))
25933    };
25934    (range, new_text)
25935}
25936
25937fn process_completion_for_edit(
25938    completion: &Completion,
25939    intent: CompletionIntent,
25940    buffer: &Entity<Buffer>,
25941    cursor_position: &text::Anchor,
25942    cx: &mut Context<Editor>,
25943) -> CompletionEdit {
25944    let buffer = buffer.read(cx);
25945    let buffer_snapshot = buffer.snapshot();
25946    let (snippet, new_text) = if completion.is_snippet() {
25947        let mut snippet_source = completion.new_text.clone();
25948        // Workaround for typescript language server issues so that methods don't expand within
25949        // strings and functions with type expressions. The previous point is used because the query
25950        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25951        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25952        let previous_point = if previous_point.column > 0 {
25953            cursor_position.to_previous_offset(&buffer_snapshot)
25954        } else {
25955            cursor_position.to_offset(&buffer_snapshot)
25956        };
25957        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25958            && scope.prefers_label_for_snippet_in_completion()
25959            && let Some(label) = completion.label()
25960            && matches!(
25961                completion.kind(),
25962                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25963            )
25964        {
25965            snippet_source = label;
25966        }
25967        match Snippet::parse(&snippet_source).log_err() {
25968            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25969            None => (None, completion.new_text.clone()),
25970        }
25971    } else {
25972        (None, completion.new_text.clone())
25973    };
25974
25975    let mut range_to_replace = {
25976        let replace_range = &completion.replace_range;
25977        if let CompletionSource::Lsp {
25978            insert_range: Some(insert_range),
25979            ..
25980        } = &completion.source
25981        {
25982            debug_assert_eq!(
25983                insert_range.start, replace_range.start,
25984                "insert_range and replace_range should start at the same position"
25985            );
25986            debug_assert!(
25987                insert_range
25988                    .start
25989                    .cmp(cursor_position, &buffer_snapshot)
25990                    .is_le(),
25991                "insert_range should start before or at cursor position"
25992            );
25993            debug_assert!(
25994                replace_range
25995                    .start
25996                    .cmp(cursor_position, &buffer_snapshot)
25997                    .is_le(),
25998                "replace_range should start before or at cursor position"
25999            );
26000
26001            let should_replace = match intent {
26002                CompletionIntent::CompleteWithInsert => false,
26003                CompletionIntent::CompleteWithReplace => true,
26004                CompletionIntent::Complete | CompletionIntent::Compose => {
26005                    let insert_mode = LanguageSettings::for_buffer(&buffer, cx)
26006                        .completions
26007                        .lsp_insert_mode;
26008                    match insert_mode {
26009                        LspInsertMode::Insert => false,
26010                        LspInsertMode::Replace => true,
26011                        LspInsertMode::ReplaceSubsequence => {
26012                            let mut text_to_replace = buffer.chars_for_range(
26013                                buffer.anchor_before(replace_range.start)
26014                                    ..buffer.anchor_after(replace_range.end),
26015                            );
26016                            let mut current_needle = text_to_replace.next();
26017                            for haystack_ch in completion.label.text.chars() {
26018                                if let Some(needle_ch) = current_needle
26019                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
26020                                {
26021                                    current_needle = text_to_replace.next();
26022                                }
26023                            }
26024                            current_needle.is_none()
26025                        }
26026                        LspInsertMode::ReplaceSuffix => {
26027                            if replace_range
26028                                .end
26029                                .cmp(cursor_position, &buffer_snapshot)
26030                                .is_gt()
26031                            {
26032                                let range_after_cursor = *cursor_position..replace_range.end;
26033                                let text_after_cursor = buffer
26034                                    .text_for_range(
26035                                        buffer.anchor_before(range_after_cursor.start)
26036                                            ..buffer.anchor_after(range_after_cursor.end),
26037                                    )
26038                                    .collect::<String>()
26039                                    .to_ascii_lowercase();
26040                                completion
26041                                    .label
26042                                    .text
26043                                    .to_ascii_lowercase()
26044                                    .ends_with(&text_after_cursor)
26045                            } else {
26046                                true
26047                            }
26048                        }
26049                    }
26050                }
26051            };
26052
26053            if should_replace {
26054                replace_range.clone()
26055            } else {
26056                insert_range.clone()
26057            }
26058        } else {
26059            replace_range.clone()
26060        }
26061    };
26062
26063    if range_to_replace
26064        .end
26065        .cmp(cursor_position, &buffer_snapshot)
26066        .is_lt()
26067    {
26068        range_to_replace.end = *cursor_position;
26069    }
26070
26071    let replace_range = range_to_replace.to_offset(buffer);
26072    CompletionEdit {
26073        new_text,
26074        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
26075        snippet,
26076    }
26077}
26078
26079struct CompletionEdit {
26080    new_text: String,
26081    replace_range: Range<BufferOffset>,
26082    snippet: Option<Snippet>,
26083}
26084
26085fn comment_delimiter_for_newline(
26086    start_point: &Point,
26087    buffer: &MultiBufferSnapshot,
26088    language: &LanguageScope,
26089) -> Option<Arc<str>> {
26090    let delimiters = language.line_comment_prefixes();
26091    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
26092    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26093
26094    let num_of_whitespaces = snapshot
26095        .chars_for_range(range.clone())
26096        .take_while(|c| c.is_whitespace())
26097        .count();
26098    let comment_candidate = snapshot
26099        .chars_for_range(range.clone())
26100        .skip(num_of_whitespaces)
26101        .take(max_len_of_delimiter + 2)
26102        .collect::<String>();
26103    let (delimiter, trimmed_len, is_repl) = delimiters
26104        .iter()
26105        .filter_map(|delimiter| {
26106            let prefix = delimiter.trim_end();
26107            if comment_candidate.starts_with(prefix) {
26108                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
26109                {
26110                    stripped_comment.starts_with(" %%")
26111                } else {
26112                    false
26113                };
26114                Some((delimiter, prefix.len(), is_repl))
26115            } else {
26116                None
26117            }
26118        })
26119        .max_by_key(|(_, len, _)| *len)?;
26120
26121    if let Some(BlockCommentConfig {
26122        start: block_start, ..
26123    }) = language.block_comment()
26124    {
26125        let block_start_trimmed = block_start.trim_end();
26126        if block_start_trimmed.starts_with(delimiter.trim_end()) {
26127            let line_content = snapshot
26128                .chars_for_range(range.clone())
26129                .skip(num_of_whitespaces)
26130                .take(block_start_trimmed.len())
26131                .collect::<String>();
26132
26133            if line_content.starts_with(block_start_trimmed) {
26134                return None;
26135            }
26136        }
26137    }
26138
26139    let cursor_is_placed_after_comment_marker =
26140        num_of_whitespaces + trimmed_len <= start_point.column as usize;
26141    if cursor_is_placed_after_comment_marker {
26142        if !is_repl {
26143            return Some(delimiter.clone());
26144        }
26145
26146        let line_content_after_cursor: String = snapshot
26147            .chars_for_range(range)
26148            .skip(start_point.column as usize)
26149            .collect();
26150
26151        if line_content_after_cursor.trim().is_empty() {
26152            return None;
26153        } else {
26154            return Some(delimiter.clone());
26155        }
26156    } else {
26157        None
26158    }
26159}
26160
26161fn documentation_delimiter_for_newline(
26162    start_point: &Point,
26163    buffer: &MultiBufferSnapshot,
26164    language: &LanguageScope,
26165    newline_config: &mut NewlineConfig,
26166) -> Option<Arc<str>> {
26167    let BlockCommentConfig {
26168        start: start_tag,
26169        end: end_tag,
26170        prefix: delimiter,
26171        tab_size: len,
26172    } = language.documentation_comment()?;
26173    let is_within_block_comment = buffer
26174        .language_scope_at(*start_point)
26175        .is_some_and(|scope| scope.override_name() == Some("comment"));
26176    if !is_within_block_comment {
26177        return None;
26178    }
26179
26180    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26181
26182    let num_of_whitespaces = snapshot
26183        .chars_for_range(range.clone())
26184        .take_while(|c| c.is_whitespace())
26185        .count();
26186
26187    // 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.
26188    let column = start_point.column;
26189    let cursor_is_after_start_tag = {
26190        let start_tag_len = start_tag.len();
26191        let start_tag_line = snapshot
26192            .chars_for_range(range.clone())
26193            .skip(num_of_whitespaces)
26194            .take(start_tag_len)
26195            .collect::<String>();
26196        if start_tag_line.starts_with(start_tag.as_ref()) {
26197            num_of_whitespaces + start_tag_len <= column as usize
26198        } else {
26199            false
26200        }
26201    };
26202
26203    let cursor_is_after_delimiter = {
26204        let delimiter_trim = delimiter.trim_end();
26205        let delimiter_line = snapshot
26206            .chars_for_range(range.clone())
26207            .skip(num_of_whitespaces)
26208            .take(delimiter_trim.len())
26209            .collect::<String>();
26210        if delimiter_line.starts_with(delimiter_trim) {
26211            num_of_whitespaces + delimiter_trim.len() <= column as usize
26212        } else {
26213            false
26214        }
26215    };
26216
26217    let mut needs_extra_line = false;
26218    let mut extra_line_additional_indent = IndentSize::spaces(0);
26219
26220    let cursor_is_before_end_tag_if_exists = {
26221        let mut char_position = 0u32;
26222        let mut end_tag_offset = None;
26223
26224        'outer: for chunk in snapshot.text_for_range(range) {
26225            if let Some(byte_pos) = chunk.find(&**end_tag) {
26226                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26227                end_tag_offset = Some(char_position + chars_before_match);
26228                break 'outer;
26229            }
26230            char_position += chunk.chars().count() as u32;
26231        }
26232
26233        if let Some(end_tag_offset) = end_tag_offset {
26234            let cursor_is_before_end_tag = column <= end_tag_offset;
26235            if cursor_is_after_start_tag {
26236                if cursor_is_before_end_tag {
26237                    needs_extra_line = true;
26238                }
26239                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26240                if cursor_is_at_start_of_end_tag {
26241                    extra_line_additional_indent.len = *len;
26242                }
26243            }
26244            cursor_is_before_end_tag
26245        } else {
26246            true
26247        }
26248    };
26249
26250    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26251        && cursor_is_before_end_tag_if_exists
26252    {
26253        let additional_indent = if cursor_is_after_start_tag {
26254            IndentSize::spaces(*len)
26255        } else {
26256            IndentSize::spaces(0)
26257        };
26258
26259        *newline_config = NewlineConfig::Newline {
26260            additional_indent,
26261            extra_line_additional_indent: if needs_extra_line {
26262                Some(extra_line_additional_indent)
26263            } else {
26264                None
26265            },
26266            prevent_auto_indent: true,
26267        };
26268        Some(delimiter.clone())
26269    } else {
26270        None
26271    }
26272}
26273
26274const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26275
26276fn list_delimiter_for_newline(
26277    start_point: &Point,
26278    buffer: &MultiBufferSnapshot,
26279    language: &LanguageScope,
26280    newline_config: &mut NewlineConfig,
26281) -> Option<Arc<str>> {
26282    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26283
26284    let num_of_whitespaces = snapshot
26285        .chars_for_range(range.clone())
26286        .take_while(|c| c.is_whitespace())
26287        .count();
26288
26289    let task_list_entries: Vec<_> = language
26290        .task_list()
26291        .into_iter()
26292        .flat_map(|config| {
26293            config
26294                .prefixes
26295                .iter()
26296                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26297        })
26298        .collect();
26299    let unordered_list_entries: Vec<_> = language
26300        .unordered_list()
26301        .iter()
26302        .map(|marker| (marker.as_ref(), marker.as_ref()))
26303        .collect();
26304
26305    let all_entries: Vec<_> = task_list_entries
26306        .into_iter()
26307        .chain(unordered_list_entries)
26308        .collect();
26309
26310    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26311        let candidate: String = snapshot
26312            .chars_for_range(range.clone())
26313            .skip(num_of_whitespaces)
26314            .take(max_prefix_len)
26315            .collect();
26316
26317        if let Some((prefix, continuation)) = all_entries
26318            .iter()
26319            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26320            .max_by_key(|(prefix, _)| prefix.len())
26321        {
26322            let end_of_prefix = num_of_whitespaces + prefix.len();
26323            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26324            let has_content_after_marker = snapshot
26325                .chars_for_range(range)
26326                .skip(end_of_prefix)
26327                .any(|c| !c.is_whitespace());
26328
26329            if has_content_after_marker && cursor_is_after_prefix {
26330                return Some((*continuation).into());
26331            }
26332
26333            if start_point.column as usize == end_of_prefix {
26334                if num_of_whitespaces == 0 {
26335                    *newline_config = NewlineConfig::ClearCurrentLine;
26336                } else {
26337                    *newline_config = NewlineConfig::UnindentCurrentLine {
26338                        continuation: (*continuation).into(),
26339                    };
26340                }
26341            }
26342
26343            return None;
26344        }
26345    }
26346
26347    let candidate: String = snapshot
26348        .chars_for_range(range.clone())
26349        .skip(num_of_whitespaces)
26350        .take(ORDERED_LIST_MAX_MARKER_LEN)
26351        .collect();
26352
26353    for ordered_config in language.ordered_list() {
26354        let regex = match Regex::new(&ordered_config.pattern) {
26355            Ok(r) => r,
26356            Err(_) => continue,
26357        };
26358
26359        if let Some(captures) = regex.captures(&candidate) {
26360            let full_match = captures.get(0)?;
26361            let marker_len = full_match.len();
26362            let end_of_prefix = num_of_whitespaces + marker_len;
26363            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26364
26365            let has_content_after_marker = snapshot
26366                .chars_for_range(range)
26367                .skip(end_of_prefix)
26368                .any(|c| !c.is_whitespace());
26369
26370            if has_content_after_marker && cursor_is_after_prefix {
26371                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26372                let continuation = ordered_config
26373                    .format
26374                    .replace("{1}", &(number + 1).to_string());
26375                return Some(continuation.into());
26376            }
26377
26378            if start_point.column as usize == end_of_prefix {
26379                let continuation = ordered_config.format.replace("{1}", "1");
26380                if num_of_whitespaces == 0 {
26381                    *newline_config = NewlineConfig::ClearCurrentLine;
26382                } else {
26383                    *newline_config = NewlineConfig::UnindentCurrentLine {
26384                        continuation: continuation.into(),
26385                    };
26386                }
26387            }
26388
26389            return None;
26390        }
26391    }
26392
26393    None
26394}
26395
26396fn is_list_prefix_row(
26397    row: MultiBufferRow,
26398    buffer: &MultiBufferSnapshot,
26399    language: &LanguageScope,
26400) -> bool {
26401    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26402        return false;
26403    };
26404
26405    let num_of_whitespaces = snapshot
26406        .chars_for_range(range.clone())
26407        .take_while(|c| c.is_whitespace())
26408        .count();
26409
26410    let task_list_prefixes: Vec<_> = language
26411        .task_list()
26412        .into_iter()
26413        .flat_map(|config| {
26414            config
26415                .prefixes
26416                .iter()
26417                .map(|p| p.as_ref())
26418                .collect::<Vec<_>>()
26419        })
26420        .collect();
26421    let unordered_list_markers: Vec<_> = language
26422        .unordered_list()
26423        .iter()
26424        .map(|marker| marker.as_ref())
26425        .collect();
26426    let all_prefixes: Vec<_> = task_list_prefixes
26427        .into_iter()
26428        .chain(unordered_list_markers)
26429        .collect();
26430    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26431        let candidate: String = snapshot
26432            .chars_for_range(range.clone())
26433            .skip(num_of_whitespaces)
26434            .take(max_prefix_len)
26435            .collect();
26436        if all_prefixes
26437            .iter()
26438            .any(|prefix| candidate.starts_with(*prefix))
26439        {
26440            return true;
26441        }
26442    }
26443
26444    let ordered_list_candidate: String = snapshot
26445        .chars_for_range(range)
26446        .skip(num_of_whitespaces)
26447        .take(ORDERED_LIST_MAX_MARKER_LEN)
26448        .collect();
26449    for ordered_config in language.ordered_list() {
26450        let regex = match Regex::new(&ordered_config.pattern) {
26451            Ok(r) => r,
26452            Err(_) => continue,
26453        };
26454        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26455            return captures.get(0).is_some();
26456        }
26457    }
26458
26459    false
26460}
26461
26462#[derive(Debug)]
26463enum NewlineConfig {
26464    /// Insert newline with optional additional indent and optional extra blank line
26465    Newline {
26466        additional_indent: IndentSize,
26467        extra_line_additional_indent: Option<IndentSize>,
26468        prevent_auto_indent: bool,
26469    },
26470    /// Clear the current line
26471    ClearCurrentLine,
26472    /// Unindent the current line and add continuation
26473    UnindentCurrentLine { continuation: Arc<str> },
26474}
26475
26476impl NewlineConfig {
26477    fn has_extra_line(&self) -> bool {
26478        matches!(
26479            self,
26480            Self::Newline {
26481                extra_line_additional_indent: Some(_),
26482                ..
26483            }
26484        )
26485    }
26486
26487    fn insert_extra_newline_brackets(
26488        buffer: &MultiBufferSnapshot,
26489        range: Range<MultiBufferOffset>,
26490        language: &language::LanguageScope,
26491    ) -> bool {
26492        let leading_whitespace_len = buffer
26493            .reversed_chars_at(range.start)
26494            .take_while(|c| c.is_whitespace() && *c != '\n')
26495            .map(|c| c.len_utf8())
26496            .sum::<usize>();
26497        let trailing_whitespace_len = buffer
26498            .chars_at(range.end)
26499            .take_while(|c| c.is_whitespace() && *c != '\n')
26500            .map(|c| c.len_utf8())
26501            .sum::<usize>();
26502        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26503
26504        language.brackets().any(|(pair, enabled)| {
26505            let pair_start = pair.start.trim_end();
26506            let pair_end = pair.end.trim_start();
26507
26508            enabled
26509                && pair.newline
26510                && buffer.contains_str_at(range.end, pair_end)
26511                && buffer.contains_str_at(
26512                    range.start.saturating_sub_usize(pair_start.len()),
26513                    pair_start,
26514                )
26515        })
26516    }
26517
26518    fn insert_extra_newline_tree_sitter(
26519        buffer: &MultiBufferSnapshot,
26520        range: Range<MultiBufferOffset>,
26521    ) -> bool {
26522        let (buffer, range) = match buffer
26523            .range_to_buffer_ranges(range.start..=range.end)
26524            .as_slice()
26525        {
26526            [(buffer, range, _)] => (*buffer, range.clone()),
26527            _ => return false,
26528        };
26529        let pair = {
26530            let mut result: Option<BracketMatch<usize>> = None;
26531
26532            for pair in buffer
26533                .all_bracket_ranges(range.start.0..range.end.0)
26534                .filter(move |pair| {
26535                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26536                })
26537            {
26538                let len = pair.close_range.end - pair.open_range.start;
26539
26540                if let Some(existing) = &result {
26541                    let existing_len = existing.close_range.end - existing.open_range.start;
26542                    if len > existing_len {
26543                        continue;
26544                    }
26545                }
26546
26547                result = Some(pair);
26548            }
26549
26550            result
26551        };
26552        let Some(pair) = pair else {
26553            return false;
26554        };
26555        pair.newline_only
26556            && buffer
26557                .chars_for_range(pair.open_range.end..range.start.0)
26558                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26559                .all(|c| c.is_whitespace() && c != '\n')
26560    }
26561}
26562
26563fn update_uncommitted_diff_for_buffer(
26564    editor: Entity<Editor>,
26565    project: &Entity<Project>,
26566    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26567    buffer: Entity<MultiBuffer>,
26568    cx: &mut App,
26569) -> Task<()> {
26570    let mut tasks = Vec::new();
26571    project.update(cx, |project, cx| {
26572        for buffer in buffers {
26573            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26574                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26575            }
26576        }
26577    });
26578    cx.spawn(async move |cx| {
26579        let diffs = future::join_all(tasks).await;
26580        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26581            return;
26582        }
26583
26584        buffer.update(cx, |buffer, cx| {
26585            for diff in diffs.into_iter().flatten() {
26586                buffer.add_diff(diff, cx);
26587            }
26588        });
26589    })
26590}
26591
26592fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26593    let tab_size = tab_size.get() as usize;
26594    let mut width = offset;
26595
26596    for ch in text.chars() {
26597        width += if ch == '\t' {
26598            tab_size - (width % tab_size)
26599        } else {
26600            1
26601        };
26602    }
26603
26604    width - offset
26605}
26606
26607#[cfg(test)]
26608mod tests {
26609    use super::*;
26610
26611    #[test]
26612    fn test_string_size_with_expanded_tabs() {
26613        let nz = |val| NonZeroU32::new(val).unwrap();
26614        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26615        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26616        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26617        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26618        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26619        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26620        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26621        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26622    }
26623}
26624
26625/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26626struct WordBreakingTokenizer<'a> {
26627    input: &'a str,
26628}
26629
26630impl<'a> WordBreakingTokenizer<'a> {
26631    fn new(input: &'a str) -> Self {
26632        Self { input }
26633    }
26634}
26635
26636fn is_char_ideographic(ch: char) -> bool {
26637    use unicode_script::Script::*;
26638    use unicode_script::UnicodeScript;
26639    matches!(ch.script(), Han | Tangut | Yi)
26640}
26641
26642fn is_grapheme_ideographic(text: &str) -> bool {
26643    text.chars().any(is_char_ideographic)
26644}
26645
26646fn is_grapheme_whitespace(text: &str) -> bool {
26647    text.chars().any(|x| x.is_whitespace())
26648}
26649
26650fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26651    text.chars()
26652        .next()
26653        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26654}
26655
26656#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26657enum WordBreakToken<'a> {
26658    Word { token: &'a str, grapheme_len: usize },
26659    InlineWhitespace { token: &'a str, grapheme_len: usize },
26660    Newline,
26661}
26662
26663impl<'a> Iterator for WordBreakingTokenizer<'a> {
26664    /// Yields a span, the count of graphemes in the token, and whether it was
26665    /// whitespace. Note that it also breaks at word boundaries.
26666    type Item = WordBreakToken<'a>;
26667
26668    fn next(&mut self) -> Option<Self::Item> {
26669        use unicode_segmentation::UnicodeSegmentation;
26670        if self.input.is_empty() {
26671            return None;
26672        }
26673
26674        let mut iter = self.input.graphemes(true).peekable();
26675        let mut offset = 0;
26676        let mut grapheme_len = 0;
26677        if let Some(first_grapheme) = iter.next() {
26678            let is_newline = first_grapheme == "\n";
26679            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26680            offset += first_grapheme.len();
26681            grapheme_len += 1;
26682            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26683                if let Some(grapheme) = iter.peek().copied()
26684                    && should_stay_with_preceding_ideograph(grapheme)
26685                {
26686                    offset += grapheme.len();
26687                    grapheme_len += 1;
26688                }
26689            } else {
26690                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26691                let mut next_word_bound = words.peek().copied();
26692                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26693                    next_word_bound = words.next();
26694                }
26695                while let Some(grapheme) = iter.peek().copied() {
26696                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26697                        break;
26698                    };
26699                    if is_grapheme_whitespace(grapheme) != is_whitespace
26700                        || (grapheme == "\n") != is_newline
26701                    {
26702                        break;
26703                    };
26704                    offset += grapheme.len();
26705                    grapheme_len += 1;
26706                    iter.next();
26707                }
26708            }
26709            let token = &self.input[..offset];
26710            self.input = &self.input[offset..];
26711            if token == "\n" {
26712                Some(WordBreakToken::Newline)
26713            } else if is_whitespace {
26714                Some(WordBreakToken::InlineWhitespace {
26715                    token,
26716                    grapheme_len,
26717                })
26718            } else {
26719                Some(WordBreakToken::Word {
26720                    token,
26721                    grapheme_len,
26722                })
26723            }
26724        } else {
26725            None
26726        }
26727    }
26728}
26729
26730#[test]
26731fn test_word_breaking_tokenizer() {
26732    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26733        ("", &[]),
26734        ("  ", &[whitespace("  ", 2)]),
26735        ("Ʒ", &[word("Ʒ", 1)]),
26736        ("Ǽ", &[word("Ǽ", 1)]),
26737        ("", &[word("", 1)]),
26738        ("⋑⋑", &[word("⋑⋑", 2)]),
26739        (
26740            "原理,进而",
26741            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26742        ),
26743        (
26744            "hello world",
26745            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26746        ),
26747        (
26748            "hello, world",
26749            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26750        ),
26751        (
26752            "  hello world",
26753            &[
26754                whitespace("  ", 2),
26755                word("hello", 5),
26756                whitespace(" ", 1),
26757                word("world", 5),
26758            ],
26759        ),
26760        (
26761            "这是什么 \n 钢笔",
26762            &[
26763                word("", 1),
26764                word("", 1),
26765                word("", 1),
26766                word("", 1),
26767                whitespace(" ", 1),
26768                newline(),
26769                whitespace(" ", 1),
26770                word("", 1),
26771                word("", 1),
26772            ],
26773        ),
26774        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26775    ];
26776
26777    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26778        WordBreakToken::Word {
26779            token,
26780            grapheme_len,
26781        }
26782    }
26783
26784    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26785        WordBreakToken::InlineWhitespace {
26786            token,
26787            grapheme_len,
26788        }
26789    }
26790
26791    fn newline() -> WordBreakToken<'static> {
26792        WordBreakToken::Newline
26793    }
26794
26795    for (input, result) in tests {
26796        assert_eq!(
26797            WordBreakingTokenizer::new(input)
26798                .collect::<Vec<_>>()
26799                .as_slice(),
26800            *result,
26801        );
26802    }
26803}
26804
26805fn wrap_with_prefix(
26806    first_line_prefix: String,
26807    subsequent_lines_prefix: String,
26808    unwrapped_text: String,
26809    wrap_column: usize,
26810    tab_size: NonZeroU32,
26811    preserve_existing_whitespace: bool,
26812) -> String {
26813    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26814    let subsequent_lines_prefix_len =
26815        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26816    let mut wrapped_text = String::new();
26817    let mut current_line = first_line_prefix;
26818    let mut is_first_line = true;
26819
26820    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26821    let mut current_line_len = first_line_prefix_len;
26822    let mut in_whitespace = false;
26823    for token in tokenizer {
26824        let have_preceding_whitespace = in_whitespace;
26825        match token {
26826            WordBreakToken::Word {
26827                token,
26828                grapheme_len,
26829            } => {
26830                in_whitespace = false;
26831                let current_prefix_len = if is_first_line {
26832                    first_line_prefix_len
26833                } else {
26834                    subsequent_lines_prefix_len
26835                };
26836                if current_line_len + grapheme_len > wrap_column
26837                    && current_line_len != current_prefix_len
26838                {
26839                    wrapped_text.push_str(current_line.trim_end());
26840                    wrapped_text.push('\n');
26841                    is_first_line = false;
26842                    current_line = subsequent_lines_prefix.clone();
26843                    current_line_len = subsequent_lines_prefix_len;
26844                }
26845                current_line.push_str(token);
26846                current_line_len += grapheme_len;
26847            }
26848            WordBreakToken::InlineWhitespace {
26849                mut token,
26850                mut grapheme_len,
26851            } => {
26852                in_whitespace = true;
26853                if have_preceding_whitespace && !preserve_existing_whitespace {
26854                    continue;
26855                }
26856                if !preserve_existing_whitespace {
26857                    // Keep a single whitespace grapheme as-is
26858                    if let Some(first) =
26859                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26860                    {
26861                        token = first;
26862                    } else {
26863                        token = " ";
26864                    }
26865                    grapheme_len = 1;
26866                }
26867                let current_prefix_len = if is_first_line {
26868                    first_line_prefix_len
26869                } else {
26870                    subsequent_lines_prefix_len
26871                };
26872                if current_line_len + grapheme_len > wrap_column {
26873                    wrapped_text.push_str(current_line.trim_end());
26874                    wrapped_text.push('\n');
26875                    is_first_line = false;
26876                    current_line = subsequent_lines_prefix.clone();
26877                    current_line_len = subsequent_lines_prefix_len;
26878                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26879                    current_line.push_str(token);
26880                    current_line_len += grapheme_len;
26881                }
26882            }
26883            WordBreakToken::Newline => {
26884                in_whitespace = true;
26885                let current_prefix_len = if is_first_line {
26886                    first_line_prefix_len
26887                } else {
26888                    subsequent_lines_prefix_len
26889                };
26890                if preserve_existing_whitespace {
26891                    wrapped_text.push_str(current_line.trim_end());
26892                    wrapped_text.push('\n');
26893                    is_first_line = false;
26894                    current_line = subsequent_lines_prefix.clone();
26895                    current_line_len = subsequent_lines_prefix_len;
26896                } else if have_preceding_whitespace {
26897                    continue;
26898                } else if current_line_len + 1 > wrap_column
26899                    && current_line_len != current_prefix_len
26900                {
26901                    wrapped_text.push_str(current_line.trim_end());
26902                    wrapped_text.push('\n');
26903                    is_first_line = false;
26904                    current_line = subsequent_lines_prefix.clone();
26905                    current_line_len = subsequent_lines_prefix_len;
26906                } else if current_line_len != current_prefix_len {
26907                    current_line.push(' ');
26908                    current_line_len += 1;
26909                }
26910            }
26911        }
26912    }
26913
26914    if !current_line.is_empty() {
26915        wrapped_text.push_str(&current_line);
26916    }
26917    wrapped_text
26918}
26919
26920#[test]
26921fn test_wrap_with_prefix() {
26922    assert_eq!(
26923        wrap_with_prefix(
26924            "# ".to_string(),
26925            "# ".to_string(),
26926            "abcdefg".to_string(),
26927            4,
26928            NonZeroU32::new(4).unwrap(),
26929            false,
26930        ),
26931        "# abcdefg"
26932    );
26933    assert_eq!(
26934        wrap_with_prefix(
26935            "".to_string(),
26936            "".to_string(),
26937            "\thello world".to_string(),
26938            8,
26939            NonZeroU32::new(4).unwrap(),
26940            false,
26941        ),
26942        "hello\nworld"
26943    );
26944    assert_eq!(
26945        wrap_with_prefix(
26946            "// ".to_string(),
26947            "// ".to_string(),
26948            "xx \nyy zz aa bb cc".to_string(),
26949            12,
26950            NonZeroU32::new(4).unwrap(),
26951            false,
26952        ),
26953        "// xx yy zz\n// aa bb cc"
26954    );
26955    assert_eq!(
26956        wrap_with_prefix(
26957            String::new(),
26958            String::new(),
26959            "这是什么 \n 钢笔".to_string(),
26960            3,
26961            NonZeroU32::new(4).unwrap(),
26962            false,
26963        ),
26964        "这是什\n么 钢\n"
26965    );
26966    assert_eq!(
26967        wrap_with_prefix(
26968            String::new(),
26969            String::new(),
26970            format!("foo{}bar", '\u{2009}'), // thin space
26971            80,
26972            NonZeroU32::new(4).unwrap(),
26973            false,
26974        ),
26975        format!("foo{}bar", '\u{2009}')
26976    );
26977}
26978
26979pub trait CollaborationHub {
26980    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26981    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26982    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26983}
26984
26985impl CollaborationHub for Entity<Project> {
26986    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26987        self.read(cx).collaborators()
26988    }
26989
26990    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26991        self.read(cx).user_store().read(cx).participant_indices()
26992    }
26993
26994    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26995        let this = self.read(cx);
26996        let user_ids = this.collaborators().values().map(|c| c.user_id);
26997        this.user_store().read(cx).participant_names(user_ids, cx)
26998    }
26999}
27000
27001pub trait SemanticsProvider {
27002    fn hover(
27003        &self,
27004        buffer: &Entity<Buffer>,
27005        position: text::Anchor,
27006        cx: &mut App,
27007    ) -> Option<Task<Option<Vec<project::Hover>>>>;
27008
27009    fn inline_values(
27010        &self,
27011        buffer_handle: Entity<Buffer>,
27012        range: Range<text::Anchor>,
27013        cx: &mut App,
27014    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
27015
27016    fn applicable_inlay_chunks(
27017        &self,
27018        buffer: &Entity<Buffer>,
27019        ranges: &[Range<text::Anchor>],
27020        cx: &mut App,
27021    ) -> Vec<Range<BufferRow>>;
27022
27023    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
27024
27025    fn inlay_hints(
27026        &self,
27027        invalidate: InvalidationStrategy,
27028        buffer: Entity<Buffer>,
27029        ranges: Vec<Range<text::Anchor>>,
27030        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27031        cx: &mut App,
27032    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
27033
27034    fn semantic_tokens(
27035        &self,
27036        buffer: Entity<Buffer>,
27037        refresh: Option<RefreshForServer>,
27038        cx: &mut App,
27039    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
27040
27041    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27042
27043    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27044
27045    fn document_highlights(
27046        &self,
27047        buffer: &Entity<Buffer>,
27048        position: text::Anchor,
27049        cx: &mut App,
27050    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
27051
27052    fn definitions(
27053        &self,
27054        buffer: &Entity<Buffer>,
27055        position: text::Anchor,
27056        kind: GotoDefinitionKind,
27057        cx: &mut App,
27058    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
27059
27060    fn range_for_rename(
27061        &self,
27062        buffer: &Entity<Buffer>,
27063        position: text::Anchor,
27064        cx: &mut App,
27065    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
27066
27067    fn perform_rename(
27068        &self,
27069        buffer: &Entity<Buffer>,
27070        position: text::Anchor,
27071        new_name: String,
27072        cx: &mut App,
27073    ) -> Option<Task<Result<ProjectTransaction>>>;
27074}
27075
27076pub trait CompletionProvider {
27077    fn completions(
27078        &self,
27079        excerpt_id: ExcerptId,
27080        buffer: &Entity<Buffer>,
27081        buffer_position: text::Anchor,
27082        trigger: CompletionContext,
27083        window: &mut Window,
27084        cx: &mut Context<Editor>,
27085    ) -> Task<Result<Vec<CompletionResponse>>>;
27086
27087    fn resolve_completions(
27088        &self,
27089        _buffer: Entity<Buffer>,
27090        _completion_indices: Vec<usize>,
27091        _completions: Rc<RefCell<Box<[Completion]>>>,
27092        _cx: &mut Context<Editor>,
27093    ) -> Task<Result<bool>> {
27094        Task::ready(Ok(false))
27095    }
27096
27097    fn apply_additional_edits_for_completion(
27098        &self,
27099        _buffer: Entity<Buffer>,
27100        _completions: Rc<RefCell<Box<[Completion]>>>,
27101        _completion_index: usize,
27102        _push_to_history: bool,
27103        _all_commit_ranges: Vec<Range<language::Anchor>>,
27104        _cx: &mut Context<Editor>,
27105    ) -> Task<Result<Option<language::Transaction>>> {
27106        Task::ready(Ok(None))
27107    }
27108
27109    fn is_completion_trigger(
27110        &self,
27111        buffer: &Entity<Buffer>,
27112        position: language::Anchor,
27113        text: &str,
27114        trigger_in_words: bool,
27115        cx: &mut Context<Editor>,
27116    ) -> bool;
27117
27118    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
27119
27120    fn sort_completions(&self) -> bool {
27121        true
27122    }
27123
27124    fn filter_completions(&self) -> bool {
27125        true
27126    }
27127
27128    fn show_snippets(&self) -> bool {
27129        false
27130    }
27131}
27132
27133pub trait CodeActionProvider {
27134    fn id(&self) -> Arc<str>;
27135
27136    fn code_actions(
27137        &self,
27138        buffer: &Entity<Buffer>,
27139        range: Range<text::Anchor>,
27140        window: &mut Window,
27141        cx: &mut App,
27142    ) -> Task<Result<Vec<CodeAction>>>;
27143
27144    fn apply_code_action(
27145        &self,
27146        buffer_handle: Entity<Buffer>,
27147        action: CodeAction,
27148        excerpt_id: ExcerptId,
27149        push_to_history: bool,
27150        window: &mut Window,
27151        cx: &mut App,
27152    ) -> Task<Result<ProjectTransaction>>;
27153}
27154
27155impl CodeActionProvider for Entity<Project> {
27156    fn id(&self) -> Arc<str> {
27157        "project".into()
27158    }
27159
27160    fn code_actions(
27161        &self,
27162        buffer: &Entity<Buffer>,
27163        range: Range<text::Anchor>,
27164        _window: &mut Window,
27165        cx: &mut App,
27166    ) -> Task<Result<Vec<CodeAction>>> {
27167        self.update(cx, |project, cx| {
27168            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27169            let code_actions = project.code_actions(buffer, range, None, cx);
27170            cx.background_spawn(async move {
27171                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27172                Ok(code_lens_actions
27173                    .context("code lens fetch")?
27174                    .into_iter()
27175                    .flatten()
27176                    .chain(
27177                        code_actions
27178                            .context("code action fetch")?
27179                            .into_iter()
27180                            .flatten(),
27181                    )
27182                    .collect())
27183            })
27184        })
27185    }
27186
27187    fn apply_code_action(
27188        &self,
27189        buffer_handle: Entity<Buffer>,
27190        action: CodeAction,
27191        _excerpt_id: ExcerptId,
27192        push_to_history: bool,
27193        _window: &mut Window,
27194        cx: &mut App,
27195    ) -> Task<Result<ProjectTransaction>> {
27196        self.update(cx, |project, cx| {
27197            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27198        })
27199    }
27200}
27201
27202fn snippet_completions(
27203    project: &Project,
27204    buffer: &Entity<Buffer>,
27205    buffer_anchor: text::Anchor,
27206    classifier: CharClassifier,
27207    cx: &mut App,
27208) -> Task<Result<CompletionResponse>> {
27209    let languages = buffer.read(cx).languages_at(buffer_anchor);
27210    let snippet_store = project.snippets().read(cx);
27211
27212    let scopes: Vec<_> = languages
27213        .iter()
27214        .filter_map(|language| {
27215            let language_name = language.lsp_id();
27216            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27217
27218            if snippets.is_empty() {
27219                None
27220            } else {
27221                Some((language.default_scope(), snippets))
27222            }
27223        })
27224        .collect();
27225
27226    if scopes.is_empty() {
27227        return Task::ready(Ok(CompletionResponse {
27228            completions: vec![],
27229            display_options: CompletionDisplayOptions::default(),
27230            is_incomplete: false,
27231        }));
27232    }
27233
27234    let snapshot = buffer.read(cx).text_snapshot();
27235    let executor = cx.background_executor().clone();
27236
27237    cx.background_spawn(async move {
27238        let is_word_char = |c| classifier.is_word(c);
27239
27240        let mut is_incomplete = false;
27241        let mut completions: Vec<Completion> = Vec::new();
27242
27243        const MAX_PREFIX_LEN: usize = 128;
27244        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27245        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27246        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27247
27248        let max_buffer_window: String = snapshot
27249            .text_for_range(window_start..buffer_offset)
27250            .collect();
27251
27252        if max_buffer_window.is_empty() {
27253            return Ok(CompletionResponse {
27254                completions: vec![],
27255                display_options: CompletionDisplayOptions::default(),
27256                is_incomplete: true,
27257            });
27258        }
27259
27260        for (_scope, snippets) in scopes.into_iter() {
27261            // Sort snippets by word count to match longer snippet prefixes first.
27262            let mut sorted_snippet_candidates = snippets
27263                .iter()
27264                .enumerate()
27265                .flat_map(|(snippet_ix, snippet)| {
27266                    snippet
27267                        .prefix
27268                        .iter()
27269                        .enumerate()
27270                        .map(move |(prefix_ix, prefix)| {
27271                            let word_count =
27272                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27273                            ((snippet_ix, prefix_ix), prefix, word_count)
27274                        })
27275                })
27276                .collect_vec();
27277            sorted_snippet_candidates
27278                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27279
27280            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27281
27282            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27283                .take(
27284                    sorted_snippet_candidates
27285                        .first()
27286                        .map(|(_, _, word_count)| *word_count)
27287                        .unwrap_or_default(),
27288                )
27289                .collect_vec();
27290
27291            const MAX_RESULTS: usize = 100;
27292            // Each match also remembers how many characters from the buffer it consumed
27293            let mut matches: Vec<(StringMatch, usize)> = vec![];
27294
27295            let mut snippet_list_cutoff_index = 0;
27296            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27297                let word_count = buffer_index + 1;
27298                // Increase `snippet_list_cutoff_index` until we have all of the
27299                // snippets with sufficiently many words.
27300                while sorted_snippet_candidates
27301                    .get(snippet_list_cutoff_index)
27302                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27303                        *snippet_word_count >= word_count
27304                    })
27305                {
27306                    snippet_list_cutoff_index += 1;
27307                }
27308
27309                // Take only the candidates with at least `word_count` many words
27310                let snippet_candidates_at_word_len =
27311                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27312
27313                let candidates = snippet_candidates_at_word_len
27314                    .iter()
27315                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27316                    .enumerate() // index in `sorted_snippet_candidates`
27317                    // First char must match
27318                    .filter(|(_ix, prefix)| {
27319                        itertools::equal(
27320                            prefix
27321                                .chars()
27322                                .next()
27323                                .into_iter()
27324                                .flat_map(|c| c.to_lowercase()),
27325                            buffer_window
27326                                .chars()
27327                                .next()
27328                                .into_iter()
27329                                .flat_map(|c| c.to_lowercase()),
27330                        )
27331                    })
27332                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27333                    .collect::<Vec<StringMatchCandidate>>();
27334
27335                matches.extend(
27336                    fuzzy::match_strings(
27337                        &candidates,
27338                        &buffer_window,
27339                        buffer_window.chars().any(|c| c.is_uppercase()),
27340                        true,
27341                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27342                        &Default::default(),
27343                        executor.clone(),
27344                    )
27345                    .await
27346                    .into_iter()
27347                    .map(|string_match| (string_match, buffer_window.len())),
27348                );
27349
27350                if matches.len() >= MAX_RESULTS {
27351                    break;
27352                }
27353            }
27354
27355            let to_lsp = |point: &text::Anchor| {
27356                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27357                point_to_lsp(end)
27358            };
27359            let lsp_end = to_lsp(&buffer_anchor);
27360
27361            if matches.len() >= MAX_RESULTS {
27362                is_incomplete = true;
27363            }
27364
27365            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27366                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27367                    sorted_snippet_candidates[string_match.candidate_id];
27368                let snippet = &snippets[snippet_index];
27369                let start = buffer_offset - buffer_window_len;
27370                let start = snapshot.anchor_before(start);
27371                let range = start..buffer_anchor;
27372                let lsp_start = to_lsp(&start);
27373                let lsp_range = lsp::Range {
27374                    start: lsp_start,
27375                    end: lsp_end,
27376                };
27377                Completion {
27378                    replace_range: range,
27379                    new_text: snippet.body.clone(),
27380                    source: CompletionSource::Lsp {
27381                        insert_range: None,
27382                        server_id: LanguageServerId(usize::MAX),
27383                        resolved: true,
27384                        lsp_completion: Box::new(lsp::CompletionItem {
27385                            label: snippet.prefix.first().unwrap().clone(),
27386                            kind: Some(CompletionItemKind::SNIPPET),
27387                            label_details: snippet.description.as_ref().map(|description| {
27388                                lsp::CompletionItemLabelDetails {
27389                                    detail: Some(description.clone()),
27390                                    description: None,
27391                                }
27392                            }),
27393                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27394                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27395                                lsp::InsertReplaceEdit {
27396                                    new_text: snippet.body.clone(),
27397                                    insert: lsp_range,
27398                                    replace: lsp_range,
27399                                },
27400                            )),
27401                            filter_text: Some(snippet.body.clone()),
27402                            sort_text: Some(char::MAX.to_string()),
27403                            ..lsp::CompletionItem::default()
27404                        }),
27405                        lsp_defaults: None,
27406                    },
27407                    label: CodeLabel {
27408                        text: matching_prefix.clone(),
27409                        runs: Vec::new(),
27410                        filter_range: 0..matching_prefix.len(),
27411                    },
27412                    icon_path: None,
27413                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27414                        single_line: snippet.name.clone().into(),
27415                        plain_text: snippet
27416                            .description
27417                            .clone()
27418                            .map(|description| description.into()),
27419                    }),
27420                    insert_text_mode: None,
27421                    confirm: None,
27422                    match_start: Some(start),
27423                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27424                }
27425            }));
27426        }
27427
27428        Ok(CompletionResponse {
27429            completions,
27430            display_options: CompletionDisplayOptions::default(),
27431            is_incomplete,
27432        })
27433    })
27434}
27435
27436impl CompletionProvider for Entity<Project> {
27437    fn completions(
27438        &self,
27439        _excerpt_id: ExcerptId,
27440        buffer: &Entity<Buffer>,
27441        buffer_position: text::Anchor,
27442        options: CompletionContext,
27443        _window: &mut Window,
27444        cx: &mut Context<Editor>,
27445    ) -> Task<Result<Vec<CompletionResponse>>> {
27446        self.update(cx, |project, cx| {
27447            let task = project.completions(buffer, buffer_position, options, cx);
27448            cx.background_spawn(task)
27449        })
27450    }
27451
27452    fn resolve_completions(
27453        &self,
27454        buffer: Entity<Buffer>,
27455        completion_indices: Vec<usize>,
27456        completions: Rc<RefCell<Box<[Completion]>>>,
27457        cx: &mut Context<Editor>,
27458    ) -> Task<Result<bool>> {
27459        self.update(cx, |project, cx| {
27460            project.lsp_store().update(cx, |lsp_store, cx| {
27461                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27462            })
27463        })
27464    }
27465
27466    fn apply_additional_edits_for_completion(
27467        &self,
27468        buffer: Entity<Buffer>,
27469        completions: Rc<RefCell<Box<[Completion]>>>,
27470        completion_index: usize,
27471        push_to_history: bool,
27472        all_commit_ranges: Vec<Range<language::Anchor>>,
27473        cx: &mut Context<Editor>,
27474    ) -> Task<Result<Option<language::Transaction>>> {
27475        self.update(cx, |project, cx| {
27476            project.lsp_store().update(cx, |lsp_store, cx| {
27477                lsp_store.apply_additional_edits_for_completion(
27478                    buffer,
27479                    completions,
27480                    completion_index,
27481                    push_to_history,
27482                    all_commit_ranges,
27483                    cx,
27484                )
27485            })
27486        })
27487    }
27488
27489    fn is_completion_trigger(
27490        &self,
27491        buffer: &Entity<Buffer>,
27492        position: language::Anchor,
27493        text: &str,
27494        trigger_in_words: bool,
27495        cx: &mut Context<Editor>,
27496    ) -> bool {
27497        let mut chars = text.chars();
27498        let char = if let Some(char) = chars.next() {
27499            char
27500        } else {
27501            return false;
27502        };
27503        if chars.next().is_some() {
27504            return false;
27505        }
27506
27507        let buffer = buffer.read(cx);
27508        let snapshot = buffer.snapshot();
27509        let classifier = snapshot
27510            .char_classifier_at(position)
27511            .scope_context(Some(CharScopeContext::Completion));
27512        if trigger_in_words && classifier.is_word(char) {
27513            return true;
27514        }
27515
27516        buffer.completion_triggers().contains(text)
27517    }
27518
27519    fn show_snippets(&self) -> bool {
27520        true
27521    }
27522}
27523
27524impl SemanticsProvider for WeakEntity<Project> {
27525    fn hover(
27526        &self,
27527        buffer: &Entity<Buffer>,
27528        position: text::Anchor,
27529        cx: &mut App,
27530    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27531        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27532            .ok()
27533    }
27534
27535    fn document_highlights(
27536        &self,
27537        buffer: &Entity<Buffer>,
27538        position: text::Anchor,
27539        cx: &mut App,
27540    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27541        self.update(cx, |project, cx| {
27542            project.document_highlights(buffer, position, cx)
27543        })
27544        .ok()
27545    }
27546
27547    fn definitions(
27548        &self,
27549        buffer: &Entity<Buffer>,
27550        position: text::Anchor,
27551        kind: GotoDefinitionKind,
27552        cx: &mut App,
27553    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27554        self.update(cx, |project, cx| match kind {
27555            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27556            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27557            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27558            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27559        })
27560        .ok()
27561    }
27562
27563    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27564        self.update(cx, |project, cx| {
27565            if project
27566                .active_debug_session(cx)
27567                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27568            {
27569                return true;
27570            }
27571
27572            buffer.update(cx, |buffer, cx| {
27573                project.any_language_server_supports_inlay_hints(buffer, cx)
27574            })
27575        })
27576        .unwrap_or(false)
27577    }
27578
27579    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27580        self.update(cx, |project, cx| {
27581            buffer.update(cx, |buffer, cx| {
27582                project.any_language_server_supports_semantic_tokens(buffer, cx)
27583            })
27584        })
27585        .unwrap_or(false)
27586    }
27587
27588    fn inline_values(
27589        &self,
27590        buffer_handle: Entity<Buffer>,
27591        range: Range<text::Anchor>,
27592        cx: &mut App,
27593    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27594        self.update(cx, |project, cx| {
27595            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27596
27597            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27598        })
27599        .ok()
27600        .flatten()
27601    }
27602
27603    fn applicable_inlay_chunks(
27604        &self,
27605        buffer: &Entity<Buffer>,
27606        ranges: &[Range<text::Anchor>],
27607        cx: &mut App,
27608    ) -> Vec<Range<BufferRow>> {
27609        self.update(cx, |project, cx| {
27610            project.lsp_store().update(cx, |lsp_store, cx| {
27611                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27612            })
27613        })
27614        .unwrap_or_default()
27615    }
27616
27617    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27618        self.update(cx, |project, cx| {
27619            project.lsp_store().update(cx, |lsp_store, _| {
27620                lsp_store.invalidate_inlay_hints(for_buffers)
27621            })
27622        })
27623        .ok();
27624    }
27625
27626    fn inlay_hints(
27627        &self,
27628        invalidate: InvalidationStrategy,
27629        buffer: Entity<Buffer>,
27630        ranges: Vec<Range<text::Anchor>>,
27631        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27632        cx: &mut App,
27633    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27634        self.update(cx, |project, cx| {
27635            project.lsp_store().update(cx, |lsp_store, cx| {
27636                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27637            })
27638        })
27639        .ok()
27640    }
27641
27642    fn semantic_tokens(
27643        &self,
27644        buffer: Entity<Buffer>,
27645        refresh: Option<RefreshForServer>,
27646        cx: &mut App,
27647    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27648        self.update(cx, |this, cx| {
27649            this.lsp_store().update(cx, |lsp_store, cx| {
27650                lsp_store.semantic_tokens(buffer, refresh, cx)
27651            })
27652        })
27653        .ok()
27654    }
27655
27656    fn range_for_rename(
27657        &self,
27658        buffer: &Entity<Buffer>,
27659        position: text::Anchor,
27660        cx: &mut App,
27661    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27662        self.update(cx, |project, cx| {
27663            let buffer = buffer.clone();
27664            let task = project.prepare_rename(buffer.clone(), position, cx);
27665            cx.spawn(async move |_, cx| {
27666                Ok(match task.await? {
27667                    PrepareRenameResponse::Success(range) => Some(range),
27668                    PrepareRenameResponse::InvalidPosition => None,
27669                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27670                        // Fallback on using TreeSitter info to determine identifier range
27671                        buffer.read_with(cx, |buffer, _| {
27672                            let snapshot = buffer.snapshot();
27673                            let (range, kind) = snapshot.surrounding_word(position, None);
27674                            if kind != Some(CharKind::Word) {
27675                                return None;
27676                            }
27677                            Some(
27678                                snapshot.anchor_before(range.start)
27679                                    ..snapshot.anchor_after(range.end),
27680                            )
27681                        })
27682                    }
27683                })
27684            })
27685        })
27686        .ok()
27687    }
27688
27689    fn perform_rename(
27690        &self,
27691        buffer: &Entity<Buffer>,
27692        position: text::Anchor,
27693        new_name: String,
27694        cx: &mut App,
27695    ) -> Option<Task<Result<ProjectTransaction>>> {
27696        self.update(cx, |project, cx| {
27697            project.perform_rename(buffer.clone(), position, new_name, cx)
27698        })
27699        .ok()
27700    }
27701}
27702
27703fn consume_contiguous_rows(
27704    contiguous_row_selections: &mut Vec<Selection<Point>>,
27705    selection: &Selection<Point>,
27706    display_map: &DisplaySnapshot,
27707    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27708) -> (MultiBufferRow, MultiBufferRow) {
27709    contiguous_row_selections.push(selection.clone());
27710    let start_row = starting_row(selection, display_map);
27711    let mut end_row = ending_row(selection, display_map);
27712
27713    while let Some(next_selection) = selections.peek() {
27714        if next_selection.start.row <= end_row.0 {
27715            end_row = ending_row(next_selection, display_map);
27716            contiguous_row_selections.push(selections.next().unwrap().clone());
27717        } else {
27718            break;
27719        }
27720    }
27721    (start_row, end_row)
27722}
27723
27724fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27725    if selection.start.column > 0 {
27726        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27727    } else {
27728        MultiBufferRow(selection.start.row)
27729    }
27730}
27731
27732fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27733    if next_selection.end.column > 0 || next_selection.is_empty() {
27734        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27735    } else {
27736        MultiBufferRow(next_selection.end.row)
27737    }
27738}
27739
27740impl EditorSnapshot {
27741    pub fn remote_selections_in_range<'a>(
27742        &'a self,
27743        range: &'a Range<Anchor>,
27744        collaboration_hub: &dyn CollaborationHub,
27745        cx: &'a App,
27746    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27747        let participant_names = collaboration_hub.user_names(cx);
27748        let participant_indices = collaboration_hub.user_participant_indices(cx);
27749        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27750        let collaborators_by_replica_id = collaborators_by_peer_id
27751            .values()
27752            .map(|collaborator| (collaborator.replica_id, collaborator))
27753            .collect::<HashMap<_, _>>();
27754        self.buffer_snapshot()
27755            .selections_in_range(range, false)
27756            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27757                if replica_id == ReplicaId::AGENT {
27758                    Some(RemoteSelection {
27759                        replica_id,
27760                        selection,
27761                        cursor_shape,
27762                        line_mode,
27763                        collaborator_id: CollaboratorId::Agent,
27764                        user_name: Some("Agent".into()),
27765                        color: cx.theme().players().agent(),
27766                    })
27767                } else {
27768                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27769                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27770                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27771                    Some(RemoteSelection {
27772                        replica_id,
27773                        selection,
27774                        cursor_shape,
27775                        line_mode,
27776                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27777                        user_name,
27778                        color: if let Some(index) = participant_index {
27779                            cx.theme().players().color_for_participant(index.0)
27780                        } else {
27781                            cx.theme().players().absent()
27782                        },
27783                    })
27784                }
27785            })
27786    }
27787
27788    pub fn hunks_for_ranges(
27789        &self,
27790        ranges: impl IntoIterator<Item = Range<Point>>,
27791    ) -> Vec<MultiBufferDiffHunk> {
27792        let mut hunks = Vec::new();
27793        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27794            HashMap::default();
27795        for query_range in ranges {
27796            let query_rows =
27797                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27798            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27799                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27800            ) {
27801                // Include deleted hunks that are adjacent to the query range, because
27802                // otherwise they would be missed.
27803                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27804                if hunk.status().is_deleted() {
27805                    intersects_range |= hunk.row_range.start == query_rows.end;
27806                    intersects_range |= hunk.row_range.end == query_rows.start;
27807                }
27808                if intersects_range {
27809                    if !processed_buffer_rows
27810                        .entry(hunk.buffer_id)
27811                        .or_default()
27812                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27813                    {
27814                        continue;
27815                    }
27816                    hunks.push(hunk);
27817                }
27818            }
27819        }
27820
27821        hunks
27822    }
27823
27824    fn display_diff_hunks_for_rows<'a>(
27825        &'a self,
27826        display_rows: Range<DisplayRow>,
27827        folded_buffers: &'a HashSet<BufferId>,
27828    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27829        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27830        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27831
27832        self.buffer_snapshot()
27833            .diff_hunks_in_range(buffer_start..buffer_end)
27834            .filter_map(|hunk| {
27835                if folded_buffers.contains(&hunk.buffer_id)
27836                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27837                {
27838                    return None;
27839                }
27840
27841                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27842                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27843                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27844                    let line_len = self.buffer_snapshot().line_len(last_row);
27845                    Point::new(last_row.0, line_len)
27846                } else {
27847                    Point::new(hunk.row_range.end.0, 0)
27848                };
27849
27850                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27851                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27852
27853                let display_hunk = if hunk_display_start.column() != 0 {
27854                    DisplayDiffHunk::Folded {
27855                        display_row: hunk_display_start.row(),
27856                    }
27857                } else {
27858                    let mut end_row = hunk_display_end.row();
27859                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27860                        end_row.0 += 1;
27861                    }
27862                    let is_created_file = hunk.is_created_file();
27863
27864                    DisplayDiffHunk::Unfolded {
27865                        status: hunk.status(),
27866                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27867                            ..hunk.diff_base_byte_range.end.0,
27868                        word_diffs: hunk.word_diffs,
27869                        display_row_range: hunk_display_start.row()..end_row,
27870                        multi_buffer_range: Anchor::range_in_buffer(
27871                            hunk.excerpt_id,
27872                            hunk.buffer_range,
27873                        ),
27874                        is_created_file,
27875                    }
27876                };
27877
27878                Some(display_hunk)
27879            })
27880    }
27881
27882    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27883        self.display_snapshot
27884            .buffer_snapshot()
27885            .language_at(position)
27886    }
27887
27888    pub fn is_focused(&self) -> bool {
27889        self.is_focused
27890    }
27891
27892    pub fn placeholder_text(&self) -> Option<String> {
27893        self.placeholder_display_snapshot
27894            .as_ref()
27895            .map(|display_map| display_map.text())
27896    }
27897
27898    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27899        self.scroll_anchor.scroll_position(&self.display_snapshot)
27900    }
27901
27902    pub fn gutter_dimensions(
27903        &self,
27904        font_id: FontId,
27905        font_size: Pixels,
27906        style: &EditorStyle,
27907        window: &mut Window,
27908        cx: &App,
27909    ) -> GutterDimensions {
27910        if self.show_gutter
27911            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27912            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27913        {
27914            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27915                matches!(
27916                    ProjectSettings::get_global(cx).git.git_gutter,
27917                    GitGutterSetting::TrackedFiles
27918                )
27919            });
27920            let gutter_settings = EditorSettings::get_global(cx).gutter;
27921            let show_line_numbers = self
27922                .show_line_numbers
27923                .unwrap_or(gutter_settings.line_numbers);
27924            let line_gutter_width = if show_line_numbers {
27925                // Avoid flicker-like gutter resizes when the line number gains another digit by
27926                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27927                let min_width_for_number_on_gutter =
27928                    ch_advance * gutter_settings.min_line_number_digits as f32;
27929                self.max_line_number_width(style, window)
27930                    .max(min_width_for_number_on_gutter)
27931            } else {
27932                0.0.into()
27933            };
27934
27935            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27936            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27937
27938            let git_blame_entries_width =
27939                self.git_blame_gutter_max_author_length
27940                    .map(|max_author_length| {
27941                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27942                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27943
27944                        /// The number of characters to dedicate to gaps and margins.
27945                        const SPACING_WIDTH: usize = 4;
27946
27947                        let max_char_count = max_author_length.min(renderer.max_author_length())
27948                            + ::git::SHORT_SHA_LENGTH
27949                            + MAX_RELATIVE_TIMESTAMP.len()
27950                            + SPACING_WIDTH;
27951
27952                        ch_advance * max_char_count
27953                    });
27954
27955            let is_singleton = self.buffer_snapshot().is_singleton();
27956
27957            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27958            left_padding += if !is_singleton {
27959                ch_width * 4.0
27960            } else if show_runnables || show_breakpoints {
27961                ch_width * 3.0
27962            } else if show_git_gutter && show_line_numbers {
27963                ch_width * 2.0
27964            } else if show_git_gutter || show_line_numbers {
27965                ch_width
27966            } else {
27967                px(0.)
27968            };
27969
27970            let shows_folds = is_singleton && gutter_settings.folds;
27971
27972            let right_padding = if shows_folds && show_line_numbers {
27973                ch_width * 4.0
27974            } else if shows_folds || (!is_singleton && show_line_numbers) {
27975                ch_width * 3.0
27976            } else if show_line_numbers {
27977                ch_width
27978            } else {
27979                px(0.)
27980            };
27981
27982            GutterDimensions {
27983                left_padding,
27984                right_padding,
27985                width: line_gutter_width + left_padding + right_padding,
27986                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27987                git_blame_entries_width,
27988            }
27989        } else if self.offset_content {
27990            GutterDimensions::default_with_margin(font_id, font_size, cx)
27991        } else {
27992            GutterDimensions::default()
27993        }
27994    }
27995
27996    pub fn render_crease_toggle(
27997        &self,
27998        buffer_row: MultiBufferRow,
27999        row_contains_cursor: bool,
28000        editor: Entity<Editor>,
28001        window: &mut Window,
28002        cx: &mut App,
28003    ) -> Option<AnyElement> {
28004        let folded = self.is_line_folded(buffer_row);
28005        let mut is_foldable = false;
28006
28007        if let Some(crease) = self
28008            .crease_snapshot
28009            .query_row(buffer_row, self.buffer_snapshot())
28010        {
28011            is_foldable = true;
28012            match crease {
28013                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
28014                    if let Some(render_toggle) = render_toggle {
28015                        let toggle_callback =
28016                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
28017                                if folded {
28018                                    editor.update(cx, |editor, cx| {
28019                                        editor.fold_at(buffer_row, window, cx)
28020                                    });
28021                                } else {
28022                                    editor.update(cx, |editor, cx| {
28023                                        editor.unfold_at(buffer_row, window, cx)
28024                                    });
28025                                }
28026                            });
28027                        return Some((render_toggle)(
28028                            buffer_row,
28029                            folded,
28030                            toggle_callback,
28031                            window,
28032                            cx,
28033                        ));
28034                    }
28035                }
28036            }
28037        }
28038
28039        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
28040
28041        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
28042            Some(
28043                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
28044                    .toggle_state(folded)
28045                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
28046                        if folded {
28047                            this.unfold_at(buffer_row, window, cx);
28048                        } else {
28049                            this.fold_at(buffer_row, window, cx);
28050                        }
28051                    }))
28052                    .into_any_element(),
28053            )
28054        } else {
28055            None
28056        }
28057    }
28058
28059    pub fn render_crease_trailer(
28060        &self,
28061        buffer_row: MultiBufferRow,
28062        window: &mut Window,
28063        cx: &mut App,
28064    ) -> Option<AnyElement> {
28065        let folded = self.is_line_folded(buffer_row);
28066        if let Crease::Inline { render_trailer, .. } = self
28067            .crease_snapshot
28068            .query_row(buffer_row, self.buffer_snapshot())?
28069        {
28070            let render_trailer = render_trailer.as_ref()?;
28071            Some(render_trailer(buffer_row, folded, window, cx))
28072        } else {
28073            None
28074        }
28075    }
28076
28077    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
28078        let digit_count = self.widest_line_number().ilog10() + 1;
28079        column_pixels(style, digit_count as usize, window)
28080    }
28081
28082    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
28083    ///
28084    /// This is positive if `base` is before `line`.
28085    fn relative_line_delta(
28086        &self,
28087        current_selection_head: DisplayRow,
28088        first_visible_row: DisplayRow,
28089        consider_wrapped_lines: bool,
28090    ) -> i64 {
28091        let current_selection_head = current_selection_head.as_display_point().to_point(self);
28092        let first_visible_row = first_visible_row.as_display_point().to_point(self);
28093
28094        if consider_wrapped_lines {
28095            let wrap_snapshot = self.wrap_snapshot();
28096            let base_wrap_row = wrap_snapshot
28097                .make_wrap_point(current_selection_head, Bias::Left)
28098                .row();
28099            let wrap_row = wrap_snapshot
28100                .make_wrap_point(first_visible_row, Bias::Left)
28101                .row();
28102
28103            wrap_row.0 as i64 - base_wrap_row.0 as i64
28104        } else {
28105            let fold_snapshot = self.fold_snapshot();
28106            let base_fold_row = fold_snapshot
28107                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
28108                .row();
28109            let fold_row = fold_snapshot
28110                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
28111                .row();
28112
28113            fold_row as i64 - base_fold_row as i64
28114        }
28115    }
28116
28117    /// Returns the unsigned relative line number to display for each row in `rows`.
28118    ///
28119    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
28120    pub fn calculate_relative_line_numbers(
28121        &self,
28122        rows: &Range<DisplayRow>,
28123        current_selection_head: DisplayRow,
28124        count_wrapped_lines: bool,
28125    ) -> HashMap<DisplayRow, u32> {
28126        let initial_offset =
28127            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
28128
28129        self.row_infos(rows.start)
28130            .take(rows.len())
28131            .enumerate()
28132            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
28133            .filter(|(_row, row_info)| {
28134                row_info.buffer_row.is_some()
28135                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
28136            })
28137            .enumerate()
28138            .filter_map(|(i, (row, row_info))| {
28139                // We want to ensure here that the current line has absolute
28140                // numbering, even if we are in a soft-wrapped line. With the
28141                // exception that if we are in a deleted line, we should number this
28142                // relative with 0, as otherwise it would have no line number at all
28143                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
28144
28145                (relative_line_number != 0
28146                    || row_info
28147                        .diff_status
28148                        .is_some_and(|status| status.is_deleted()))
28149                .then_some((row, relative_line_number))
28150            })
28151            .collect()
28152    }
28153}
28154
28155pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28156    let font_size = style.text.font_size.to_pixels(window.rem_size());
28157    let layout = window.text_system().shape_line(
28158        SharedString::from(" ".repeat(column)),
28159        font_size,
28160        &[TextRun {
28161            len: column,
28162            font: style.text.font(),
28163            color: Hsla::default(),
28164            ..Default::default()
28165        }],
28166        None,
28167    );
28168
28169    layout.width
28170}
28171
28172impl Deref for EditorSnapshot {
28173    type Target = DisplaySnapshot;
28174
28175    fn deref(&self) -> &Self::Target {
28176        &self.display_snapshot
28177    }
28178}
28179
28180#[derive(Clone, Debug, PartialEq, Eq)]
28181pub enum EditorEvent {
28182    /// Emitted when the stored review comments change (added, removed, or updated).
28183    ReviewCommentsChanged {
28184        /// The new total count of review comments.
28185        total_count: usize,
28186    },
28187    InputIgnored {
28188        text: Arc<str>,
28189    },
28190    InputHandled {
28191        utf16_range_to_replace: Option<Range<isize>>,
28192        text: Arc<str>,
28193    },
28194    ExcerptsAdded {
28195        buffer: Entity<Buffer>,
28196        predecessor: ExcerptId,
28197        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28198    },
28199    ExcerptsRemoved {
28200        ids: Vec<ExcerptId>,
28201        removed_buffer_ids: Vec<BufferId>,
28202    },
28203    BufferFoldToggled {
28204        ids: Vec<ExcerptId>,
28205        folded: bool,
28206    },
28207    ExcerptsEdited {
28208        ids: Vec<ExcerptId>,
28209    },
28210    ExcerptsExpanded {
28211        ids: Vec<ExcerptId>,
28212    },
28213    ExpandExcerptsRequested {
28214        excerpt_ids: Vec<ExcerptId>,
28215        lines: u32,
28216        direction: ExpandExcerptDirection,
28217    },
28218    StageOrUnstageRequested {
28219        stage: bool,
28220        hunks: Vec<MultiBufferDiffHunk>,
28221    },
28222    OpenExcerptsRequested {
28223        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28224        split: bool,
28225    },
28226    RestoreRequested {
28227        hunks: Vec<MultiBufferDiffHunk>,
28228    },
28229    BufferEdited,
28230    Edited {
28231        transaction_id: clock::Lamport,
28232    },
28233    Reparsed(BufferId),
28234    Focused,
28235    FocusedIn,
28236    Blurred,
28237    DirtyChanged,
28238    Saved,
28239    TitleChanged,
28240    SelectionsChanged {
28241        local: bool,
28242    },
28243    ScrollPositionChanged {
28244        local: bool,
28245        autoscroll: bool,
28246    },
28247    TransactionUndone {
28248        transaction_id: clock::Lamport,
28249    },
28250    TransactionBegun {
28251        transaction_id: clock::Lamport,
28252    },
28253    CursorShapeChanged,
28254    BreadcrumbsChanged,
28255    OutlineSymbolsChanged,
28256    PushedToNavHistory {
28257        anchor: Anchor,
28258        is_deactivate: bool,
28259    },
28260}
28261
28262impl EventEmitter<EditorEvent> for Editor {}
28263
28264impl Focusable for Editor {
28265    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28266        self.focus_handle.clone()
28267    }
28268}
28269
28270impl Render for Editor {
28271    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28272        EditorElement::new(&cx.entity(), self.create_style(cx))
28273    }
28274}
28275
28276impl EntityInputHandler for Editor {
28277    fn text_for_range(
28278        &mut self,
28279        range_utf16: Range<usize>,
28280        adjusted_range: &mut Option<Range<usize>>,
28281        _: &mut Window,
28282        cx: &mut Context<Self>,
28283    ) -> Option<String> {
28284        let snapshot = self.buffer.read(cx).read(cx);
28285        let start = snapshot.clip_offset_utf16(
28286            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28287            Bias::Left,
28288        );
28289        let end = snapshot.clip_offset_utf16(
28290            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28291            Bias::Right,
28292        );
28293        if (start.0.0..end.0.0) != range_utf16 {
28294            adjusted_range.replace(start.0.0..end.0.0);
28295        }
28296        Some(snapshot.text_for_range(start..end).collect())
28297    }
28298
28299    fn selected_text_range(
28300        &mut self,
28301        ignore_disabled_input: bool,
28302        _: &mut Window,
28303        cx: &mut Context<Self>,
28304    ) -> Option<UTF16Selection> {
28305        // Prevent the IME menu from appearing when holding down an alphabetic key
28306        // while input is disabled.
28307        if !ignore_disabled_input && !self.input_enabled {
28308            return None;
28309        }
28310
28311        let selection = self
28312            .selections
28313            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28314        let range = selection.range();
28315
28316        Some(UTF16Selection {
28317            range: range.start.0.0..range.end.0.0,
28318            reversed: selection.reversed,
28319        })
28320    }
28321
28322    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28323        let snapshot = self.buffer.read(cx).read(cx);
28324        let range = self
28325            .text_highlights(HighlightKey::InputComposition, cx)?
28326            .1
28327            .first()?;
28328        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28329    }
28330
28331    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28332        self.clear_highlights(HighlightKey::InputComposition, cx);
28333        self.ime_transaction.take();
28334    }
28335
28336    fn replace_text_in_range(
28337        &mut self,
28338        range_utf16: Option<Range<usize>>,
28339        text: &str,
28340        window: &mut Window,
28341        cx: &mut Context<Self>,
28342    ) {
28343        if !self.input_enabled {
28344            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28345            return;
28346        }
28347
28348        self.transact(window, cx, |this, window, cx| {
28349            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28350                if let Some(marked_ranges) = this.marked_text_ranges(cx) {
28351                    // During IME composition, macOS reports the replacement range
28352                    // relative to the first marked region (the only one visible via
28353                    // marked_text_range). The correct targets for replacement are the
28354                    // marked ranges themselves — one per cursor — so use them directly.
28355                    Some(marked_ranges)
28356                } else if range_utf16.start == range_utf16.end {
28357                    // An empty replacement range means "insert at cursor" with no text
28358                    // to replace. macOS reports the cursor position from its own
28359                    // (single-cursor) view of the buffer, which diverges from our actual
28360                    // cursor positions after multi-cursor edits have shifted offsets.
28361                    // Treating this as range_utf16=None lets each cursor insert in place.
28362                    None
28363                } else {
28364                    // Outside of IME composition (e.g. Accessibility Keyboard word
28365                    // completion), the range is an absolute document offset for the
28366                    // newest cursor. Fan it out to all cursors via
28367                    // selection_replacement_ranges, which applies the delta relative
28368                    // to the newest selection to every cursor.
28369                    let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28370                        ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28371                    Some(this.selection_replacement_ranges(range_utf16, cx))
28372                }
28373            } else {
28374                this.marked_text_ranges(cx)
28375            };
28376
28377            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28378                let newest_selection_id = this.selections.newest_anchor().id;
28379                this.selections
28380                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28381                    .iter()
28382                    .zip(ranges_to_replace.iter())
28383                    .find_map(|(selection, range)| {
28384                        if selection.id == newest_selection_id {
28385                            Some(
28386                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28387                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28388                            )
28389                        } else {
28390                            None
28391                        }
28392                    })
28393            });
28394
28395            cx.emit(EditorEvent::InputHandled {
28396                utf16_range_to_replace: range_to_replace,
28397                text: text.into(),
28398            });
28399
28400            if let Some(new_selected_ranges) = new_selected_ranges {
28401                // Only backspace if at least one range covers actual text. When all
28402                // ranges are empty (e.g. a trailing-space insertion from Accessibility
28403                // Keyboard sends replacementRange=cursor..cursor), backspace would
28404                // incorrectly delete the character just before the cursor.
28405                let should_backspace = new_selected_ranges.iter().any(|r| r.start != r.end);
28406                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28407                    selections.select_ranges(new_selected_ranges)
28408                });
28409                if should_backspace {
28410                    this.backspace(&Default::default(), window, cx);
28411                }
28412            }
28413
28414            this.handle_input(text, window, cx);
28415        });
28416
28417        if let Some(transaction) = self.ime_transaction {
28418            self.buffer.update(cx, |buffer, cx| {
28419                buffer.group_until_transaction(transaction, cx);
28420            });
28421        }
28422
28423        self.unmark_text(window, cx);
28424    }
28425
28426    fn replace_and_mark_text_in_range(
28427        &mut self,
28428        range_utf16: Option<Range<usize>>,
28429        text: &str,
28430        new_selected_range_utf16: Option<Range<usize>>,
28431        window: &mut Window,
28432        cx: &mut Context<Self>,
28433    ) {
28434        if !self.input_enabled {
28435            return;
28436        }
28437
28438        let transaction = self.transact(window, cx, |this, window, cx| {
28439            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28440                let snapshot = this.buffer.read(cx).read(cx);
28441                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28442                    for marked_range in &mut marked_ranges {
28443                        marked_range.end = marked_range.start + relative_range_utf16.end;
28444                        marked_range.start += relative_range_utf16.start;
28445                        marked_range.start =
28446                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28447                        marked_range.end =
28448                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28449                    }
28450                }
28451                Some(marked_ranges)
28452            } else if let Some(range_utf16) = range_utf16 {
28453                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28454                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28455                Some(this.selection_replacement_ranges(range_utf16, cx))
28456            } else {
28457                None
28458            };
28459
28460            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28461                let newest_selection_id = this.selections.newest_anchor().id;
28462                this.selections
28463                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28464                    .iter()
28465                    .zip(ranges_to_replace.iter())
28466                    .find_map(|(selection, range)| {
28467                        if selection.id == newest_selection_id {
28468                            Some(
28469                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28470                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28471                            )
28472                        } else {
28473                            None
28474                        }
28475                    })
28476            });
28477
28478            cx.emit(EditorEvent::InputHandled {
28479                utf16_range_to_replace: range_to_replace,
28480                text: text.into(),
28481            });
28482
28483            if let Some(ranges) = ranges_to_replace {
28484                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28485                    s.select_ranges(ranges)
28486                });
28487            }
28488
28489            let marked_ranges = {
28490                let snapshot = this.buffer.read(cx).read(cx);
28491                this.selections
28492                    .disjoint_anchors_arc()
28493                    .iter()
28494                    .map(|selection| {
28495                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28496                    })
28497                    .collect::<Vec<_>>()
28498            };
28499
28500            if text.is_empty() {
28501                this.unmark_text(window, cx);
28502            } else {
28503                this.highlight_text(
28504                    HighlightKey::InputComposition,
28505                    marked_ranges.clone(),
28506                    HighlightStyle {
28507                        underline: Some(UnderlineStyle {
28508                            thickness: px(1.),
28509                            color: None,
28510                            wavy: false,
28511                        }),
28512                        ..Default::default()
28513                    },
28514                    cx,
28515                );
28516            }
28517
28518            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28519            let use_autoclose = this.use_autoclose;
28520            let use_auto_surround = this.use_auto_surround;
28521            this.set_use_autoclose(false);
28522            this.set_use_auto_surround(false);
28523            this.handle_input(text, window, cx);
28524            this.set_use_autoclose(use_autoclose);
28525            this.set_use_auto_surround(use_auto_surround);
28526
28527            if let Some(new_selected_range) = new_selected_range_utf16 {
28528                let snapshot = this.buffer.read(cx).read(cx);
28529                let new_selected_ranges = marked_ranges
28530                    .into_iter()
28531                    .map(|marked_range| {
28532                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28533                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28534                            insertion_start.0 + new_selected_range.start,
28535                        ));
28536                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28537                            insertion_start.0 + new_selected_range.end,
28538                        ));
28539                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28540                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28541                    })
28542                    .collect::<Vec<_>>();
28543
28544                drop(snapshot);
28545                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28546                    selections.select_ranges(new_selected_ranges)
28547                });
28548            }
28549        });
28550
28551        self.ime_transaction = self.ime_transaction.or(transaction);
28552        if let Some(transaction) = self.ime_transaction {
28553            self.buffer.update(cx, |buffer, cx| {
28554                buffer.group_until_transaction(transaction, cx);
28555            });
28556        }
28557
28558        if self
28559            .text_highlights(HighlightKey::InputComposition, cx)
28560            .is_none()
28561        {
28562            self.ime_transaction.take();
28563        }
28564    }
28565
28566    fn bounds_for_range(
28567        &mut self,
28568        range_utf16: Range<usize>,
28569        element_bounds: gpui::Bounds<Pixels>,
28570        window: &mut Window,
28571        cx: &mut Context<Self>,
28572    ) -> Option<gpui::Bounds<Pixels>> {
28573        let text_layout_details = self.text_layout_details(window, cx);
28574        let CharacterDimensions {
28575            em_width,
28576            em_advance,
28577            line_height,
28578        } = self.character_dimensions(window, cx);
28579
28580        let snapshot = self.snapshot(window, cx);
28581        let scroll_position = snapshot.scroll_position();
28582        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28583
28584        let start =
28585            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28586        let x = Pixels::from(
28587            ScrollOffset::from(
28588                snapshot.x_for_display_point(start, &text_layout_details)
28589                    + self.gutter_dimensions.full_width(),
28590            ) - scroll_left,
28591        );
28592        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28593
28594        Some(Bounds {
28595            origin: element_bounds.origin + point(x, y),
28596            size: size(em_width, line_height),
28597        })
28598    }
28599
28600    fn character_index_for_point(
28601        &mut self,
28602        point: gpui::Point<Pixels>,
28603        _window: &mut Window,
28604        _cx: &mut Context<Self>,
28605    ) -> Option<usize> {
28606        let position_map = self.last_position_map.as_ref()?;
28607        if !position_map.text_hitbox.contains(&point) {
28608            return None;
28609        }
28610        let display_point = position_map.point_for_position(point).previous_valid;
28611        let anchor = position_map
28612            .snapshot
28613            .display_point_to_anchor(display_point, Bias::Left);
28614        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28615        Some(utf16_offset.0.0)
28616    }
28617
28618    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28619        self.expects_character_input
28620    }
28621}
28622
28623trait SelectionExt {
28624    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28625    fn spanned_rows(
28626        &self,
28627        include_end_if_at_line_start: bool,
28628        map: &DisplaySnapshot,
28629    ) -> Range<MultiBufferRow>;
28630}
28631
28632impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28633    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28634        let start = self
28635            .start
28636            .to_point(map.buffer_snapshot())
28637            .to_display_point(map);
28638        let end = self
28639            .end
28640            .to_point(map.buffer_snapshot())
28641            .to_display_point(map);
28642        if self.reversed {
28643            end..start
28644        } else {
28645            start..end
28646        }
28647    }
28648
28649    fn spanned_rows(
28650        &self,
28651        include_end_if_at_line_start: bool,
28652        map: &DisplaySnapshot,
28653    ) -> Range<MultiBufferRow> {
28654        let start = self.start.to_point(map.buffer_snapshot());
28655        let mut end = self.end.to_point(map.buffer_snapshot());
28656        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28657            end.row -= 1;
28658        }
28659
28660        let buffer_start = map.prev_line_boundary(start).0;
28661        let buffer_end = map.next_line_boundary(end).0;
28662        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28663    }
28664}
28665
28666impl<T: InvalidationRegion> InvalidationStack<T> {
28667    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28668    where
28669        S: Clone + ToOffset,
28670    {
28671        while let Some(region) = self.last() {
28672            let all_selections_inside_invalidation_ranges =
28673                if selections.len() == region.ranges().len() {
28674                    selections
28675                        .iter()
28676                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28677                        .all(|(selection, invalidation_range)| {
28678                            let head = selection.head().to_offset(buffer);
28679                            invalidation_range.start <= head && invalidation_range.end >= head
28680                        })
28681                } else {
28682                    false
28683                };
28684
28685            if all_selections_inside_invalidation_ranges {
28686                break;
28687            } else {
28688                self.pop();
28689            }
28690        }
28691    }
28692}
28693
28694#[derive(Clone)]
28695struct ErasedEditorImpl(Entity<Editor>);
28696
28697impl ui_input::ErasedEditor for ErasedEditorImpl {
28698    fn text(&self, cx: &App) -> String {
28699        self.0.read(cx).text(cx)
28700    }
28701
28702    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28703        self.0.update(cx, |this, cx| {
28704            this.set_text(text, window, cx);
28705        })
28706    }
28707
28708    fn clear(&self, window: &mut Window, cx: &mut App) {
28709        self.0.update(cx, |this, cx| this.clear(window, cx));
28710    }
28711
28712    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28713        self.0.update(cx, |this, cx| {
28714            this.set_placeholder_text(text, window, cx);
28715        });
28716    }
28717
28718    fn focus_handle(&self, cx: &App) -> FocusHandle {
28719        self.0.read(cx).focus_handle(cx)
28720    }
28721
28722    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28723        let settings = ThemeSettings::get_global(cx);
28724        let theme_color = cx.theme().colors();
28725
28726        let text_style = TextStyle {
28727            font_family: settings.ui_font.family.clone(),
28728            font_features: settings.ui_font.features.clone(),
28729            font_size: rems(0.875).into(),
28730            font_weight: settings.ui_font.weight,
28731            font_style: FontStyle::Normal,
28732            line_height: relative(1.2),
28733            color: theme_color.text,
28734            ..Default::default()
28735        };
28736        let editor_style = EditorStyle {
28737            background: theme_color.ghost_element_background,
28738            local_player: cx.theme().players().local(),
28739            syntax: cx.theme().syntax().clone(),
28740            text: text_style,
28741            ..Default::default()
28742        };
28743        EditorElement::new(&self.0, editor_style).into_any()
28744    }
28745
28746    fn as_any(&self) -> &dyn Any {
28747        &self.0
28748    }
28749
28750    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28751        self.0.update(cx, |editor, cx| {
28752            let editor_offset = editor.buffer().read(cx).len(cx);
28753            editor.change_selections(
28754                SelectionEffects::scroll(Autoscroll::Next),
28755                window,
28756                cx,
28757                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28758            );
28759        });
28760    }
28761
28762    fn subscribe(
28763        &self,
28764        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28765        window: &mut Window,
28766        cx: &mut App,
28767    ) -> Subscription {
28768        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28769            let event = match event {
28770                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28771                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28772                _ => return,
28773            };
28774            (callback)(event, window, cx);
28775        })
28776    }
28777
28778    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28779        self.0.update(cx, |editor, cx| {
28780            editor.set_masked(masked, cx);
28781        });
28782    }
28783}
28784impl<T> Default for InvalidationStack<T> {
28785    fn default() -> Self {
28786        Self(Default::default())
28787    }
28788}
28789
28790impl<T> Deref for InvalidationStack<T> {
28791    type Target = Vec<T>;
28792
28793    fn deref(&self) -> &Self::Target {
28794        &self.0
28795    }
28796}
28797
28798impl<T> DerefMut for InvalidationStack<T> {
28799    fn deref_mut(&mut self) -> &mut Self::Target {
28800        &mut self.0
28801    }
28802}
28803
28804impl InvalidationRegion for SnippetState {
28805    fn ranges(&self) -> &[Range<Anchor>] {
28806        &self.ranges[self.active_index]
28807    }
28808}
28809
28810fn edit_prediction_edit_text(
28811    current_snapshot: &BufferSnapshot,
28812    edits: &[(Range<Anchor>, impl AsRef<str>)],
28813    edit_preview: &EditPreview,
28814    include_deletions: bool,
28815    cx: &App,
28816) -> HighlightedText {
28817    let edits = edits
28818        .iter()
28819        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28820        .collect::<Vec<_>>();
28821
28822    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28823}
28824
28825fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28826    // Fallback for providers that don't provide edit_preview (like Copilot)
28827    // Just show the raw edit text with basic styling
28828    let mut text = String::new();
28829    let mut highlights = Vec::new();
28830
28831    let insertion_highlight_style = HighlightStyle {
28832        color: Some(cx.theme().colors().text),
28833        ..Default::default()
28834    };
28835
28836    for (_, edit_text) in edits {
28837        let start_offset = text.len();
28838        text.push_str(edit_text);
28839        let end_offset = text.len();
28840
28841        if start_offset < end_offset {
28842            highlights.push((start_offset..end_offset, insertion_highlight_style));
28843        }
28844    }
28845
28846    HighlightedText {
28847        text: text.into(),
28848        highlights,
28849    }
28850}
28851
28852pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28853    match severity {
28854        lsp::DiagnosticSeverity::ERROR => colors.error,
28855        lsp::DiagnosticSeverity::WARNING => colors.warning,
28856        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28857        lsp::DiagnosticSeverity::HINT => colors.info,
28858        _ => colors.ignored,
28859    }
28860}
28861
28862pub fn styled_runs_for_code_label<'a>(
28863    label: &'a CodeLabel,
28864    syntax_theme: &'a theme::SyntaxTheme,
28865    local_player: &'a theme::PlayerColor,
28866) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28867    let fade_out = HighlightStyle {
28868        fade_out: Some(0.35),
28869        ..Default::default()
28870    };
28871
28872    if label.runs.is_empty() {
28873        let desc_start = label.filter_range.end;
28874        let fade_run =
28875            (desc_start < label.text.len()).then(|| (desc_start..label.text.len(), fade_out));
28876        return Either::Left(fade_run.into_iter());
28877    }
28878
28879    let mut prev_end = label.filter_range.end;
28880    Either::Right(
28881        label
28882            .runs
28883            .iter()
28884            .enumerate()
28885            .flat_map(move |(ix, (range, highlight_id))| {
28886                let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28887                    HighlightStyle {
28888                        color: Some(local_player.cursor),
28889                        ..Default::default()
28890                    }
28891                } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28892                    HighlightStyle {
28893                        background_color: Some(local_player.selection),
28894                        ..Default::default()
28895                    }
28896                } else if let Some(style) = syntax_theme.get(*highlight_id).cloned() {
28897                    style
28898                } else {
28899                    return Default::default();
28900                };
28901
28902                let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28903                let muted_style = style.highlight(fade_out);
28904                if range.start >= label.filter_range.end {
28905                    if range.start > prev_end {
28906                        runs.push((prev_end..range.start, fade_out));
28907                    }
28908                    runs.push((range.clone(), muted_style));
28909                } else if range.end <= label.filter_range.end {
28910                    runs.push((range.clone(), style));
28911                } else {
28912                    runs.push((range.start..label.filter_range.end, style));
28913                    runs.push((label.filter_range.end..range.end, muted_style));
28914                }
28915                prev_end = cmp::max(prev_end, range.end);
28916
28917                if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28918                    runs.push((prev_end..label.text.len(), fade_out));
28919                }
28920
28921                runs
28922            }),
28923    )
28924}
28925
28926pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28927    let mut prev_index = 0;
28928    let mut prev_codepoint: Option<char> = None;
28929    text.char_indices()
28930        .chain([(text.len(), '\0')])
28931        .filter_map(move |(index, codepoint)| {
28932            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28933            let is_boundary = index == text.len()
28934                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28935                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28936            if is_boundary {
28937                let chunk = &text[prev_index..index];
28938                prev_index = index;
28939                Some(chunk)
28940            } else {
28941                None
28942            }
28943        })
28944}
28945
28946/// Given a string of text immediately before the cursor, iterates over possible
28947/// strings a snippet could match to. More precisely: returns an iterator over
28948/// suffixes of `text` created by splitting at word boundaries (before & after
28949/// every non-word character).
28950///
28951/// Shorter suffixes are returned first.
28952pub(crate) fn snippet_candidate_suffixes<'a>(
28953    text: &'a str,
28954    is_word_char: &'a dyn Fn(char) -> bool,
28955) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28956    let mut prev_index = text.len();
28957    let mut prev_codepoint = None;
28958    text.char_indices()
28959        .rev()
28960        .chain([(0, '\0')])
28961        .filter_map(move |(index, codepoint)| {
28962            let prev_index = std::mem::replace(&mut prev_index, index);
28963            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28964            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28965                None
28966            } else {
28967                let chunk = &text[prev_index..]; // go to end of string
28968                Some(chunk)
28969            }
28970        })
28971}
28972
28973pub trait RangeToAnchorExt: Sized {
28974    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28975
28976    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28977        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28978        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28979    }
28980}
28981
28982impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28983    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28984        let start_offset = self.start.to_offset(snapshot);
28985        let end_offset = self.end.to_offset(snapshot);
28986        if start_offset == end_offset {
28987            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28988        } else {
28989            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28990        }
28991    }
28992}
28993
28994pub trait RowExt {
28995    fn as_f64(&self) -> f64;
28996
28997    fn next_row(&self) -> Self;
28998
28999    fn previous_row(&self) -> Self;
29000
29001    fn minus(&self, other: Self) -> u32;
29002}
29003
29004impl RowExt for DisplayRow {
29005    fn as_f64(&self) -> f64 {
29006        self.0 as _
29007    }
29008
29009    fn next_row(&self) -> Self {
29010        Self(self.0 + 1)
29011    }
29012
29013    fn previous_row(&self) -> Self {
29014        Self(self.0.saturating_sub(1))
29015    }
29016
29017    fn minus(&self, other: Self) -> u32 {
29018        self.0 - other.0
29019    }
29020}
29021
29022impl RowExt for MultiBufferRow {
29023    fn as_f64(&self) -> f64 {
29024        self.0 as _
29025    }
29026
29027    fn next_row(&self) -> Self {
29028        Self(self.0 + 1)
29029    }
29030
29031    fn previous_row(&self) -> Self {
29032        Self(self.0.saturating_sub(1))
29033    }
29034
29035    fn minus(&self, other: Self) -> u32 {
29036        self.0 - other.0
29037    }
29038}
29039
29040trait RowRangeExt {
29041    type Row;
29042
29043    fn len(&self) -> usize;
29044
29045    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
29046}
29047
29048impl RowRangeExt for Range<MultiBufferRow> {
29049    type Row = MultiBufferRow;
29050
29051    fn len(&self) -> usize {
29052        (self.end.0 - self.start.0) as usize
29053    }
29054
29055    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
29056        (self.start.0..self.end.0).map(MultiBufferRow)
29057    }
29058}
29059
29060impl RowRangeExt for Range<DisplayRow> {
29061    type Row = DisplayRow;
29062
29063    fn len(&self) -> usize {
29064        (self.end.0 - self.start.0) as usize
29065    }
29066
29067    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
29068        (self.start.0..self.end.0).map(DisplayRow)
29069    }
29070}
29071
29072/// If select range has more than one line, we
29073/// just point the cursor to range.start.
29074fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
29075    if range.start.row == range.end.row {
29076        range
29077    } else {
29078        range.start..range.start
29079    }
29080}
29081pub struct KillRing(ClipboardItem);
29082impl Global for KillRing {}
29083
29084const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
29085
29086enum BreakpointPromptEditAction {
29087    Log,
29088    Condition,
29089    HitCondition,
29090}
29091
29092struct BreakpointPromptEditor {
29093    pub(crate) prompt: Entity<Editor>,
29094    editor: WeakEntity<Editor>,
29095    breakpoint_anchor: Anchor,
29096    breakpoint: Breakpoint,
29097    edit_action: BreakpointPromptEditAction,
29098    block_ids: HashSet<CustomBlockId>,
29099    editor_margins: Arc<Mutex<EditorMargins>>,
29100    _subscriptions: Vec<Subscription>,
29101}
29102
29103impl BreakpointPromptEditor {
29104    const MAX_LINES: u8 = 4;
29105
29106    fn new(
29107        editor: WeakEntity<Editor>,
29108        breakpoint_anchor: Anchor,
29109        breakpoint: Breakpoint,
29110        edit_action: BreakpointPromptEditAction,
29111        window: &mut Window,
29112        cx: &mut Context<Self>,
29113    ) -> Self {
29114        let base_text = match edit_action {
29115            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
29116            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
29117            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
29118        }
29119        .map(|msg| msg.to_string())
29120        .unwrap_or_default();
29121
29122        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
29123        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
29124
29125        let prompt = cx.new(|cx| {
29126            let mut prompt = Editor::new(
29127                EditorMode::AutoHeight {
29128                    min_lines: 1,
29129                    max_lines: Some(Self::MAX_LINES as usize),
29130                },
29131                buffer,
29132                None,
29133                window,
29134                cx,
29135            );
29136            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
29137            prompt.set_show_cursor_when_unfocused(false, cx);
29138            prompt.set_placeholder_text(
29139                match edit_action {
29140                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
29141                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
29142                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
29143                },
29144                window,
29145                cx,
29146            );
29147
29148            prompt
29149        });
29150
29151        Self {
29152            prompt,
29153            editor,
29154            breakpoint_anchor,
29155            breakpoint,
29156            edit_action,
29157            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
29158            block_ids: Default::default(),
29159            _subscriptions: vec![],
29160        }
29161    }
29162
29163    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
29164        self.block_ids.extend(block_ids)
29165    }
29166
29167    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
29168        if let Some(editor) = self.editor.upgrade() {
29169            let message = self
29170                .prompt
29171                .read(cx)
29172                .buffer
29173                .read(cx)
29174                .as_singleton()
29175                .expect("A multi buffer in breakpoint prompt isn't possible")
29176                .read(cx)
29177                .as_rope()
29178                .to_string();
29179
29180            editor.update(cx, |editor, cx| {
29181                editor.edit_breakpoint_at_anchor(
29182                    self.breakpoint_anchor,
29183                    self.breakpoint.clone(),
29184                    match self.edit_action {
29185                        BreakpointPromptEditAction::Log => {
29186                            BreakpointEditAction::EditLogMessage(message.into())
29187                        }
29188                        BreakpointPromptEditAction::Condition => {
29189                            BreakpointEditAction::EditCondition(message.into())
29190                        }
29191                        BreakpointPromptEditAction::HitCondition => {
29192                            BreakpointEditAction::EditHitCondition(message.into())
29193                        }
29194                    },
29195                    cx,
29196                );
29197
29198                editor.remove_blocks(self.block_ids.clone(), None, cx);
29199                cx.focus_self(window);
29200            });
29201        }
29202    }
29203
29204    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29205        self.editor
29206            .update(cx, |editor, cx| {
29207                editor.remove_blocks(self.block_ids.clone(), None, cx);
29208                window.focus(&editor.focus_handle, cx);
29209            })
29210            .log_err();
29211    }
29212
29213    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29214        let settings = ThemeSettings::get_global(cx);
29215        let text_style = TextStyle {
29216            color: if self.prompt.read(cx).read_only(cx) {
29217                cx.theme().colors().text_disabled
29218            } else {
29219                cx.theme().colors().text
29220            },
29221            font_family: settings.buffer_font.family.clone(),
29222            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29223            font_size: settings.buffer_font_size(cx).into(),
29224            font_weight: settings.buffer_font.weight,
29225            line_height: relative(settings.buffer_line_height.value()),
29226            ..Default::default()
29227        };
29228        EditorElement::new(
29229            &self.prompt,
29230            EditorStyle {
29231                background: cx.theme().colors().editor_background,
29232                local_player: cx.theme().players().local(),
29233                text: text_style,
29234                ..Default::default()
29235            },
29236        )
29237    }
29238
29239    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29240        let focus_handle = self.prompt.focus_handle(cx);
29241        IconButton::new("cancel", IconName::Close)
29242            .icon_color(Color::Muted)
29243            .shape(IconButtonShape::Square)
29244            .tooltip(move |_window, cx| {
29245                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29246            })
29247            .on_click(cx.listener(|this, _, window, cx| {
29248                this.cancel(&menu::Cancel, window, cx);
29249            }))
29250    }
29251
29252    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29253        let focus_handle = self.prompt.focus_handle(cx);
29254        IconButton::new("confirm", IconName::Return)
29255            .icon_color(Color::Muted)
29256            .shape(IconButtonShape::Square)
29257            .tooltip(move |_window, cx| {
29258                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29259            })
29260            .on_click(cx.listener(|this, _, window, cx| {
29261                this.confirm(&menu::Confirm, window, cx);
29262            }))
29263    }
29264}
29265
29266impl Render for BreakpointPromptEditor {
29267    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29268        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29269        let editor_margins = *self.editor_margins.lock();
29270        let gutter_dimensions = editor_margins.gutter;
29271        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29272        let right_padding = editor_margins.right + px(9.);
29273        h_flex()
29274            .key_context("Editor")
29275            .bg(cx.theme().colors().editor_background)
29276            .border_y_1()
29277            .border_color(cx.theme().status().info_border)
29278            .size_full()
29279            .py(window.line_height() / 2.5)
29280            .pr(right_padding)
29281            .on_action(cx.listener(Self::confirm))
29282            .on_action(cx.listener(Self::cancel))
29283            .child(
29284                WithRemSize::new(ui_font_size)
29285                    .h_full()
29286                    .w(left_gutter_width)
29287                    .flex()
29288                    .flex_row()
29289                    .flex_shrink_0()
29290                    .items_center()
29291                    .justify_center()
29292                    .gap_1()
29293                    .child(self.render_close_button(cx)),
29294            )
29295            .child(
29296                h_flex()
29297                    .w_full()
29298                    .justify_between()
29299                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29300                    .child(
29301                        WithRemSize::new(ui_font_size)
29302                            .flex()
29303                            .flex_row()
29304                            .items_center()
29305                            .child(self.render_confirm_button(cx)),
29306                    ),
29307            )
29308    }
29309}
29310
29311impl Focusable for BreakpointPromptEditor {
29312    fn focus_handle(&self, cx: &App) -> FocusHandle {
29313        self.prompt.focus_handle(cx)
29314    }
29315}
29316
29317fn all_edits_insertions_or_deletions(
29318    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29319    snapshot: &MultiBufferSnapshot,
29320) -> bool {
29321    let mut all_insertions = true;
29322    let mut all_deletions = true;
29323
29324    for (range, new_text) in edits.iter() {
29325        let range_is_empty = range.to_offset(snapshot).is_empty();
29326        let text_is_empty = new_text.is_empty();
29327
29328        if range_is_empty != text_is_empty {
29329            if range_is_empty {
29330                all_deletions = false;
29331            } else {
29332                all_insertions = false;
29333            }
29334        } else {
29335            return false;
29336        }
29337
29338        if !all_insertions && !all_deletions {
29339            return false;
29340        }
29341    }
29342    all_insertions || all_deletions
29343}
29344
29345struct MissingEditPredictionKeybindingTooltip;
29346
29347impl Render for MissingEditPredictionKeybindingTooltip {
29348    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29349        ui::tooltip_container(cx, |container, cx| {
29350            container
29351                .flex_shrink_0()
29352                .max_w_80()
29353                .min_h(rems_from_px(124.))
29354                .justify_between()
29355                .child(
29356                    v_flex()
29357                        .flex_1()
29358                        .text_ui_sm(cx)
29359                        .child(Label::new("Conflict with Accept Keybinding"))
29360                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29361                )
29362                .child(
29363                    h_flex()
29364                        .pb_1()
29365                        .gap_1()
29366                        .items_end()
29367                        .w_full()
29368                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29369                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29370                        }))
29371                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29372                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29373                        })),
29374                )
29375        })
29376    }
29377}
29378
29379#[derive(Debug, Clone, Copy, PartialEq)]
29380pub struct LineHighlight {
29381    pub background: Background,
29382    pub border: Option<gpui::Hsla>,
29383    pub include_gutter: bool,
29384    pub type_id: Option<TypeId>,
29385}
29386
29387struct LineManipulationResult {
29388    pub new_text: String,
29389    pub line_count_before: usize,
29390    pub line_count_after: usize,
29391}
29392
29393fn render_diff_hunk_controls(
29394    row: u32,
29395    status: &DiffHunkStatus,
29396    hunk_range: Range<Anchor>,
29397    is_created_file: bool,
29398    line_height: Pixels,
29399    editor: &Entity<Editor>,
29400    _window: &mut Window,
29401    cx: &mut App,
29402) -> AnyElement {
29403    h_flex()
29404        .h(line_height)
29405        .mr_1()
29406        .gap_1()
29407        .px_0p5()
29408        .pb_1()
29409        .border_x_1()
29410        .border_b_1()
29411        .border_color(cx.theme().colors().border_variant)
29412        .rounded_b_lg()
29413        .bg(cx.theme().colors().editor_background)
29414        .gap_1()
29415        .block_mouse_except_scroll()
29416        .shadow_md()
29417        .child(if status.has_secondary_hunk() {
29418            Button::new(("stage", row as u64), "Stage")
29419                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29420                .tooltip({
29421                    let focus_handle = editor.focus_handle(cx);
29422                    move |_window, cx| {
29423                        Tooltip::for_action_in(
29424                            "Stage Hunk",
29425                            &::git::ToggleStaged,
29426                            &focus_handle,
29427                            cx,
29428                        )
29429                    }
29430                })
29431                .on_click({
29432                    let editor = editor.clone();
29433                    move |_event, _window, cx| {
29434                        editor.update(cx, |editor, cx| {
29435                            editor.stage_or_unstage_diff_hunks(
29436                                true,
29437                                vec![hunk_range.start..hunk_range.start],
29438                                cx,
29439                            );
29440                        });
29441                    }
29442                })
29443        } else {
29444            Button::new(("unstage", row as u64), "Unstage")
29445                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29446                .tooltip({
29447                    let focus_handle = editor.focus_handle(cx);
29448                    move |_window, cx| {
29449                        Tooltip::for_action_in(
29450                            "Unstage Hunk",
29451                            &::git::ToggleStaged,
29452                            &focus_handle,
29453                            cx,
29454                        )
29455                    }
29456                })
29457                .on_click({
29458                    let editor = editor.clone();
29459                    move |_event, _window, cx| {
29460                        editor.update(cx, |editor, cx| {
29461                            editor.stage_or_unstage_diff_hunks(
29462                                false,
29463                                vec![hunk_range.start..hunk_range.start],
29464                                cx,
29465                            );
29466                        });
29467                    }
29468                })
29469        })
29470        .child(
29471            Button::new(("restore", row as u64), "Restore")
29472                .tooltip({
29473                    let focus_handle = editor.focus_handle(cx);
29474                    move |_window, cx| {
29475                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29476                    }
29477                })
29478                .on_click({
29479                    let editor = editor.clone();
29480                    move |_event, window, cx| {
29481                        editor.update(cx, |editor, cx| {
29482                            let snapshot = editor.snapshot(window, cx);
29483                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29484                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29485                        });
29486                    }
29487                })
29488                .disabled(is_created_file),
29489        )
29490        .when(
29491            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29492            |el| {
29493                el.child(
29494                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29495                        .shape(IconButtonShape::Square)
29496                        .icon_size(IconSize::Small)
29497                        // .disabled(!has_multiple_hunks)
29498                        .tooltip({
29499                            let focus_handle = editor.focus_handle(cx);
29500                            move |_window, cx| {
29501                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29502                            }
29503                        })
29504                        .on_click({
29505                            let editor = editor.clone();
29506                            move |_event, window, cx| {
29507                                editor.update(cx, |editor, cx| {
29508                                    let snapshot = editor.snapshot(window, cx);
29509                                    let position =
29510                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29511                                    editor.go_to_hunk_before_or_after_position(
29512                                        &snapshot,
29513                                        position,
29514                                        Direction::Next,
29515                                        true,
29516                                        window,
29517                                        cx,
29518                                    );
29519                                    editor.expand_selected_diff_hunks(cx);
29520                                });
29521                            }
29522                        }),
29523                )
29524                .child(
29525                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29526                        .shape(IconButtonShape::Square)
29527                        .icon_size(IconSize::Small)
29528                        // .disabled(!has_multiple_hunks)
29529                        .tooltip({
29530                            let focus_handle = editor.focus_handle(cx);
29531                            move |_window, cx| {
29532                                Tooltip::for_action_in(
29533                                    "Previous Hunk",
29534                                    &GoToPreviousHunk,
29535                                    &focus_handle,
29536                                    cx,
29537                                )
29538                            }
29539                        })
29540                        .on_click({
29541                            let editor = editor.clone();
29542                            move |_event, window, cx| {
29543                                editor.update(cx, |editor, cx| {
29544                                    let snapshot = editor.snapshot(window, cx);
29545                                    let point =
29546                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29547                                    editor.go_to_hunk_before_or_after_position(
29548                                        &snapshot,
29549                                        point,
29550                                        Direction::Prev,
29551                                        true,
29552                                        window,
29553                                        cx,
29554                                    );
29555                                    editor.expand_selected_diff_hunks(cx);
29556                                });
29557                            }
29558                        }),
29559                )
29560            },
29561        )
29562        .into_any_element()
29563}
29564
29565pub fn multibuffer_context_lines(cx: &App) -> u32 {
29566    EditorSettings::try_get(cx)
29567        .map(|settings| settings.excerpt_context_lines)
29568        .unwrap_or(2)
29569        .min(32)
29570}