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::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
  137    WordsQuery,
  138    language_settings::{
  139        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  140        all_language_settings, language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::DB;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::TaskVariables;
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207    ThemeSettings, observe_buffer_font_size_adjustment,
  208};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224use zed_actions::editor::{MoveDown, MoveUp};
  225
  226use crate::{
  227    code_context_menus::CompletionsMenuSource,
  228    editor_settings::MultiCursorModifier,
  229    hover_links::{find_url, find_url_from_range},
  230    inlays::{
  231        InlineValueCache,
  232        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  233    },
  234    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  235    scroll::{ScrollOffset, ScrollPixelOffset},
  236    selections_collection::resolve_selections_wrapping_blocks,
  237    semantic_tokens::SemanticTokenState,
  238    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  239};
  240
  241pub const FILE_HEADER_HEIGHT: u32 = 2;
  242pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  243pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  244const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  245const MAX_LINE_LEN: usize = 1024;
  246const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  247const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  248pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  249#[doc(hidden)]
  250pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  251pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  252
  253pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  254pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  256pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  257
  258pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  259pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  260pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  261
  262pub type RenderDiffHunkControlsFn = Arc<
  263    dyn Fn(
  264        u32,
  265        &DiffHunkStatus,
  266        Range<Anchor>,
  267        bool,
  268        Pixels,
  269        &Entity<Editor>,
  270        &mut Window,
  271        &mut App,
  272    ) -> AnyElement,
  273>;
  274
  275enum ReportEditorEvent {
  276    Saved { auto_saved: bool },
  277    EditorOpened,
  278    Closed,
  279}
  280
  281impl ReportEditorEvent {
  282    pub fn event_type(&self) -> &'static str {
  283        match self {
  284            Self::Saved { .. } => "Editor Saved",
  285            Self::EditorOpened => "Editor Opened",
  286            Self::Closed => "Editor Closed",
  287        }
  288    }
  289}
  290
  291pub enum ActiveDebugLine {}
  292pub enum DebugStackFrameLine {}
  293
  294pub enum ConflictsOuter {}
  295pub enum ConflictsOurs {}
  296pub enum ConflictsTheirs {}
  297pub enum ConflictsOursMarker {}
  298pub enum ConflictsTheirsMarker {}
  299
  300pub struct HunkAddedColor;
  301pub struct HunkRemovedColor;
  302
  303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  304pub enum Navigated {
  305    Yes,
  306    No,
  307}
  308
  309impl Navigated {
  310    pub fn from_bool(yes: bool) -> Navigated {
  311        if yes { Navigated::Yes } else { Navigated::No }
  312    }
  313}
  314
  315#[derive(Debug, Clone, PartialEq, Eq)]
  316enum DisplayDiffHunk {
  317    Folded {
  318        display_row: DisplayRow,
  319    },
  320    Unfolded {
  321        is_created_file: bool,
  322        diff_base_byte_range: Range<usize>,
  323        display_row_range: Range<DisplayRow>,
  324        multi_buffer_range: Range<Anchor>,
  325        status: DiffHunkStatus,
  326        word_diffs: Vec<Range<MultiBufferOffset>>,
  327    },
  328}
  329
  330pub enum HideMouseCursorOrigin {
  331    TypingAction,
  332    MovementAction,
  333}
  334
  335pub fn init(cx: &mut App) {
  336    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  337    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  338
  339    workspace::register_project_item::<Editor>(cx);
  340    workspace::FollowableViewRegistry::register::<Editor>(cx);
  341    workspace::register_serializable_item::<Editor>(cx);
  342
  343    cx.observe_new(
  344        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  345            workspace.register_action(Editor::new_file);
  346            workspace.register_action(Editor::new_file_split);
  347            workspace.register_action(Editor::new_file_vertical);
  348            workspace.register_action(Editor::new_file_horizontal);
  349            workspace.register_action(Editor::cancel_language_server_work);
  350            workspace.register_action(Editor::toggle_focus);
  351        },
  352    )
  353    .detach();
  354
  355    cx.on_action(move |_: &workspace::NewFile, cx| {
  356        let app_state = workspace::AppState::global(cx);
  357        if let Some(app_state) = app_state.upgrade() {
  358            workspace::open_new(
  359                Default::default(),
  360                app_state,
  361                cx,
  362                |workspace, window, cx| {
  363                    Editor::new_file(workspace, &Default::default(), window, cx)
  364                },
  365            )
  366            .detach_and_log_err(cx);
  367        }
  368    })
  369    .on_action(move |_: &workspace::NewWindow, cx| {
  370        let app_state = workspace::AppState::global(cx);
  371        if let Some(app_state) = app_state.upgrade() {
  372            workspace::open_new(
  373                Default::default(),
  374                app_state,
  375                cx,
  376                |workspace, window, cx| {
  377                    cx.activate(true);
  378                    Editor::new_file(workspace, &Default::default(), window, cx)
  379                },
  380            )
  381            .detach_and_log_err(cx);
  382        }
  383    });
  384    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  385        Arc::new(ErasedEditorImpl(
  386            cx.new(|cx| Editor::single_line(window, cx)),
  387        )) as Arc<dyn ErasedEditor>
  388    });
  389    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  390}
  391
  392pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  393    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  394}
  395
  396pub trait DiagnosticRenderer {
  397    fn render_group(
  398        &self,
  399        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  400        buffer_id: BufferId,
  401        snapshot: EditorSnapshot,
  402        editor: WeakEntity<Editor>,
  403        language_registry: Option<Arc<LanguageRegistry>>,
  404        cx: &mut App,
  405    ) -> Vec<BlockProperties<Anchor>>;
  406
  407    fn render_hover(
  408        &self,
  409        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  410        range: Range<Point>,
  411        buffer_id: BufferId,
  412        language_registry: Option<Arc<LanguageRegistry>>,
  413        cx: &mut App,
  414    ) -> Option<Entity<markdown::Markdown>>;
  415
  416    fn open_link(
  417        &self,
  418        editor: &mut Editor,
  419        link: SharedString,
  420        window: &mut Window,
  421        cx: &mut Context<Editor>,
  422    );
  423}
  424
  425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  426
  427impl GlobalDiagnosticRenderer {
  428    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  429        cx.try_global::<Self>().map(|g| g.0.clone())
  430    }
  431}
  432
  433impl gpui::Global for GlobalDiagnosticRenderer {}
  434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  435    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  436}
  437
  438pub struct SearchWithinRange;
  439
  440trait InvalidationRegion {
  441    fn ranges(&self) -> &[Range<Anchor>];
  442}
  443
  444#[derive(Clone, Debug, PartialEq)]
  445pub enum SelectPhase {
  446    Begin {
  447        position: DisplayPoint,
  448        add: bool,
  449        click_count: usize,
  450    },
  451    BeginColumnar {
  452        position: DisplayPoint,
  453        reset: bool,
  454        mode: ColumnarMode,
  455        goal_column: u32,
  456    },
  457    Extend {
  458        position: DisplayPoint,
  459        click_count: usize,
  460    },
  461    Update {
  462        position: DisplayPoint,
  463        goal_column: u32,
  464        scroll_delta: gpui::Point<f32>,
  465    },
  466    End,
  467}
  468
  469#[derive(Clone, Debug, PartialEq)]
  470pub enum ColumnarMode {
  471    FromMouse,
  472    FromSelection,
  473}
  474
  475#[derive(Clone, Debug)]
  476pub enum SelectMode {
  477    Character,
  478    Word(Range<Anchor>),
  479    Line(Range<Anchor>),
  480    All,
  481}
  482
  483#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  484pub enum SizingBehavior {
  485    /// The editor will layout itself using `size_full` and will include the vertical
  486    /// scroll margin as requested by user settings.
  487    #[default]
  488    Default,
  489    /// The editor will layout itself using `size_full`, but will not have any
  490    /// vertical overscroll.
  491    ExcludeOverscrollMargin,
  492    /// The editor will request a vertical size according to its content and will be
  493    /// layouted without a vertical scroll margin.
  494    SizeByContent,
  495}
  496
  497#[derive(Clone, PartialEq, Eq, Debug)]
  498pub enum EditorMode {
  499    SingleLine,
  500    AutoHeight {
  501        min_lines: usize,
  502        max_lines: Option<usize>,
  503    },
  504    Full {
  505        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  506        scale_ui_elements_with_buffer_font_size: bool,
  507        /// When set to `true`, the editor will render a background for the active line.
  508        show_active_line_background: bool,
  509        /// Determines the sizing behavior for this editor
  510        sizing_behavior: SizingBehavior,
  511    },
  512    Minimap {
  513        parent: WeakEntity<Editor>,
  514    },
  515}
  516
  517impl EditorMode {
  518    pub fn full() -> Self {
  519        Self::Full {
  520            scale_ui_elements_with_buffer_font_size: true,
  521            show_active_line_background: true,
  522            sizing_behavior: SizingBehavior::Default,
  523        }
  524    }
  525
  526    #[inline]
  527    pub fn is_full(&self) -> bool {
  528        matches!(self, Self::Full { .. })
  529    }
  530
  531    #[inline]
  532    pub fn is_single_line(&self) -> bool {
  533        matches!(self, Self::SingleLine { .. })
  534    }
  535
  536    #[inline]
  537    fn is_minimap(&self) -> bool {
  538        matches!(self, Self::Minimap { .. })
  539    }
  540}
  541
  542#[derive(Copy, Clone, Debug)]
  543pub enum SoftWrap {
  544    /// Prefer not to wrap at all.
  545    ///
  546    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  547    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  548    GitDiff,
  549    /// Prefer a single line generally, unless an overly long line is encountered.
  550    None,
  551    /// Soft wrap lines that exceed the editor width.
  552    EditorWidth,
  553    /// Soft wrap lines at the preferred line length.
  554    Column(u32),
  555    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  556    Bounded(u32),
  557}
  558
  559#[derive(Clone)]
  560pub struct EditorStyle {
  561    pub background: Hsla,
  562    pub border: Hsla,
  563    pub local_player: PlayerColor,
  564    pub text: TextStyle,
  565    pub scrollbar_width: Pixels,
  566    pub syntax: Arc<SyntaxTheme>,
  567    pub status: StatusColors,
  568    pub inlay_hints_style: HighlightStyle,
  569    pub edit_prediction_styles: EditPredictionStyles,
  570    pub unnecessary_code_fade: f32,
  571    pub show_underlines: bool,
  572}
  573
  574impl Default for EditorStyle {
  575    fn default() -> Self {
  576        Self {
  577            background: Hsla::default(),
  578            border: Hsla::default(),
  579            local_player: PlayerColor::default(),
  580            text: TextStyle::default(),
  581            scrollbar_width: Pixels::default(),
  582            syntax: Default::default(),
  583            // HACK: Status colors don't have a real default.
  584            // We should look into removing the status colors from the editor
  585            // style and retrieve them directly from the theme.
  586            status: StatusColors::dark(),
  587            inlay_hints_style: HighlightStyle::default(),
  588            edit_prediction_styles: EditPredictionStyles {
  589                insertion: HighlightStyle::default(),
  590                whitespace: HighlightStyle::default(),
  591            },
  592            unnecessary_code_fade: Default::default(),
  593            show_underlines: true,
  594        }
  595    }
  596}
  597
  598pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  599    let show_background = language_settings::language_settings(None, None, cx)
  600        .inlay_hints
  601        .show_background;
  602
  603    let mut style = cx.theme().syntax().get("hint");
  604
  605    if style.color.is_none() {
  606        style.color = Some(cx.theme().status().hint);
  607    }
  608
  609    if !show_background {
  610        style.background_color = None;
  611        return style;
  612    }
  613
  614    if style.background_color.is_none() {
  615        style.background_color = Some(cx.theme().status().hint_background);
  616    }
  617
  618    style
  619}
  620
  621pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  622    EditPredictionStyles {
  623        insertion: HighlightStyle {
  624            color: Some(cx.theme().status().predictive),
  625            ..HighlightStyle::default()
  626        },
  627        whitespace: HighlightStyle {
  628            background_color: Some(cx.theme().status().created_background),
  629            ..HighlightStyle::default()
  630        },
  631    }
  632}
  633
  634type CompletionId = usize;
  635
  636pub(crate) enum EditDisplayMode {
  637    TabAccept,
  638    DiffPopover,
  639    Inline,
  640}
  641
  642enum EditPrediction {
  643    Edit {
  644        edits: Vec<(Range<Anchor>, Arc<str>)>,
  645        /// Predicted cursor position as (anchor, offset_from_anchor).
  646        /// The anchor is in multibuffer coordinates; after applying edits,
  647        /// resolve the anchor and add the offset to get the final cursor position.
  648        cursor_position: Option<(Anchor, usize)>,
  649        edit_preview: Option<EditPreview>,
  650        display_mode: EditDisplayMode,
  651        snapshot: BufferSnapshot,
  652    },
  653    /// Move to a specific location in the active editor
  654    MoveWithin {
  655        target: Anchor,
  656        snapshot: BufferSnapshot,
  657    },
  658    /// Move to a specific location in a different editor (not the active one)
  659    MoveOutside {
  660        target: language::Anchor,
  661        snapshot: BufferSnapshot,
  662    },
  663}
  664
  665struct EditPredictionState {
  666    inlay_ids: Vec<InlayId>,
  667    completion: EditPrediction,
  668    completion_id: Option<SharedString>,
  669    invalidation_range: Option<Range<Anchor>>,
  670}
  671
  672enum EditPredictionSettings {
  673    Disabled,
  674    Enabled {
  675        show_in_menu: bool,
  676        preview_requires_modifier: bool,
  677    },
  678}
  679
  680#[derive(Debug, Clone)]
  681struct InlineDiagnostic {
  682    message: SharedString,
  683    group_id: usize,
  684    is_primary: bool,
  685    start: Point,
  686    severity: lsp::DiagnosticSeverity,
  687}
  688
  689pub enum MenuEditPredictionsPolicy {
  690    Never,
  691    ByProvider,
  692}
  693
  694pub enum EditPredictionPreview {
  695    /// Modifier is not pressed
  696    Inactive { released_too_fast: bool },
  697    /// Modifier pressed
  698    Active {
  699        since: Instant,
  700        previous_scroll_position: Option<SharedScrollAnchor>,
  701    },
  702}
  703
  704impl EditPredictionPreview {
  705    pub fn released_too_fast(&self) -> bool {
  706        match self {
  707            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  708            EditPredictionPreview::Active { .. } => false,
  709        }
  710    }
  711
  712    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  713        if let EditPredictionPreview::Active {
  714            previous_scroll_position,
  715            ..
  716        } = self
  717        {
  718            *previous_scroll_position = scroll_position;
  719        }
  720    }
  721}
  722
  723pub struct ContextMenuOptions {
  724    pub min_entries_visible: usize,
  725    pub max_entries_visible: usize,
  726    pub placement: Option<ContextMenuPlacement>,
  727}
  728
  729#[derive(Debug, Clone, PartialEq, Eq)]
  730pub enum ContextMenuPlacement {
  731    Above,
  732    Below,
  733}
  734
  735#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  736struct EditorActionId(usize);
  737
  738impl EditorActionId {
  739    pub fn post_inc(&mut self) -> Self {
  740        let answer = self.0;
  741
  742        *self = Self(answer + 1);
  743
  744        Self(answer)
  745    }
  746}
  747
  748// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  749// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  750
  751type BackgroundHighlight = (
  752    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  753    Arc<[Range<Anchor>]>,
  754);
  755type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  756
  757#[derive(Default)]
  758struct ScrollbarMarkerState {
  759    scrollbar_size: Size<Pixels>,
  760    dirty: bool,
  761    markers: Arc<[PaintQuad]>,
  762    pending_refresh: Option<Task<Result<()>>>,
  763}
  764
  765impl ScrollbarMarkerState {
  766    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  767        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  768    }
  769}
  770
  771#[derive(Clone, Copy, PartialEq, Eq)]
  772pub enum MinimapVisibility {
  773    Disabled,
  774    Enabled {
  775        /// The configuration currently present in the users settings.
  776        setting_configuration: bool,
  777        /// Whether to override the currently set visibility from the users setting.
  778        toggle_override: bool,
  779    },
  780}
  781
  782impl MinimapVisibility {
  783    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  784        if mode.is_full() {
  785            Self::Enabled {
  786                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  787                toggle_override: false,
  788            }
  789        } else {
  790            Self::Disabled
  791        }
  792    }
  793
  794    fn hidden(&self) -> Self {
  795        match *self {
  796            Self::Enabled {
  797                setting_configuration,
  798                ..
  799            } => Self::Enabled {
  800                setting_configuration,
  801                toggle_override: setting_configuration,
  802            },
  803            Self::Disabled => Self::Disabled,
  804        }
  805    }
  806
  807    fn disabled(&self) -> bool {
  808        matches!(*self, Self::Disabled)
  809    }
  810
  811    fn settings_visibility(&self) -> bool {
  812        match *self {
  813            Self::Enabled {
  814                setting_configuration,
  815                ..
  816            } => setting_configuration,
  817            _ => false,
  818        }
  819    }
  820
  821    fn visible(&self) -> bool {
  822        match *self {
  823            Self::Enabled {
  824                setting_configuration,
  825                toggle_override,
  826            } => setting_configuration ^ toggle_override,
  827            _ => false,
  828        }
  829    }
  830
  831    fn toggle_visibility(&self) -> Self {
  832        match *self {
  833            Self::Enabled {
  834                toggle_override,
  835                setting_configuration,
  836            } => Self::Enabled {
  837                setting_configuration,
  838                toggle_override: !toggle_override,
  839            },
  840            Self::Disabled => Self::Disabled,
  841        }
  842    }
  843}
  844
  845#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  846pub enum BufferSerialization {
  847    All,
  848    NonDirtyBuffers,
  849}
  850
  851impl BufferSerialization {
  852    fn new(restore_unsaved_buffers: bool) -> Self {
  853        if restore_unsaved_buffers {
  854            Self::All
  855        } else {
  856            Self::NonDirtyBuffers
  857        }
  858    }
  859}
  860
  861/// Addons allow storing per-editor state in other crates (e.g. Vim)
  862pub trait Addon: 'static {
  863    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  864
  865    fn render_buffer_header_controls(
  866        &self,
  867        _: &ExcerptInfo,
  868        _: &Window,
  869        _: &App,
  870    ) -> Option<AnyElement> {
  871        None
  872    }
  873
  874    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  875        None
  876    }
  877
  878    fn to_any(&self) -> &dyn std::any::Any;
  879
  880    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  881        None
  882    }
  883}
  884
  885struct ChangeLocation {
  886    current: Option<Vec<Anchor>>,
  887    original: Vec<Anchor>,
  888}
  889impl ChangeLocation {
  890    fn locations(&self) -> &[Anchor] {
  891        self.current.as_ref().unwrap_or(&self.original)
  892    }
  893}
  894
  895/// A set of caret positions, registered when the editor was edited.
  896pub struct ChangeList {
  897    changes: Vec<ChangeLocation>,
  898    /// Currently "selected" change.
  899    position: Option<usize>,
  900}
  901
  902impl ChangeList {
  903    pub fn new() -> Self {
  904        Self {
  905            changes: Vec::new(),
  906            position: None,
  907        }
  908    }
  909
  910    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  911    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  912    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  913        if self.changes.is_empty() {
  914            return None;
  915        }
  916
  917        let prev = self.position.unwrap_or(self.changes.len());
  918        let next = if direction == Direction::Prev {
  919            prev.saturating_sub(count)
  920        } else {
  921            (prev + count).min(self.changes.len() - 1)
  922        };
  923        self.position = Some(next);
  924        self.changes.get(next).map(|change| change.locations())
  925    }
  926
  927    /// Adds a new change to the list, resetting the change list position.
  928    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  929        self.position.take();
  930        if let Some(last) = self.changes.last_mut()
  931            && group
  932        {
  933            last.current = Some(new_positions)
  934        } else {
  935            self.changes.push(ChangeLocation {
  936                original: new_positions,
  937                current: None,
  938            });
  939        }
  940    }
  941
  942    pub fn last(&self) -> Option<&[Anchor]> {
  943        self.changes.last().map(|change| change.locations())
  944    }
  945
  946    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  947        self.changes.last().map(|change| change.original.as_slice())
  948    }
  949
  950    pub fn invert_last_group(&mut self) {
  951        if let Some(last) = self.changes.last_mut()
  952            && let Some(current) = last.current.as_mut()
  953        {
  954            mem::swap(&mut last.original, current);
  955        }
  956    }
  957}
  958
  959#[derive(Clone)]
  960struct InlineBlamePopoverState {
  961    scroll_handle: ScrollHandle,
  962    commit_message: Option<ParsedCommitMessage>,
  963    markdown: Entity<Markdown>,
  964}
  965
  966struct InlineBlamePopover {
  967    position: gpui::Point<Pixels>,
  968    hide_task: Option<Task<()>>,
  969    popover_bounds: Option<Bounds<Pixels>>,
  970    popover_state: InlineBlamePopoverState,
  971    keyboard_grace: bool,
  972}
  973
  974enum SelectionDragState {
  975    /// State when no drag related activity is detected.
  976    None,
  977    /// State when the mouse is down on a selection that is about to be dragged.
  978    ReadyToDrag {
  979        selection: Selection<Anchor>,
  980        click_position: gpui::Point<Pixels>,
  981        mouse_down_time: Instant,
  982    },
  983    /// State when the mouse is dragging the selection in the editor.
  984    Dragging {
  985        selection: Selection<Anchor>,
  986        drop_cursor: Selection<Anchor>,
  987        hide_drop_cursor: bool,
  988    },
  989}
  990
  991enum ColumnarSelectionState {
  992    FromMouse {
  993        selection_tail: Anchor,
  994        display_point: Option<DisplayPoint>,
  995    },
  996    FromSelection {
  997        selection_tail: Anchor,
  998    },
  999}
 1000
 1001/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1002/// a breakpoint on them.
 1003#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1004struct PhantomBreakpointIndicator {
 1005    display_row: DisplayRow,
 1006    /// There's a small debounce between hovering over the line and showing the indicator.
 1007    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1008    is_active: bool,
 1009    collides_with_existing_breakpoint: bool,
 1010}
 1011
 1012/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1013/// in diff view mode.
 1014#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1015pub(crate) struct PhantomDiffReviewIndicator {
 1016    /// The starting anchor of the selection (or the only row if not dragging).
 1017    pub start: Anchor,
 1018    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1019    pub end: Anchor,
 1020    /// There's a small debounce between hovering over the line and showing the indicator.
 1021    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1022    pub is_active: bool,
 1023}
 1024
 1025#[derive(Clone, Debug)]
 1026pub(crate) struct DiffReviewDragState {
 1027    pub start_anchor: Anchor,
 1028    pub current_anchor: Anchor,
 1029}
 1030
 1031impl DiffReviewDragState {
 1032    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1033        let start = self.start_anchor.to_display_point(snapshot).row();
 1034        let current = self.current_anchor.to_display_point(snapshot).row();
 1035
 1036        (start..=current).sorted()
 1037    }
 1038}
 1039
 1040/// Identifies a specific hunk in the diff buffer.
 1041/// Used as a key to group comments by their location.
 1042#[derive(Clone, Debug)]
 1043pub struct DiffHunkKey {
 1044    /// The file path (relative to worktree) this hunk belongs to.
 1045    pub file_path: Arc<util::rel_path::RelPath>,
 1046    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1047    pub hunk_start_anchor: Anchor,
 1048}
 1049
 1050/// A review comment stored locally before being sent to the Agent panel.
 1051#[derive(Clone)]
 1052pub struct StoredReviewComment {
 1053    /// Unique identifier for this comment (for edit/delete operations).
 1054    pub id: usize,
 1055    /// The comment text entered by the user.
 1056    pub comment: String,
 1057    /// Anchors for the code range being reviewed.
 1058    pub range: Range<Anchor>,
 1059    /// Timestamp when the comment was created (for chronological ordering).
 1060    pub created_at: Instant,
 1061    /// Whether this comment is currently being edited inline.
 1062    pub is_editing: bool,
 1063}
 1064
 1065impl StoredReviewComment {
 1066    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1067        Self {
 1068            id,
 1069            comment,
 1070            range: anchor_range,
 1071            created_at: Instant::now(),
 1072            is_editing: false,
 1073        }
 1074    }
 1075}
 1076
 1077/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1078pub(crate) struct DiffReviewOverlay {
 1079    pub anchor_range: Range<Anchor>,
 1080    /// The block ID for the overlay.
 1081    pub block_id: CustomBlockId,
 1082    /// The editor entity for the review input.
 1083    pub prompt_editor: Entity<Editor>,
 1084    /// The hunk key this overlay belongs to.
 1085    pub hunk_key: DiffHunkKey,
 1086    /// Whether the comments section is expanded.
 1087    pub comments_expanded: bool,
 1088    /// Editors for comments currently being edited inline.
 1089    /// Key: comment ID, Value: Editor entity for inline editing.
 1090    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1091    /// Subscriptions for inline edit editors' action handlers.
 1092    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1093    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1094    /// The current user's avatar URI for display in comment rows.
 1095    pub user_avatar_uri: Option<SharedUri>,
 1096    /// Subscription to keep the action handler alive.
 1097    _subscription: Subscription,
 1098}
 1099
 1100/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1101///
 1102/// See the [module level documentation](self) for more information.
 1103pub struct Editor {
 1104    focus_handle: FocusHandle,
 1105    last_focused_descendant: Option<WeakFocusHandle>,
 1106    /// The text buffer being edited
 1107    buffer: Entity<MultiBuffer>,
 1108    /// Map of how text in the buffer should be displayed.
 1109    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1110    pub display_map: Entity<DisplayMap>,
 1111    placeholder_display_map: Option<Entity<DisplayMap>>,
 1112    pub selections: SelectionsCollection,
 1113    pub scroll_manager: ScrollManager,
 1114    /// When inline assist editors are linked, they all render cursors because
 1115    /// typing enters text into each of them, even the ones that aren't focused.
 1116    pub(crate) show_cursor_when_unfocused: bool,
 1117    columnar_selection_state: Option<ColumnarSelectionState>,
 1118    add_selections_state: Option<AddSelectionsState>,
 1119    select_next_state: Option<SelectNextState>,
 1120    select_prev_state: Option<SelectNextState>,
 1121    selection_history: SelectionHistory,
 1122    defer_selection_effects: bool,
 1123    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1124    autoclose_regions: Vec<AutocloseRegion>,
 1125    snippet_stack: InvalidationStack<SnippetState>,
 1126    select_syntax_node_history: SelectSyntaxNodeHistory,
 1127    ime_transaction: Option<TransactionId>,
 1128    pub diagnostics_max_severity: DiagnosticSeverity,
 1129    active_diagnostics: ActiveDiagnostic,
 1130    show_inline_diagnostics: bool,
 1131    inline_diagnostics_update: Task<()>,
 1132    inline_diagnostics_enabled: bool,
 1133    diagnostics_enabled: bool,
 1134    word_completions_enabled: bool,
 1135    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1136    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1137    hard_wrap: Option<usize>,
 1138    project: Option<Entity<Project>>,
 1139    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1140    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1141    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1142    blink_manager: Entity<BlinkManager>,
 1143    show_cursor_names: bool,
 1144    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1145    pub show_local_selections: bool,
 1146    mode: EditorMode,
 1147    show_breadcrumbs: bool,
 1148    show_gutter: bool,
 1149    show_scrollbars: ScrollbarAxes,
 1150    minimap_visibility: MinimapVisibility,
 1151    offset_content: bool,
 1152    disable_expand_excerpt_buttons: bool,
 1153    delegate_expand_excerpts: bool,
 1154    delegate_stage_and_restore: bool,
 1155    delegate_open_excerpts: bool,
 1156    enable_lsp_data: bool,
 1157    enable_runnables: bool,
 1158    show_line_numbers: Option<bool>,
 1159    use_relative_line_numbers: Option<bool>,
 1160    show_git_diff_gutter: Option<bool>,
 1161    show_code_actions: Option<bool>,
 1162    show_runnables: Option<bool>,
 1163    show_breakpoints: Option<bool>,
 1164    show_diff_review_button: bool,
 1165    show_wrap_guides: Option<bool>,
 1166    show_indent_guides: Option<bool>,
 1167    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1168    highlight_order: usize,
 1169    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1170    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1171    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1172    scrollbar_marker_state: ScrollbarMarkerState,
 1173    active_indent_guides_state: ActiveIndentGuidesState,
 1174    nav_history: Option<ItemNavHistory>,
 1175    context_menu: RefCell<Option<CodeContextMenu>>,
 1176    context_menu_options: Option<ContextMenuOptions>,
 1177    mouse_context_menu: Option<MouseContextMenu>,
 1178    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1179    inline_blame_popover: Option<InlineBlamePopover>,
 1180    inline_blame_popover_show_task: Option<Task<()>>,
 1181    signature_help_state: SignatureHelpState,
 1182    auto_signature_help: Option<bool>,
 1183    find_all_references_task_sources: Vec<Anchor>,
 1184    next_completion_id: CompletionId,
 1185    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1186    code_actions_task: Option<Task<Result<()>>>,
 1187    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1188    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1189    debounced_selection_highlight_complete: bool,
 1190    document_highlights_task: Option<Task<()>>,
 1191    linked_editing_range_task: Option<Task<Option<()>>>,
 1192    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1193    pending_rename: Option<RenameState>,
 1194    searchable: bool,
 1195    cursor_shape: CursorShape,
 1196    /// Whether the cursor is offset one character to the left when something is
 1197    /// selected (needed for vim visual mode)
 1198    cursor_offset_on_selection: bool,
 1199    current_line_highlight: Option<CurrentLineHighlight>,
 1200    /// Whether to collapse search match ranges to just their start position.
 1201    /// When true, navigating to a match positions the cursor at the match
 1202    /// without selecting the matched text.
 1203    collapse_matches: bool,
 1204    autoindent_mode: Option<AutoindentMode>,
 1205    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1206    input_enabled: bool,
 1207    expects_character_input: bool,
 1208    use_modal_editing: bool,
 1209    read_only: bool,
 1210    leader_id: Option<CollaboratorId>,
 1211    remote_id: Option<ViewId>,
 1212    pub hover_state: HoverState,
 1213    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1214    prev_pressure_stage: Option<PressureStage>,
 1215    gutter_hovered: bool,
 1216    hovered_link_state: Option<HoveredLinkState>,
 1217    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1218    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1219    active_edit_prediction: Option<EditPredictionState>,
 1220    /// Used to prevent flickering as the user types while the menu is open
 1221    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1222    edit_prediction_settings: EditPredictionSettings,
 1223    edit_predictions_hidden_for_vim_mode: bool,
 1224    show_edit_predictions_override: Option<bool>,
 1225    show_completions_on_input_override: Option<bool>,
 1226    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1227    edit_prediction_preview: EditPredictionPreview,
 1228    edit_prediction_indent_conflict: bool,
 1229    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1230    next_inlay_id: usize,
 1231    next_color_inlay_id: usize,
 1232    _subscriptions: Vec<Subscription>,
 1233    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1234    gutter_dimensions: GutterDimensions,
 1235    style: Option<EditorStyle>,
 1236    text_style_refinement: Option<TextStyleRefinement>,
 1237    next_editor_action_id: EditorActionId,
 1238    editor_actions: Rc<
 1239        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1240    >,
 1241    use_autoclose: bool,
 1242    use_auto_surround: bool,
 1243    auto_replace_emoji_shortcode: bool,
 1244    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1245    show_git_blame_gutter: bool,
 1246    show_git_blame_inline: bool,
 1247    show_git_blame_inline_delay_task: Option<Task<()>>,
 1248    git_blame_inline_enabled: bool,
 1249    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1250    buffer_serialization: Option<BufferSerialization>,
 1251    show_selection_menu: Option<bool>,
 1252    blame: Option<Entity<GitBlame>>,
 1253    blame_subscription: Option<Subscription>,
 1254    custom_context_menu: Option<
 1255        Box<
 1256            dyn 'static
 1257                + Fn(
 1258                    &mut Self,
 1259                    DisplayPoint,
 1260                    &mut Window,
 1261                    &mut Context<Self>,
 1262                ) -> Option<Entity<ui::ContextMenu>>,
 1263        >,
 1264    >,
 1265    last_bounds: Option<Bounds<Pixels>>,
 1266    last_position_map: Option<Rc<PositionMap>>,
 1267    expect_bounds_change: Option<Bounds<Pixels>>,
 1268    runnables: RunnableData,
 1269    breakpoint_store: Option<Entity<BreakpointStore>>,
 1270    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1271    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1272    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1273    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1274    /// when hunks have comments stored.
 1275    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1276    /// Stored review comments grouped by hunk.
 1277    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1278    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1279    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1280    /// Counter for generating unique comment IDs.
 1281    next_review_comment_id: usize,
 1282    hovered_diff_hunk_row: Option<DisplayRow>,
 1283    pull_diagnostics_task: Task<()>,
 1284    in_project_search: bool,
 1285    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1286    breadcrumb_header: Option<String>,
 1287    focused_block: Option<FocusedBlock>,
 1288    next_scroll_position: NextScrollCursorCenterTopBottom,
 1289    addons: HashMap<TypeId, Box<dyn Addon>>,
 1290    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1291    load_diff_task: Option<Shared<Task<()>>>,
 1292    /// Whether we are temporarily displaying a diff other than git's
 1293    temporary_diff_override: bool,
 1294    selection_mark_mode: bool,
 1295    toggle_fold_multiple_buffers: Task<()>,
 1296    _scroll_cursor_center_top_bottom_task: Task<()>,
 1297    serialize_selections: Task<()>,
 1298    serialize_folds: Task<()>,
 1299    mouse_cursor_hidden: bool,
 1300    minimap: Option<Entity<Self>>,
 1301    hide_mouse_mode: HideMouseMode,
 1302    pub change_list: ChangeList,
 1303    inline_value_cache: InlineValueCache,
 1304    number_deleted_lines: bool,
 1305
 1306    selection_drag_state: SelectionDragState,
 1307    colors: Option<LspColorData>,
 1308    post_scroll_update: Task<()>,
 1309    refresh_colors_task: Task<()>,
 1310    use_document_folding_ranges: bool,
 1311    refresh_folding_ranges_task: Task<()>,
 1312    inlay_hints: Option<LspInlayHintData>,
 1313    folding_newlines: Task<()>,
 1314    select_next_is_case_sensitive: Option<bool>,
 1315    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1316    on_local_selections_changed:
 1317        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1318    suppress_selection_callback: bool,
 1319    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1320    accent_data: Option<AccentData>,
 1321    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1322    semantic_token_state: SemanticTokenState,
 1323    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1324    refresh_document_symbols_task: Shared<Task<()>>,
 1325    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1326    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1327    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1328    sticky_headers_task: Task<()>,
 1329    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1330    pub(crate) colorize_brackets_task: Task<()>,
 1331}
 1332
 1333#[derive(Debug, PartialEq)]
 1334struct AccentData {
 1335    colors: AccentColors,
 1336    overrides: Vec<SharedString>,
 1337}
 1338
 1339fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1340    if debounce_ms > 0 {
 1341        Some(Duration::from_millis(debounce_ms))
 1342    } else {
 1343        None
 1344    }
 1345}
 1346
 1347#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1348enum NextScrollCursorCenterTopBottom {
 1349    #[default]
 1350    Center,
 1351    Top,
 1352    Bottom,
 1353}
 1354
 1355impl NextScrollCursorCenterTopBottom {
 1356    fn next(&self) -> Self {
 1357        match self {
 1358            Self::Center => Self::Top,
 1359            Self::Top => Self::Bottom,
 1360            Self::Bottom => Self::Center,
 1361        }
 1362    }
 1363}
 1364
 1365#[derive(Clone)]
 1366pub struct EditorSnapshot {
 1367    pub mode: EditorMode,
 1368    show_gutter: bool,
 1369    offset_content: bool,
 1370    show_line_numbers: Option<bool>,
 1371    number_deleted_lines: bool,
 1372    show_git_diff_gutter: Option<bool>,
 1373    show_code_actions: Option<bool>,
 1374    show_runnables: Option<bool>,
 1375    show_breakpoints: Option<bool>,
 1376    git_blame_gutter_max_author_length: Option<usize>,
 1377    pub display_snapshot: DisplaySnapshot,
 1378    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1379    is_focused: bool,
 1380    scroll_anchor: SharedScrollAnchor,
 1381    ongoing_scroll: OngoingScroll,
 1382    current_line_highlight: CurrentLineHighlight,
 1383    gutter_hovered: bool,
 1384    semantic_tokens_enabled: bool,
 1385}
 1386
 1387#[derive(Default, Debug, Clone, Copy)]
 1388pub struct GutterDimensions {
 1389    pub left_padding: Pixels,
 1390    pub right_padding: Pixels,
 1391    pub width: Pixels,
 1392    pub margin: Pixels,
 1393    pub git_blame_entries_width: Option<Pixels>,
 1394}
 1395
 1396impl GutterDimensions {
 1397    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1398        Self {
 1399            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1400            ..Default::default()
 1401        }
 1402    }
 1403
 1404    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1405        -cx.text_system().descent(font_id, font_size)
 1406    }
 1407    /// The full width of the space taken up by the gutter.
 1408    pub fn full_width(&self) -> Pixels {
 1409        self.margin + self.width
 1410    }
 1411
 1412    /// The width of the space reserved for the fold indicators,
 1413    /// use alongside 'justify_end' and `gutter_width` to
 1414    /// right align content with the line numbers
 1415    pub fn fold_area_width(&self) -> Pixels {
 1416        self.margin + self.right_padding
 1417    }
 1418}
 1419
 1420struct CharacterDimensions {
 1421    em_width: Pixels,
 1422    em_advance: Pixels,
 1423    line_height: Pixels,
 1424}
 1425
 1426#[derive(Debug)]
 1427pub struct RemoteSelection {
 1428    pub replica_id: ReplicaId,
 1429    pub selection: Selection<Anchor>,
 1430    pub cursor_shape: CursorShape,
 1431    pub collaborator_id: CollaboratorId,
 1432    pub line_mode: bool,
 1433    pub user_name: Option<SharedString>,
 1434    pub color: PlayerColor,
 1435}
 1436
 1437#[derive(Clone, Debug)]
 1438struct SelectionHistoryEntry {
 1439    selections: Arc<[Selection<Anchor>]>,
 1440    select_next_state: Option<SelectNextState>,
 1441    select_prev_state: Option<SelectNextState>,
 1442    add_selections_state: Option<AddSelectionsState>,
 1443}
 1444
 1445#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1446enum SelectionHistoryMode {
 1447    #[default]
 1448    Normal,
 1449    Undoing,
 1450    Redoing,
 1451    Skipping,
 1452}
 1453
 1454#[derive(Clone, PartialEq, Eq, Hash)]
 1455struct HoveredCursor {
 1456    replica_id: ReplicaId,
 1457    selection_id: usize,
 1458}
 1459
 1460#[derive(Debug)]
 1461/// SelectionEffects controls the side-effects of updating the selection.
 1462///
 1463/// The default behaviour does "what you mostly want":
 1464/// - it pushes to the nav history if the cursor moved by >10 lines
 1465/// - it re-triggers completion requests
 1466/// - it scrolls to fit
 1467///
 1468/// You might want to modify these behaviours. For example when doing a "jump"
 1469/// like go to definition, we always want to add to nav history; but when scrolling
 1470/// in vim mode we never do.
 1471///
 1472/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1473/// move.
 1474#[derive(Clone)]
 1475pub struct SelectionEffects {
 1476    nav_history: Option<bool>,
 1477    completions: bool,
 1478    scroll: Option<Autoscroll>,
 1479}
 1480
 1481impl Default for SelectionEffects {
 1482    fn default() -> Self {
 1483        Self {
 1484            nav_history: None,
 1485            completions: true,
 1486            scroll: Some(Autoscroll::fit()),
 1487        }
 1488    }
 1489}
 1490impl SelectionEffects {
 1491    pub fn scroll(scroll: Autoscroll) -> Self {
 1492        Self {
 1493            scroll: Some(scroll),
 1494            ..Default::default()
 1495        }
 1496    }
 1497
 1498    pub fn no_scroll() -> Self {
 1499        Self {
 1500            scroll: None,
 1501            ..Default::default()
 1502        }
 1503    }
 1504
 1505    pub fn completions(self, completions: bool) -> Self {
 1506        Self {
 1507            completions,
 1508            ..self
 1509        }
 1510    }
 1511
 1512    pub fn nav_history(self, nav_history: bool) -> Self {
 1513        Self {
 1514            nav_history: Some(nav_history),
 1515            ..self
 1516        }
 1517    }
 1518}
 1519
 1520struct DeferredSelectionEffectsState {
 1521    changed: bool,
 1522    effects: SelectionEffects,
 1523    old_cursor_position: Anchor,
 1524    history_entry: SelectionHistoryEntry,
 1525}
 1526
 1527#[derive(Default)]
 1528struct SelectionHistory {
 1529    #[allow(clippy::type_complexity)]
 1530    selections_by_transaction:
 1531        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1532    mode: SelectionHistoryMode,
 1533    undo_stack: VecDeque<SelectionHistoryEntry>,
 1534    redo_stack: VecDeque<SelectionHistoryEntry>,
 1535}
 1536
 1537impl SelectionHistory {
 1538    #[track_caller]
 1539    fn insert_transaction(
 1540        &mut self,
 1541        transaction_id: TransactionId,
 1542        selections: Arc<[Selection<Anchor>]>,
 1543    ) {
 1544        if selections.is_empty() {
 1545            log::error!(
 1546                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1547                std::panic::Location::caller()
 1548            );
 1549            return;
 1550        }
 1551        self.selections_by_transaction
 1552            .insert(transaction_id, (selections, None));
 1553    }
 1554
 1555    #[allow(clippy::type_complexity)]
 1556    fn transaction(
 1557        &self,
 1558        transaction_id: TransactionId,
 1559    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1560        self.selections_by_transaction.get(&transaction_id)
 1561    }
 1562
 1563    #[allow(clippy::type_complexity)]
 1564    fn transaction_mut(
 1565        &mut self,
 1566        transaction_id: TransactionId,
 1567    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1568        self.selections_by_transaction.get_mut(&transaction_id)
 1569    }
 1570
 1571    fn push(&mut self, entry: SelectionHistoryEntry) {
 1572        if !entry.selections.is_empty() {
 1573            match self.mode {
 1574                SelectionHistoryMode::Normal => {
 1575                    self.push_undo(entry);
 1576                    self.redo_stack.clear();
 1577                }
 1578                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1579                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1580                SelectionHistoryMode::Skipping => {}
 1581            }
 1582        }
 1583    }
 1584
 1585    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1586        if self
 1587            .undo_stack
 1588            .back()
 1589            .is_none_or(|e| e.selections != entry.selections)
 1590        {
 1591            self.undo_stack.push_back(entry);
 1592            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1593                self.undo_stack.pop_front();
 1594            }
 1595        }
 1596    }
 1597
 1598    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1599        if self
 1600            .redo_stack
 1601            .back()
 1602            .is_none_or(|e| e.selections != entry.selections)
 1603        {
 1604            self.redo_stack.push_back(entry);
 1605            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1606                self.redo_stack.pop_front();
 1607            }
 1608        }
 1609    }
 1610}
 1611
 1612#[derive(Clone, Copy)]
 1613pub struct RowHighlightOptions {
 1614    pub autoscroll: bool,
 1615    pub include_gutter: bool,
 1616}
 1617
 1618impl Default for RowHighlightOptions {
 1619    fn default() -> Self {
 1620        Self {
 1621            autoscroll: Default::default(),
 1622            include_gutter: true,
 1623        }
 1624    }
 1625}
 1626
 1627struct RowHighlight {
 1628    index: usize,
 1629    range: Range<Anchor>,
 1630    color: Hsla,
 1631    options: RowHighlightOptions,
 1632    type_id: TypeId,
 1633}
 1634
 1635#[derive(Clone, Debug)]
 1636struct AddSelectionsState {
 1637    groups: Vec<AddSelectionsGroup>,
 1638}
 1639
 1640#[derive(Clone, Debug)]
 1641struct AddSelectionsGroup {
 1642    above: bool,
 1643    stack: Vec<usize>,
 1644}
 1645
 1646#[derive(Clone)]
 1647struct SelectNextState {
 1648    query: AhoCorasick,
 1649    wordwise: bool,
 1650    done: bool,
 1651}
 1652
 1653impl std::fmt::Debug for SelectNextState {
 1654    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1655        f.debug_struct(std::any::type_name::<Self>())
 1656            .field("wordwise", &self.wordwise)
 1657            .field("done", &self.done)
 1658            .finish()
 1659    }
 1660}
 1661
 1662#[derive(Debug)]
 1663struct AutocloseRegion {
 1664    selection_id: usize,
 1665    range: Range<Anchor>,
 1666    pair: BracketPair,
 1667}
 1668
 1669#[derive(Debug)]
 1670struct SnippetState {
 1671    ranges: Vec<Vec<Range<Anchor>>>,
 1672    active_index: usize,
 1673    choices: Vec<Option<Vec<String>>>,
 1674}
 1675
 1676#[doc(hidden)]
 1677pub struct RenameState {
 1678    pub range: Range<Anchor>,
 1679    pub old_name: Arc<str>,
 1680    pub editor: Entity<Editor>,
 1681    block_id: CustomBlockId,
 1682}
 1683
 1684struct InvalidationStack<T>(Vec<T>);
 1685
 1686struct RegisteredEditPredictionDelegate {
 1687    provider: Arc<dyn EditPredictionDelegateHandle>,
 1688    _subscription: Subscription,
 1689}
 1690
 1691#[derive(Debug, PartialEq, Eq)]
 1692pub struct ActiveDiagnosticGroup {
 1693    pub active_range: Range<Anchor>,
 1694    pub active_message: String,
 1695    pub group_id: usize,
 1696    pub blocks: HashSet<CustomBlockId>,
 1697}
 1698
 1699#[derive(Debug, PartialEq, Eq)]
 1700
 1701pub(crate) enum ActiveDiagnostic {
 1702    None,
 1703    All,
 1704    Group(ActiveDiagnosticGroup),
 1705}
 1706
 1707#[derive(Serialize, Deserialize, Clone, Debug)]
 1708pub struct ClipboardSelection {
 1709    /// The number of bytes in this selection.
 1710    pub len: usize,
 1711    /// Whether this was a full-line selection.
 1712    pub is_entire_line: bool,
 1713    /// The indentation of the first line when this content was originally copied.
 1714    pub first_line_indent: u32,
 1715    #[serde(default)]
 1716    pub file_path: Option<PathBuf>,
 1717    #[serde(default)]
 1718    pub line_range: Option<RangeInclusive<u32>>,
 1719}
 1720
 1721impl ClipboardSelection {
 1722    pub fn for_buffer(
 1723        len: usize,
 1724        is_entire_line: bool,
 1725        range: Range<Point>,
 1726        buffer: &MultiBufferSnapshot,
 1727        project: Option<&Entity<Project>>,
 1728        cx: &App,
 1729    ) -> Self {
 1730        let first_line_indent = buffer
 1731            .indent_size_for_line(MultiBufferRow(range.start.row))
 1732            .len;
 1733
 1734        let file_path = util::maybe!({
 1735            let project = project?.read(cx);
 1736            let file = buffer.file_at(range.start)?;
 1737            let project_path = ProjectPath {
 1738                worktree_id: file.worktree_id(cx),
 1739                path: file.path().clone(),
 1740            };
 1741            project.absolute_path(&project_path, cx)
 1742        });
 1743
 1744        let line_range = file_path.as_ref().and_then(|_| {
 1745            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1746            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1747            if start_excerpt_id == end_excerpt_id {
 1748                Some(start_point.row..=end_point.row)
 1749            } else {
 1750                None
 1751            }
 1752        });
 1753
 1754        Self {
 1755            len,
 1756            is_entire_line,
 1757            first_line_indent,
 1758            file_path,
 1759            line_range,
 1760        }
 1761    }
 1762}
 1763
 1764// selections, scroll behavior, was newest selection reversed
 1765type SelectSyntaxNodeHistoryState = (
 1766    Box<[Selection<Anchor>]>,
 1767    SelectSyntaxNodeScrollBehavior,
 1768    bool,
 1769);
 1770
 1771#[derive(Default)]
 1772struct SelectSyntaxNodeHistory {
 1773    stack: Vec<SelectSyntaxNodeHistoryState>,
 1774    // disable temporarily to allow changing selections without losing the stack
 1775    pub disable_clearing: bool,
 1776}
 1777
 1778impl SelectSyntaxNodeHistory {
 1779    pub fn try_clear(&mut self) {
 1780        if !self.disable_clearing {
 1781            self.stack.clear();
 1782        }
 1783    }
 1784
 1785    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1786        self.stack.push(selection);
 1787    }
 1788
 1789    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1790        self.stack.pop()
 1791    }
 1792}
 1793
 1794enum SelectSyntaxNodeScrollBehavior {
 1795    CursorTop,
 1796    FitSelection,
 1797    CursorBottom,
 1798}
 1799
 1800#[derive(Debug, Clone, Copy)]
 1801pub(crate) struct NavigationData {
 1802    cursor_anchor: Anchor,
 1803    cursor_position: Point,
 1804    scroll_anchor: ScrollAnchor,
 1805    scroll_top_row: u32,
 1806}
 1807
 1808#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1809pub enum GotoDefinitionKind {
 1810    Symbol,
 1811    Declaration,
 1812    Type,
 1813    Implementation,
 1814}
 1815
 1816pub enum FormatTarget {
 1817    Buffers(HashSet<Entity<Buffer>>),
 1818    Ranges(Vec<Range<MultiBufferPoint>>),
 1819}
 1820
 1821pub(crate) struct FocusedBlock {
 1822    id: BlockId,
 1823    focus_handle: WeakFocusHandle,
 1824}
 1825
 1826#[derive(Clone, Debug)]
 1827pub enum JumpData {
 1828    MultiBufferRow {
 1829        row: MultiBufferRow,
 1830        line_offset_from_top: u32,
 1831    },
 1832    MultiBufferPoint {
 1833        excerpt_id: ExcerptId,
 1834        position: Point,
 1835        anchor: text::Anchor,
 1836        line_offset_from_top: u32,
 1837    },
 1838}
 1839
 1840pub enum MultibufferSelectionMode {
 1841    First,
 1842    All,
 1843}
 1844
 1845#[derive(Clone, Copy, Debug, Default)]
 1846pub struct RewrapOptions {
 1847    pub override_language_settings: bool,
 1848    pub preserve_existing_whitespace: bool,
 1849}
 1850
 1851impl Editor {
 1852    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1853        let buffer = cx.new(|cx| Buffer::local("", cx));
 1854        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1855        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1856    }
 1857
 1858    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1859        let buffer = cx.new(|cx| Buffer::local("", cx));
 1860        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1861        Self::new(EditorMode::full(), buffer, None, window, cx)
 1862    }
 1863
 1864    pub fn auto_height(
 1865        min_lines: usize,
 1866        max_lines: usize,
 1867        window: &mut Window,
 1868        cx: &mut Context<Self>,
 1869    ) -> Self {
 1870        let buffer = cx.new(|cx| Buffer::local("", cx));
 1871        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1872        Self::new(
 1873            EditorMode::AutoHeight {
 1874                min_lines,
 1875                max_lines: Some(max_lines),
 1876            },
 1877            buffer,
 1878            None,
 1879            window,
 1880            cx,
 1881        )
 1882    }
 1883
 1884    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1885    /// The editor grows as tall as needed to fit its content.
 1886    pub fn auto_height_unbounded(
 1887        min_lines: usize,
 1888        window: &mut Window,
 1889        cx: &mut Context<Self>,
 1890    ) -> Self {
 1891        let buffer = cx.new(|cx| Buffer::local("", cx));
 1892        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1893        Self::new(
 1894            EditorMode::AutoHeight {
 1895                min_lines,
 1896                max_lines: None,
 1897            },
 1898            buffer,
 1899            None,
 1900            window,
 1901            cx,
 1902        )
 1903    }
 1904
 1905    pub fn for_buffer(
 1906        buffer: Entity<Buffer>,
 1907        project: Option<Entity<Project>>,
 1908        window: &mut Window,
 1909        cx: &mut Context<Self>,
 1910    ) -> Self {
 1911        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1912        Self::new(EditorMode::full(), buffer, project, window, cx)
 1913    }
 1914
 1915    pub fn for_multibuffer(
 1916        buffer: Entity<MultiBuffer>,
 1917        project: Option<Entity<Project>>,
 1918        window: &mut Window,
 1919        cx: &mut Context<Self>,
 1920    ) -> Self {
 1921        Self::new(EditorMode::full(), buffer, project, window, cx)
 1922    }
 1923
 1924    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1925        let mut clone = Self::new(
 1926            self.mode.clone(),
 1927            self.buffer.clone(),
 1928            self.project.clone(),
 1929            window,
 1930            cx,
 1931        );
 1932        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1933            let snapshot = display_map.snapshot(cx);
 1934            clone.display_map.update(cx, |display_map, cx| {
 1935                display_map.set_state(&snapshot, cx);
 1936            });
 1937            snapshot
 1938        });
 1939        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1940        clone.folds_did_change(cx);
 1941        clone.selections.clone_state(&self.selections);
 1942        clone
 1943            .scroll_manager
 1944            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1945        clone.searchable = self.searchable;
 1946        clone.read_only = self.read_only;
 1947        clone.buffers_with_disabled_indent_guides =
 1948            self.buffers_with_disabled_indent_guides.clone();
 1949        clone
 1950    }
 1951
 1952    pub fn new(
 1953        mode: EditorMode,
 1954        buffer: Entity<MultiBuffer>,
 1955        project: Option<Entity<Project>>,
 1956        window: &mut Window,
 1957        cx: &mut Context<Self>,
 1958    ) -> Self {
 1959        Editor::new_internal(mode, buffer, project, None, window, cx)
 1960    }
 1961
 1962    pub fn refresh_sticky_headers(
 1963        &mut self,
 1964        display_snapshot: &DisplaySnapshot,
 1965        cx: &mut Context<Editor>,
 1966    ) {
 1967        if !self.mode.is_full() {
 1968            return;
 1969        }
 1970        let multi_buffer = display_snapshot.buffer_snapshot();
 1971        let scroll_anchor = self
 1972            .scroll_manager
 1973            .native_anchor(display_snapshot, cx)
 1974            .anchor;
 1975        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 1976            return;
 1977        };
 1978        let buffer = buffer.clone();
 1979
 1980        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 1981        let max_row = buffer.max_point().row;
 1982        let start_row = buffer_visible_start.row.min(max_row);
 1983        let end_row = (buffer_visible_start.row + 10).min(max_row);
 1984
 1985        let syntax = self.style(cx).syntax.clone();
 1986        let background_task = cx.background_spawn(async move {
 1987            buffer
 1988                .outline_items_containing(
 1989                    Point::new(start_row, 0)..Point::new(end_row, 0),
 1990                    true,
 1991                    Some(syntax.as_ref()),
 1992                )
 1993                .into_iter()
 1994                .map(|outline_item| OutlineItem {
 1995                    depth: outline_item.depth,
 1996                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 1997                    source_range_for_text: Anchor::range_in_buffer(
 1998                        excerpt_id,
 1999                        outline_item.source_range_for_text,
 2000                    ),
 2001                    text: outline_item.text,
 2002                    highlight_ranges: outline_item.highlight_ranges,
 2003                    name_ranges: outline_item.name_ranges,
 2004                    body_range: outline_item
 2005                        .body_range
 2006                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2007                    annotation_range: outline_item
 2008                        .annotation_range
 2009                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2010                })
 2011                .collect()
 2012        });
 2013        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2014            let sticky_headers = background_task.await;
 2015            this.update(cx, |this, cx| {
 2016                this.sticky_headers = Some(sticky_headers);
 2017                cx.notify();
 2018            })
 2019            .ok();
 2020        });
 2021    }
 2022
 2023    fn new_internal(
 2024        mode: EditorMode,
 2025        multi_buffer: Entity<MultiBuffer>,
 2026        project: Option<Entity<Project>>,
 2027        display_map: Option<Entity<DisplayMap>>,
 2028        window: &mut Window,
 2029        cx: &mut Context<Self>,
 2030    ) -> Self {
 2031        debug_assert!(
 2032            display_map.is_none() || mode.is_minimap(),
 2033            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2034        );
 2035
 2036        let full_mode = mode.is_full();
 2037        let is_minimap = mode.is_minimap();
 2038        let diagnostics_max_severity = if full_mode {
 2039            EditorSettings::get_global(cx)
 2040                .diagnostics_max_severity
 2041                .unwrap_or(DiagnosticSeverity::Hint)
 2042        } else {
 2043            DiagnosticSeverity::Off
 2044        };
 2045        let style = window.text_style();
 2046        let font_size = style.font_size.to_pixels(window.rem_size());
 2047        let editor = cx.entity().downgrade();
 2048        let fold_placeholder = FoldPlaceholder {
 2049            constrain_width: false,
 2050            render: Arc::new(move |fold_id, fold_range, cx| {
 2051                let editor = editor.clone();
 2052                FoldPlaceholder::fold_element(fold_id, cx)
 2053                    .cursor_pointer()
 2054                    .child("")
 2055                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2056                    .on_click(move |_, _window, cx| {
 2057                        editor
 2058                            .update(cx, |editor, cx| {
 2059                                editor.unfold_ranges(
 2060                                    &[fold_range.start..fold_range.end],
 2061                                    true,
 2062                                    false,
 2063                                    cx,
 2064                                );
 2065                                cx.stop_propagation();
 2066                            })
 2067                            .ok();
 2068                    })
 2069                    .into_any()
 2070            }),
 2071            merge_adjacent: true,
 2072            ..FoldPlaceholder::default()
 2073        };
 2074        let display_map = display_map.unwrap_or_else(|| {
 2075            cx.new(|cx| {
 2076                DisplayMap::new(
 2077                    multi_buffer.clone(),
 2078                    style.font(),
 2079                    font_size,
 2080                    None,
 2081                    FILE_HEADER_HEIGHT,
 2082                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2083                    fold_placeholder,
 2084                    diagnostics_max_severity,
 2085                    cx,
 2086                )
 2087            })
 2088        });
 2089
 2090        let selections = SelectionsCollection::new();
 2091
 2092        let blink_manager = cx.new(|cx| {
 2093            let mut blink_manager = BlinkManager::new(
 2094                CURSOR_BLINK_INTERVAL,
 2095                |cx| EditorSettings::get_global(cx).cursor_blink,
 2096                cx,
 2097            );
 2098            if is_minimap {
 2099                blink_manager.disable(cx);
 2100            }
 2101            blink_manager
 2102        });
 2103
 2104        let soft_wrap_mode_override =
 2105            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2106
 2107        let mut project_subscriptions = Vec::new();
 2108        if full_mode && let Some(project) = project.as_ref() {
 2109            project_subscriptions.push(cx.subscribe_in(
 2110                project,
 2111                window,
 2112                |editor, _, event, window, cx| match event {
 2113                    project::Event::RefreshCodeLens => {
 2114                        // we always query lens with actions, without storing them, always refreshing them
 2115                    }
 2116                    project::Event::RefreshInlayHints {
 2117                        server_id,
 2118                        request_id,
 2119                    } => {
 2120                        editor.refresh_inlay_hints(
 2121                            InlayHintRefreshReason::RefreshRequested {
 2122                                server_id: *server_id,
 2123                                request_id: *request_id,
 2124                            },
 2125                            cx,
 2126                        );
 2127                    }
 2128                    project::Event::RefreshSemanticTokens {
 2129                        server_id,
 2130                        request_id,
 2131                    } => {
 2132                        editor.refresh_semantic_tokens(
 2133                            None,
 2134                            Some(RefreshForServer {
 2135                                server_id: *server_id,
 2136                                request_id: *request_id,
 2137                            }),
 2138                            cx,
 2139                        );
 2140                    }
 2141                    project::Event::LanguageServerRemoved(_) => {
 2142                        editor.registered_buffers.clear();
 2143                        editor.register_visible_buffers(cx);
 2144                        editor.invalidate_semantic_tokens(None);
 2145                        editor.refresh_runnables(window, cx);
 2146                        editor.update_lsp_data(None, window, cx);
 2147                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2148                    }
 2149                    project::Event::SnippetEdit(id, snippet_edits) => {
 2150                        // todo(lw): Non singletons
 2151                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2152                            let snapshot = buffer.read(cx).snapshot();
 2153                            let focus_handle = editor.focus_handle(cx);
 2154                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2155                                for (range, snippet) in snippet_edits {
 2156                                    let buffer_range =
 2157                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2158                                    editor
 2159                                        .insert_snippet(
 2160                                            &[MultiBufferOffset(buffer_range.start)
 2161                                                ..MultiBufferOffset(buffer_range.end)],
 2162                                            snippet.clone(),
 2163                                            window,
 2164                                            cx,
 2165                                        )
 2166                                        .ok();
 2167                                }
 2168                            }
 2169                        }
 2170                    }
 2171                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2172                        let buffer_id = *buffer_id;
 2173                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2174                            editor.register_buffer(buffer_id, cx);
 2175                            editor.refresh_runnables(window, cx);
 2176                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2177                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2178                            refresh_linked_ranges(editor, window, cx);
 2179                            editor.refresh_code_actions(window, cx);
 2180                            editor.refresh_document_highlights(cx);
 2181                        }
 2182                    }
 2183
 2184                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2185                        let Some(workspace) = editor.workspace() else {
 2186                            return;
 2187                        };
 2188                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2189                        else {
 2190                            return;
 2191                        };
 2192
 2193                        if active_editor.entity_id() == cx.entity_id() {
 2194                            let entity_id = cx.entity_id();
 2195                            workspace.update(cx, |this, cx| {
 2196                                this.panes_mut()
 2197                                    .iter_mut()
 2198                                    .filter(|pane| pane.entity_id() != entity_id)
 2199                                    .for_each(|p| {
 2200                                        p.update(cx, |pane, _| {
 2201                                            pane.nav_history_mut().rename_item(
 2202                                                entity_id,
 2203                                                project_path.clone(),
 2204                                                abs_path.clone().into(),
 2205                                            );
 2206                                        })
 2207                                    });
 2208                            });
 2209
 2210                            Self::open_transaction_for_hidden_buffers(
 2211                                workspace,
 2212                                transaction.clone(),
 2213                                "Rename".to_string(),
 2214                                window,
 2215                                cx,
 2216                            );
 2217                        }
 2218                    }
 2219
 2220                    project::Event::WorkspaceEditApplied(transaction) => {
 2221                        let Some(workspace) = editor.workspace() else {
 2222                            return;
 2223                        };
 2224                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2225                        else {
 2226                            return;
 2227                        };
 2228
 2229                        if active_editor.entity_id() == cx.entity_id() {
 2230                            Self::open_transaction_for_hidden_buffers(
 2231                                workspace,
 2232                                transaction.clone(),
 2233                                "LSP Edit".to_string(),
 2234                                window,
 2235                                cx,
 2236                            );
 2237                        }
 2238                    }
 2239
 2240                    _ => {}
 2241                },
 2242            ));
 2243            if let Some(task_inventory) = project
 2244                .read(cx)
 2245                .task_store()
 2246                .read(cx)
 2247                .task_inventory()
 2248                .cloned()
 2249            {
 2250                project_subscriptions.push(cx.observe_in(
 2251                    &task_inventory,
 2252                    window,
 2253                    |editor, _, window, cx| {
 2254                        editor.refresh_runnables(window, cx);
 2255                    },
 2256                ));
 2257            };
 2258
 2259            project_subscriptions.push(cx.subscribe_in(
 2260                &project.read(cx).breakpoint_store(),
 2261                window,
 2262                |editor, _, event, window, cx| match event {
 2263                    BreakpointStoreEvent::ClearDebugLines => {
 2264                        editor.clear_row_highlights::<ActiveDebugLine>();
 2265                        editor.refresh_inline_values(cx);
 2266                    }
 2267                    BreakpointStoreEvent::SetDebugLine => {
 2268                        if editor.go_to_active_debug_line(window, cx) {
 2269                            cx.stop_propagation();
 2270                        }
 2271
 2272                        editor.refresh_inline_values(cx);
 2273                    }
 2274                    _ => {}
 2275                },
 2276            ));
 2277            let git_store = project.read(cx).git_store().clone();
 2278            let project = project.clone();
 2279            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2280                if let GitStoreEvent::RepositoryAdded = event {
 2281                    this.load_diff_task = Some(
 2282                        update_uncommitted_diff_for_buffer(
 2283                            cx.entity(),
 2284                            &project,
 2285                            this.buffer.read(cx).all_buffers(),
 2286                            this.buffer.clone(),
 2287                            cx,
 2288                        )
 2289                        .shared(),
 2290                    );
 2291                }
 2292            }));
 2293        }
 2294
 2295        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2296
 2297        let inlay_hint_settings =
 2298            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2299        let focus_handle = cx.focus_handle();
 2300        if !is_minimap {
 2301            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2302                .detach();
 2303            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2304                .detach();
 2305            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2306                .detach();
 2307            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2308                .detach();
 2309            cx.observe_pending_input(window, Self::observe_pending_input)
 2310                .detach();
 2311        }
 2312
 2313        let show_indent_guides =
 2314            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2315                Some(false)
 2316            } else {
 2317                None
 2318            };
 2319
 2320        let breakpoint_store = match (&mode, project.as_ref()) {
 2321            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2322            _ => None,
 2323        };
 2324
 2325        let mut code_action_providers = Vec::new();
 2326        let mut load_uncommitted_diff = None;
 2327        if let Some(project) = project.clone() {
 2328            load_uncommitted_diff = Some(
 2329                update_uncommitted_diff_for_buffer(
 2330                    cx.entity(),
 2331                    &project,
 2332                    multi_buffer.read(cx).all_buffers(),
 2333                    multi_buffer.clone(),
 2334                    cx,
 2335                )
 2336                .shared(),
 2337            );
 2338            code_action_providers.push(Rc::new(project) as Rc<_>);
 2339        }
 2340
 2341        let mut editor = Self {
 2342            focus_handle,
 2343            show_cursor_when_unfocused: false,
 2344            last_focused_descendant: None,
 2345            buffer: multi_buffer.clone(),
 2346            display_map: display_map.clone(),
 2347            placeholder_display_map: None,
 2348            selections,
 2349            scroll_manager: ScrollManager::new(cx),
 2350            columnar_selection_state: None,
 2351            add_selections_state: None,
 2352            select_next_state: None,
 2353            select_prev_state: None,
 2354            selection_history: SelectionHistory::default(),
 2355            defer_selection_effects: false,
 2356            deferred_selection_effects_state: None,
 2357            autoclose_regions: Vec::new(),
 2358            snippet_stack: InvalidationStack::default(),
 2359            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2360            ime_transaction: None,
 2361            active_diagnostics: ActiveDiagnostic::None,
 2362            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2363            inline_diagnostics_update: Task::ready(()),
 2364            inline_diagnostics: Vec::new(),
 2365            soft_wrap_mode_override,
 2366            diagnostics_max_severity,
 2367            hard_wrap: None,
 2368            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2369            semantics_provider: project
 2370                .as_ref()
 2371                .map(|project| Rc::new(project.downgrade()) as _),
 2372            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2373            project,
 2374            blink_manager: blink_manager.clone(),
 2375            show_local_selections: true,
 2376            show_scrollbars: ScrollbarAxes {
 2377                horizontal: full_mode,
 2378                vertical: full_mode,
 2379            },
 2380            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2381            offset_content: !matches!(mode, EditorMode::SingleLine),
 2382            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2383            show_gutter: full_mode,
 2384            show_line_numbers: (!full_mode).then_some(false),
 2385            use_relative_line_numbers: None,
 2386            disable_expand_excerpt_buttons: !full_mode,
 2387            delegate_expand_excerpts: false,
 2388            delegate_stage_and_restore: false,
 2389            delegate_open_excerpts: false,
 2390            enable_lsp_data: true,
 2391            enable_runnables: true,
 2392            show_git_diff_gutter: None,
 2393            show_code_actions: None,
 2394            show_runnables: None,
 2395            show_breakpoints: None,
 2396            show_diff_review_button: false,
 2397            show_wrap_guides: None,
 2398            show_indent_guides,
 2399            buffers_with_disabled_indent_guides: HashSet::default(),
 2400            highlight_order: 0,
 2401            highlighted_rows: HashMap::default(),
 2402            background_highlights: HashMap::default(),
 2403            gutter_highlights: HashMap::default(),
 2404            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2405            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2406            nav_history: None,
 2407            context_menu: RefCell::new(None),
 2408            context_menu_options: None,
 2409            mouse_context_menu: None,
 2410            completion_tasks: Vec::new(),
 2411            inline_blame_popover: None,
 2412            inline_blame_popover_show_task: None,
 2413            signature_help_state: SignatureHelpState::default(),
 2414            auto_signature_help: None,
 2415            find_all_references_task_sources: Vec::new(),
 2416            next_completion_id: 0,
 2417            next_inlay_id: 0,
 2418            code_action_providers,
 2419            available_code_actions: None,
 2420            code_actions_task: None,
 2421            quick_selection_highlight_task: None,
 2422            debounced_selection_highlight_task: None,
 2423            debounced_selection_highlight_complete: false,
 2424            document_highlights_task: None,
 2425            linked_editing_range_task: None,
 2426            pending_rename: None,
 2427            searchable: !is_minimap,
 2428            cursor_shape: EditorSettings::get_global(cx)
 2429                .cursor_shape
 2430                .unwrap_or_default(),
 2431            cursor_offset_on_selection: false,
 2432            current_line_highlight: None,
 2433            autoindent_mode: Some(AutoindentMode::EachLine),
 2434            collapse_matches: false,
 2435            workspace: None,
 2436            input_enabled: !is_minimap,
 2437            expects_character_input: !is_minimap,
 2438            use_modal_editing: full_mode,
 2439            read_only: is_minimap,
 2440            use_autoclose: true,
 2441            use_auto_surround: true,
 2442            auto_replace_emoji_shortcode: false,
 2443            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2444            leader_id: None,
 2445            remote_id: None,
 2446            hover_state: HoverState::default(),
 2447            pending_mouse_down: None,
 2448            prev_pressure_stage: None,
 2449            hovered_link_state: None,
 2450            edit_prediction_provider: None,
 2451            active_edit_prediction: None,
 2452            stale_edit_prediction_in_menu: None,
 2453            edit_prediction_preview: EditPredictionPreview::Inactive {
 2454                released_too_fast: false,
 2455            },
 2456            inline_diagnostics_enabled: full_mode,
 2457            diagnostics_enabled: full_mode,
 2458            word_completions_enabled: full_mode,
 2459            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2460            gutter_hovered: false,
 2461            pixel_position_of_newest_cursor: None,
 2462            last_bounds: None,
 2463            last_position_map: None,
 2464            expect_bounds_change: None,
 2465            gutter_dimensions: GutterDimensions::default(),
 2466            style: None,
 2467            show_cursor_names: false,
 2468            hovered_cursors: HashMap::default(),
 2469            next_editor_action_id: EditorActionId::default(),
 2470            editor_actions: Rc::default(),
 2471            edit_predictions_hidden_for_vim_mode: false,
 2472            show_edit_predictions_override: None,
 2473            show_completions_on_input_override: None,
 2474            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2475            edit_prediction_settings: EditPredictionSettings::Disabled,
 2476            edit_prediction_indent_conflict: false,
 2477            edit_prediction_requires_modifier_in_indent_conflict: true,
 2478            custom_context_menu: None,
 2479            show_git_blame_gutter: false,
 2480            show_git_blame_inline: false,
 2481            show_selection_menu: None,
 2482            show_git_blame_inline_delay_task: None,
 2483            git_blame_inline_enabled: full_mode
 2484                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2485            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2486            buffer_serialization: is_minimap.not().then(|| {
 2487                BufferSerialization::new(
 2488                    ProjectSettings::get_global(cx)
 2489                        .session
 2490                        .restore_unsaved_buffers,
 2491                )
 2492            }),
 2493            blame: None,
 2494            blame_subscription: None,
 2495
 2496            breakpoint_store,
 2497            gutter_breakpoint_indicator: (None, None),
 2498            gutter_diff_review_indicator: (None, None),
 2499            diff_review_drag_state: None,
 2500            diff_review_overlays: Vec::new(),
 2501            stored_review_comments: Vec::new(),
 2502            next_review_comment_id: 0,
 2503            hovered_diff_hunk_row: None,
 2504            _subscriptions: (!is_minimap)
 2505                .then(|| {
 2506                    vec![
 2507                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2508                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2509                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2510                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2511                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2512                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2513                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2514                        cx.observe_window_activation(window, |editor, window, cx| {
 2515                            let active = window.is_window_active();
 2516                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2517                                if active {
 2518                                    blink_manager.enable(cx);
 2519                                } else {
 2520                                    blink_manager.disable(cx);
 2521                                }
 2522                            });
 2523                            if active {
 2524                                editor.show_mouse_cursor(cx);
 2525                            }
 2526                        }),
 2527                    ]
 2528                })
 2529                .unwrap_or_default(),
 2530            runnables: RunnableData::new(),
 2531            pull_diagnostics_task: Task::ready(()),
 2532            colors: None,
 2533            refresh_colors_task: Task::ready(()),
 2534            use_document_folding_ranges: false,
 2535            refresh_folding_ranges_task: Task::ready(()),
 2536            inlay_hints: None,
 2537            next_color_inlay_id: 0,
 2538            post_scroll_update: Task::ready(()),
 2539            linked_edit_ranges: Default::default(),
 2540            in_project_search: false,
 2541            previous_search_ranges: None,
 2542            breadcrumb_header: None,
 2543            focused_block: None,
 2544            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2545            addons: HashMap::default(),
 2546            registered_buffers: HashMap::default(),
 2547            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2548            selection_mark_mode: false,
 2549            toggle_fold_multiple_buffers: Task::ready(()),
 2550            serialize_selections: Task::ready(()),
 2551            serialize_folds: Task::ready(()),
 2552            text_style_refinement: None,
 2553            load_diff_task: load_uncommitted_diff,
 2554            temporary_diff_override: false,
 2555            mouse_cursor_hidden: false,
 2556            minimap: None,
 2557            hide_mouse_mode: EditorSettings::get_global(cx)
 2558                .hide_mouse
 2559                .unwrap_or_default(),
 2560            change_list: ChangeList::new(),
 2561            mode,
 2562            selection_drag_state: SelectionDragState::None,
 2563            folding_newlines: Task::ready(()),
 2564            lookup_key: None,
 2565            select_next_is_case_sensitive: None,
 2566            on_local_selections_changed: None,
 2567            suppress_selection_callback: false,
 2568            applicable_language_settings: HashMap::default(),
 2569            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2570            accent_data: None,
 2571            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2572            number_deleted_lines: false,
 2573            refresh_matching_bracket_highlights_task: Task::ready(()),
 2574            refresh_document_symbols_task: Task::ready(()).shared(),
 2575            lsp_document_symbols: HashMap::default(),
 2576            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2577            outline_symbols_at_cursor: None,
 2578            sticky_headers_task: Task::ready(()),
 2579            sticky_headers: None,
 2580            colorize_brackets_task: Task::ready(()),
 2581        };
 2582
 2583        if is_minimap {
 2584            return editor;
 2585        }
 2586
 2587        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2588        editor.accent_data = editor.fetch_accent_data(cx);
 2589
 2590        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2591            editor
 2592                ._subscriptions
 2593                .push(cx.observe(breakpoints, |_, _, cx| {
 2594                    cx.notify();
 2595                }));
 2596        }
 2597        editor._subscriptions.extend(project_subscriptions);
 2598
 2599        editor._subscriptions.push(cx.subscribe_in(
 2600            &cx.entity(),
 2601            window,
 2602            |editor, _, e: &EditorEvent, window, cx| match e {
 2603                EditorEvent::ScrollPositionChanged { local, .. } => {
 2604                    if *local {
 2605                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2606                        editor.inline_blame_popover.take();
 2607                        let snapshot = editor.snapshot(window, cx);
 2608                        let new_anchor = editor
 2609                            .scroll_manager
 2610                            .native_anchor(&snapshot.display_snapshot, cx);
 2611                        editor.update_restoration_data(cx, move |data| {
 2612                            data.scroll_position = (
 2613                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2614                                new_anchor.offset,
 2615                            );
 2616                        });
 2617
 2618                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2619                            cx.background_executor()
 2620                                .timer(Duration::from_millis(50))
 2621                                .await;
 2622                            editor
 2623                                .update_in(cx, |editor, window, cx| {
 2624                                    editor.register_visible_buffers(cx);
 2625                                    editor.colorize_brackets(false, cx);
 2626                                    editor.refresh_inlay_hints(
 2627                                        InlayHintRefreshReason::NewLinesShown,
 2628                                        cx,
 2629                                    );
 2630                                    if !editor.buffer().read(cx).is_singleton() {
 2631                                        editor.update_lsp_data(None, window, cx);
 2632                                        editor.refresh_runnables(window, cx);
 2633                                    }
 2634                                })
 2635                                .ok();
 2636                        });
 2637                    }
 2638                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2639                }
 2640                EditorEvent::Edited { .. } => {
 2641                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2642                        .map(|vim_mode| vim_mode.0)
 2643                        .unwrap_or(false);
 2644                    if !vim_mode {
 2645                        let display_map = editor.display_snapshot(cx);
 2646                        let selections = editor.selections.all_adjusted_display(&display_map);
 2647                        let pop_state = editor
 2648                            .change_list
 2649                            .last()
 2650                            .map(|previous| {
 2651                                previous.len() == selections.len()
 2652                                    && previous.iter().enumerate().all(|(ix, p)| {
 2653                                        p.to_display_point(&display_map).row()
 2654                                            == selections[ix].head().row()
 2655                                    })
 2656                            })
 2657                            .unwrap_or(false);
 2658                        let new_positions = selections
 2659                            .into_iter()
 2660                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2661                            .collect();
 2662                        editor
 2663                            .change_list
 2664                            .push_to_change_list(pop_state, new_positions);
 2665                    }
 2666                }
 2667                _ => (),
 2668            },
 2669        ));
 2670
 2671        if let Some(dap_store) = editor
 2672            .project
 2673            .as_ref()
 2674            .map(|project| project.read(cx).dap_store())
 2675        {
 2676            let weak_editor = cx.weak_entity();
 2677
 2678            editor
 2679                ._subscriptions
 2680                .push(
 2681                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2682                        let session_entity = cx.entity();
 2683                        weak_editor
 2684                            .update(cx, |editor, cx| {
 2685                                editor._subscriptions.push(
 2686                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2687                                );
 2688                            })
 2689                            .ok();
 2690                    }),
 2691                );
 2692
 2693            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2694                editor
 2695                    ._subscriptions
 2696                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2697            }
 2698        }
 2699
 2700        // skip adding the initial selection to selection history
 2701        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2702        editor.end_selection(window, cx);
 2703        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2704
 2705        editor.scroll_manager.show_scrollbars(window, cx);
 2706        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2707
 2708        if full_mode {
 2709            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2710            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2711
 2712            if editor.git_blame_inline_enabled {
 2713                editor.start_git_blame_inline(false, window, cx);
 2714            }
 2715
 2716            editor.go_to_active_debug_line(window, cx);
 2717
 2718            editor.minimap =
 2719                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2720            editor.colors = Some(LspColorData::new(cx));
 2721            editor.use_document_folding_ranges = true;
 2722            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2723
 2724            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2725                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2726            }
 2727            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2728        }
 2729
 2730        editor
 2731    }
 2732
 2733    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2734        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2735    }
 2736
 2737    pub fn deploy_mouse_context_menu(
 2738        &mut self,
 2739        position: gpui::Point<Pixels>,
 2740        context_menu: Entity<ContextMenu>,
 2741        window: &mut Window,
 2742        cx: &mut Context<Self>,
 2743    ) {
 2744        self.mouse_context_menu = Some(MouseContextMenu::new(
 2745            self,
 2746            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2747            context_menu,
 2748            window,
 2749            cx,
 2750        ));
 2751    }
 2752
 2753    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2754        self.mouse_context_menu
 2755            .as_ref()
 2756            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2757    }
 2758
 2759    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2760        if self
 2761            .selections
 2762            .pending_anchor()
 2763            .is_some_and(|pending_selection| {
 2764                let snapshot = self.buffer().read(cx).snapshot(cx);
 2765                pending_selection.range().includes(range, &snapshot)
 2766            })
 2767        {
 2768            return true;
 2769        }
 2770
 2771        self.selections
 2772            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2773            .into_iter()
 2774            .any(|selection| {
 2775                // This is needed to cover a corner case, if we just check for an existing
 2776                // selection in the fold range, having a cursor at the start of the fold
 2777                // marks it as selected. Non-empty selections don't cause this.
 2778                let length = selection.end - selection.start;
 2779                length > 0
 2780            })
 2781    }
 2782
 2783    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2784        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2785    }
 2786
 2787    fn key_context_internal(
 2788        &self,
 2789        has_active_edit_prediction: bool,
 2790        window: &mut Window,
 2791        cx: &mut App,
 2792    ) -> KeyContext {
 2793        let mut key_context = KeyContext::new_with_defaults();
 2794        key_context.add("Editor");
 2795        let mode = match self.mode {
 2796            EditorMode::SingleLine => "single_line",
 2797            EditorMode::AutoHeight { .. } => "auto_height",
 2798            EditorMode::Minimap { .. } => "minimap",
 2799            EditorMode::Full { .. } => "full",
 2800        };
 2801
 2802        if EditorSettings::jupyter_enabled(cx) {
 2803            key_context.add("jupyter");
 2804        }
 2805
 2806        key_context.set("mode", mode);
 2807        if self.pending_rename.is_some() {
 2808            key_context.add("renaming");
 2809        }
 2810
 2811        if let Some(snippet_stack) = self.snippet_stack.last() {
 2812            key_context.add("in_snippet");
 2813
 2814            if snippet_stack.active_index > 0 {
 2815                key_context.add("has_previous_tabstop");
 2816            }
 2817
 2818            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2819                key_context.add("has_next_tabstop");
 2820            }
 2821        }
 2822
 2823        match self.context_menu.borrow().as_ref() {
 2824            Some(CodeContextMenu::Completions(menu)) => {
 2825                if menu.visible() {
 2826                    key_context.add("menu");
 2827                    key_context.add("showing_completions");
 2828                }
 2829            }
 2830            Some(CodeContextMenu::CodeActions(menu)) => {
 2831                if menu.visible() {
 2832                    key_context.add("menu");
 2833                    key_context.add("showing_code_actions")
 2834                }
 2835            }
 2836            None => {}
 2837        }
 2838
 2839        if self.signature_help_state.has_multiple_signatures() {
 2840            key_context.add("showing_signature_help");
 2841        }
 2842
 2843        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2844        if !self.focus_handle(cx).contains_focused(window, cx)
 2845            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2846        {
 2847            for addon in self.addons.values() {
 2848                addon.extend_key_context(&mut key_context, cx)
 2849            }
 2850        }
 2851
 2852        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2853            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2854                Some(
 2855                    file.full_path(cx)
 2856                        .extension()?
 2857                        .to_string_lossy()
 2858                        .to_lowercase(),
 2859                )
 2860            }) {
 2861                key_context.set("extension", extension);
 2862            }
 2863        } else {
 2864            key_context.add("multibuffer");
 2865        }
 2866
 2867        if has_active_edit_prediction {
 2868            if self.edit_prediction_in_conflict() {
 2869                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2870            } else {
 2871                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2872                key_context.add("copilot_suggestion");
 2873            }
 2874        }
 2875
 2876        if self.selection_mark_mode {
 2877            key_context.add("selection_mode");
 2878        }
 2879
 2880        let disjoint = self.selections.disjoint_anchors();
 2881        let snapshot = self.snapshot(window, cx);
 2882        let snapshot = snapshot.buffer_snapshot();
 2883        if self.mode == EditorMode::SingleLine
 2884            && let [selection] = disjoint
 2885            && selection.start == selection.end
 2886            && selection.end.to_offset(snapshot) == snapshot.len()
 2887        {
 2888            key_context.add("end_of_input");
 2889        }
 2890
 2891        if self.has_any_expanded_diff_hunks(cx) {
 2892            key_context.add("diffs_expanded");
 2893        }
 2894
 2895        key_context
 2896    }
 2897
 2898    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2899        self.last_bounds.as_ref()
 2900    }
 2901
 2902    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2903        if self.mouse_cursor_hidden {
 2904            self.mouse_cursor_hidden = false;
 2905            cx.notify();
 2906        }
 2907    }
 2908
 2909    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2910        let hide_mouse_cursor = match origin {
 2911            HideMouseCursorOrigin::TypingAction => {
 2912                matches!(
 2913                    self.hide_mouse_mode,
 2914                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2915                )
 2916            }
 2917            HideMouseCursorOrigin::MovementAction => {
 2918                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2919            }
 2920        };
 2921        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2922            self.mouse_cursor_hidden = hide_mouse_cursor;
 2923            cx.notify();
 2924        }
 2925    }
 2926
 2927    pub fn edit_prediction_in_conflict(&self) -> bool {
 2928        if !self.show_edit_predictions_in_menu() {
 2929            return false;
 2930        }
 2931
 2932        let showing_completions = self
 2933            .context_menu
 2934            .borrow()
 2935            .as_ref()
 2936            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2937
 2938        showing_completions
 2939            || self.edit_prediction_requires_modifier()
 2940            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2941            // bindings to insert tab characters.
 2942            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2943    }
 2944
 2945    pub fn accept_edit_prediction_keybind(
 2946        &self,
 2947        granularity: EditPredictionGranularity,
 2948        window: &mut Window,
 2949        cx: &mut App,
 2950    ) -> AcceptEditPredictionBinding {
 2951        let key_context = self.key_context_internal(true, window, cx);
 2952        let in_conflict = self.edit_prediction_in_conflict();
 2953
 2954        let bindings =
 2955            match granularity {
 2956                EditPredictionGranularity::Word => window
 2957                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2958                EditPredictionGranularity::Line => window
 2959                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2960                EditPredictionGranularity::Full => {
 2961                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2962                }
 2963            };
 2964
 2965        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2966            !in_conflict
 2967                || binding
 2968                    .keystrokes()
 2969                    .first()
 2970                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2971        }))
 2972    }
 2973
 2974    pub fn new_file(
 2975        workspace: &mut Workspace,
 2976        _: &workspace::NewFile,
 2977        window: &mut Window,
 2978        cx: &mut Context<Workspace>,
 2979    ) {
 2980        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 2981            "Failed to create buffer",
 2982            window,
 2983            cx,
 2984            |e, _, _| match e.error_code() {
 2985                ErrorCode::RemoteUpgradeRequired => Some(format!(
 2986                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 2987                e.error_tag("required").unwrap_or("the latest version")
 2988            )),
 2989                _ => None,
 2990            },
 2991        );
 2992    }
 2993
 2994    pub fn new_in_workspace(
 2995        workspace: &mut Workspace,
 2996        window: &mut Window,
 2997        cx: &mut Context<Workspace>,
 2998    ) -> Task<Result<Entity<Editor>>> {
 2999        let project = workspace.project().clone();
 3000        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3001
 3002        cx.spawn_in(window, async move |workspace, cx| {
 3003            let buffer = create.await?;
 3004            workspace.update_in(cx, |workspace, window, cx| {
 3005                let editor =
 3006                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3007                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3008                editor
 3009            })
 3010        })
 3011    }
 3012
 3013    fn new_file_vertical(
 3014        workspace: &mut Workspace,
 3015        _: &workspace::NewFileSplitVertical,
 3016        window: &mut Window,
 3017        cx: &mut Context<Workspace>,
 3018    ) {
 3019        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3020    }
 3021
 3022    fn new_file_horizontal(
 3023        workspace: &mut Workspace,
 3024        _: &workspace::NewFileSplitHorizontal,
 3025        window: &mut Window,
 3026        cx: &mut Context<Workspace>,
 3027    ) {
 3028        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3029    }
 3030
 3031    fn new_file_split(
 3032        workspace: &mut Workspace,
 3033        action: &workspace::NewFileSplit,
 3034        window: &mut Window,
 3035        cx: &mut Context<Workspace>,
 3036    ) {
 3037        Self::new_file_in_direction(workspace, action.0, window, cx)
 3038    }
 3039
 3040    fn new_file_in_direction(
 3041        workspace: &mut Workspace,
 3042        direction: SplitDirection,
 3043        window: &mut Window,
 3044        cx: &mut Context<Workspace>,
 3045    ) {
 3046        let project = workspace.project().clone();
 3047        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3048
 3049        cx.spawn_in(window, async move |workspace, cx| {
 3050            let buffer = create.await?;
 3051            workspace.update_in(cx, move |workspace, window, cx| {
 3052                workspace.split_item(
 3053                    direction,
 3054                    Box::new(
 3055                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3056                    ),
 3057                    window,
 3058                    cx,
 3059                )
 3060            })?;
 3061            anyhow::Ok(())
 3062        })
 3063        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3064            match e.error_code() {
 3065                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3066                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3067                e.error_tag("required").unwrap_or("the latest version")
 3068            )),
 3069                _ => None,
 3070            }
 3071        });
 3072    }
 3073
 3074    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3075        self.leader_id
 3076    }
 3077
 3078    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3079        &self.buffer
 3080    }
 3081
 3082    pub fn project(&self) -> Option<&Entity<Project>> {
 3083        self.project.as_ref()
 3084    }
 3085
 3086    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3087        self.workspace.as_ref()?.0.upgrade()
 3088    }
 3089
 3090    /// Detaches a task and shows an error notification in the workspace if available,
 3091    /// otherwise just logs the error.
 3092    pub fn detach_and_notify_err<R, E>(
 3093        &self,
 3094        task: Task<Result<R, E>>,
 3095        window: &mut Window,
 3096        cx: &mut App,
 3097    ) where
 3098        E: std::fmt::Debug + std::fmt::Display + 'static,
 3099        R: 'static,
 3100    {
 3101        if let Some(workspace) = self.workspace() {
 3102            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3103        } else {
 3104            task.detach_and_log_err(cx);
 3105        }
 3106    }
 3107
 3108    /// Returns the workspace serialization ID if this editor should be serialized.
 3109    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3110        self.workspace
 3111            .as_ref()
 3112            .filter(|_| self.should_serialize_buffer())
 3113            .and_then(|workspace| workspace.1)
 3114    }
 3115
 3116    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3117        self.buffer().read(cx).title(cx)
 3118    }
 3119
 3120    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3121        let git_blame_gutter_max_author_length = self
 3122            .render_git_blame_gutter(cx)
 3123            .then(|| {
 3124                if let Some(blame) = self.blame.as_ref() {
 3125                    let max_author_length =
 3126                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3127                    Some(max_author_length)
 3128                } else {
 3129                    None
 3130                }
 3131            })
 3132            .flatten();
 3133
 3134        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3135
 3136        EditorSnapshot {
 3137            mode: self.mode.clone(),
 3138            show_gutter: self.show_gutter,
 3139            offset_content: self.offset_content,
 3140            show_line_numbers: self.show_line_numbers,
 3141            number_deleted_lines: self.number_deleted_lines,
 3142            show_git_diff_gutter: self.show_git_diff_gutter,
 3143            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3144            show_code_actions: self.show_code_actions,
 3145            show_runnables: self.show_runnables,
 3146            show_breakpoints: self.show_breakpoints,
 3147            git_blame_gutter_max_author_length,
 3148            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3149            display_snapshot,
 3150            placeholder_display_snapshot: self
 3151                .placeholder_display_map
 3152                .as_ref()
 3153                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3154            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3155            is_focused: self.focus_handle.is_focused(window),
 3156            current_line_highlight: self
 3157                .current_line_highlight
 3158                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3159            gutter_hovered: self.gutter_hovered,
 3160        }
 3161    }
 3162
 3163    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3164        self.buffer.read(cx).language_at(point, cx)
 3165    }
 3166
 3167    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3168        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3169    }
 3170
 3171    pub fn active_excerpt(
 3172        &self,
 3173        cx: &App,
 3174    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3175        self.buffer
 3176            .read(cx)
 3177            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3178    }
 3179
 3180    pub fn mode(&self) -> &EditorMode {
 3181        &self.mode
 3182    }
 3183
 3184    pub fn set_mode(&mut self, mode: EditorMode) {
 3185        self.mode = mode;
 3186    }
 3187
 3188    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3189        self.collaboration_hub.as_deref()
 3190    }
 3191
 3192    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3193        self.collaboration_hub = Some(hub);
 3194    }
 3195
 3196    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3197        self.in_project_search = in_project_search;
 3198    }
 3199
 3200    pub fn set_custom_context_menu(
 3201        &mut self,
 3202        f: impl 'static
 3203        + Fn(
 3204            &mut Self,
 3205            DisplayPoint,
 3206            &mut Window,
 3207            &mut Context<Self>,
 3208        ) -> Option<Entity<ui::ContextMenu>>,
 3209    ) {
 3210        self.custom_context_menu = Some(Box::new(f))
 3211    }
 3212
 3213    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3214        self.completion_provider = provider;
 3215    }
 3216
 3217    #[cfg(any(test, feature = "test-support"))]
 3218    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3219        self.completion_provider.clone()
 3220    }
 3221
 3222    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3223        self.semantics_provider.clone()
 3224    }
 3225
 3226    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3227        self.semantics_provider = provider;
 3228    }
 3229
 3230    pub fn set_edit_prediction_provider<T>(
 3231        &mut self,
 3232        provider: Option<Entity<T>>,
 3233        window: &mut Window,
 3234        cx: &mut Context<Self>,
 3235    ) where
 3236        T: EditPredictionDelegate,
 3237    {
 3238        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3239            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3240                if this.focus_handle.is_focused(window) {
 3241                    this.update_visible_edit_prediction(window, cx);
 3242                }
 3243            }),
 3244            provider: Arc::new(provider),
 3245        });
 3246        self.update_edit_prediction_settings(cx);
 3247        self.refresh_edit_prediction(false, false, window, cx);
 3248    }
 3249
 3250    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3251        self.placeholder_display_map
 3252            .as_ref()
 3253            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3254    }
 3255
 3256    pub fn set_placeholder_text(
 3257        &mut self,
 3258        placeholder_text: &str,
 3259        window: &mut Window,
 3260        cx: &mut Context<Self>,
 3261    ) {
 3262        let multibuffer = cx
 3263            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3264
 3265        let style = window.text_style();
 3266
 3267        self.placeholder_display_map = Some(cx.new(|cx| {
 3268            DisplayMap::new(
 3269                multibuffer,
 3270                style.font(),
 3271                style.font_size.to_pixels(window.rem_size()),
 3272                None,
 3273                FILE_HEADER_HEIGHT,
 3274                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3275                Default::default(),
 3276                DiagnosticSeverity::Off,
 3277                cx,
 3278            )
 3279        }));
 3280        cx.notify();
 3281    }
 3282
 3283    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3284        self.cursor_shape = cursor_shape;
 3285
 3286        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3287        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3288
 3289        cx.notify();
 3290    }
 3291
 3292    pub fn cursor_shape(&self) -> CursorShape {
 3293        self.cursor_shape
 3294    }
 3295
 3296    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3297        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3298    }
 3299
 3300    pub fn set_current_line_highlight(
 3301        &mut self,
 3302        current_line_highlight: Option<CurrentLineHighlight>,
 3303    ) {
 3304        self.current_line_highlight = current_line_highlight;
 3305    }
 3306
 3307    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3308        self.collapse_matches = collapse_matches;
 3309    }
 3310
 3311    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3312        if self.collapse_matches {
 3313            return range.start..range.start;
 3314        }
 3315        range.clone()
 3316    }
 3317
 3318    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3319        self.display_map.read(cx).clip_at_line_ends
 3320    }
 3321
 3322    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3323        if self.display_map.read(cx).clip_at_line_ends != clip {
 3324            self.display_map
 3325                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3326        }
 3327    }
 3328
 3329    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3330        self.input_enabled = input_enabled;
 3331    }
 3332
 3333    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3334        self.expects_character_input = expects_character_input;
 3335    }
 3336
 3337    pub fn set_edit_predictions_hidden_for_vim_mode(
 3338        &mut self,
 3339        hidden: bool,
 3340        window: &mut Window,
 3341        cx: &mut Context<Self>,
 3342    ) {
 3343        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3344            self.edit_predictions_hidden_for_vim_mode = hidden;
 3345            if hidden {
 3346                self.update_visible_edit_prediction(window, cx);
 3347            } else {
 3348                self.refresh_edit_prediction(true, false, window, cx);
 3349            }
 3350        }
 3351    }
 3352
 3353    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3354        self.menu_edit_predictions_policy = value;
 3355    }
 3356
 3357    pub fn set_autoindent(&mut self, autoindent: bool) {
 3358        if autoindent {
 3359            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3360        } else {
 3361            self.autoindent_mode = None;
 3362        }
 3363    }
 3364
 3365    pub fn capability(&self, cx: &App) -> Capability {
 3366        if self.read_only {
 3367            Capability::ReadOnly
 3368        } else {
 3369            self.buffer.read(cx).capability()
 3370        }
 3371    }
 3372
 3373    pub fn read_only(&self, cx: &App) -> bool {
 3374        self.read_only || self.buffer.read(cx).read_only()
 3375    }
 3376
 3377    pub fn set_read_only(&mut self, read_only: bool) {
 3378        self.read_only = read_only;
 3379    }
 3380
 3381    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3382        self.use_autoclose = autoclose;
 3383    }
 3384
 3385    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3386        self.use_auto_surround = auto_surround;
 3387    }
 3388
 3389    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3390        self.auto_replace_emoji_shortcode = auto_replace;
 3391    }
 3392
 3393    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3394        self.buffer_serialization = should_serialize.then(|| {
 3395            BufferSerialization::new(
 3396                ProjectSettings::get_global(cx)
 3397                    .session
 3398                    .restore_unsaved_buffers,
 3399            )
 3400        })
 3401    }
 3402
 3403    fn should_serialize_buffer(&self) -> bool {
 3404        self.buffer_serialization.is_some()
 3405    }
 3406
 3407    pub fn toggle_edit_predictions(
 3408        &mut self,
 3409        _: &ToggleEditPrediction,
 3410        window: &mut Window,
 3411        cx: &mut Context<Self>,
 3412    ) {
 3413        if self.show_edit_predictions_override.is_some() {
 3414            self.set_show_edit_predictions(None, window, cx);
 3415        } else {
 3416            let show_edit_predictions = !self.edit_predictions_enabled();
 3417            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3418        }
 3419    }
 3420
 3421    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3422        self.show_completions_on_input_override = show_completions_on_input;
 3423    }
 3424
 3425    pub fn set_show_edit_predictions(
 3426        &mut self,
 3427        show_edit_predictions: Option<bool>,
 3428        window: &mut Window,
 3429        cx: &mut Context<Self>,
 3430    ) {
 3431        self.show_edit_predictions_override = show_edit_predictions;
 3432        self.update_edit_prediction_settings(cx);
 3433
 3434        if let Some(false) = show_edit_predictions {
 3435            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3436        } else {
 3437            self.refresh_edit_prediction(false, true, window, cx);
 3438        }
 3439    }
 3440
 3441    fn edit_predictions_disabled_in_scope(
 3442        &self,
 3443        buffer: &Entity<Buffer>,
 3444        buffer_position: language::Anchor,
 3445        cx: &App,
 3446    ) -> bool {
 3447        let snapshot = buffer.read(cx).snapshot();
 3448        let settings = snapshot.settings_at(buffer_position, cx);
 3449
 3450        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3451            return false;
 3452        };
 3453
 3454        scope.override_name().is_some_and(|scope_name| {
 3455            settings
 3456                .edit_predictions_disabled_in
 3457                .iter()
 3458                .any(|s| s == scope_name)
 3459        })
 3460    }
 3461
 3462    pub fn set_use_modal_editing(&mut self, to: bool) {
 3463        self.use_modal_editing = to;
 3464    }
 3465
 3466    pub fn use_modal_editing(&self) -> bool {
 3467        self.use_modal_editing
 3468    }
 3469
 3470    fn selections_did_change(
 3471        &mut self,
 3472        local: bool,
 3473        old_cursor_position: &Anchor,
 3474        effects: SelectionEffects,
 3475        window: &mut Window,
 3476        cx: &mut Context<Self>,
 3477    ) {
 3478        window.invalidate_character_coordinates();
 3479
 3480        // Copy selections to primary selection buffer
 3481        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3482        if local {
 3483            let selections = self
 3484                .selections
 3485                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3486            let buffer_handle = self.buffer.read(cx).read(cx);
 3487
 3488            let mut text = String::new();
 3489            for (index, selection) in selections.iter().enumerate() {
 3490                let text_for_selection = buffer_handle
 3491                    .text_for_range(selection.start..selection.end)
 3492                    .collect::<String>();
 3493
 3494                text.push_str(&text_for_selection);
 3495                if index != selections.len() - 1 {
 3496                    text.push('\n');
 3497                }
 3498            }
 3499
 3500            if !text.is_empty() {
 3501                cx.write_to_primary(ClipboardItem::new_string(text));
 3502            }
 3503        }
 3504
 3505        let selection_anchors = self.selections.disjoint_anchors_arc();
 3506
 3507        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3508            self.buffer.update(cx, |buffer, cx| {
 3509                buffer.set_active_selections(
 3510                    &selection_anchors,
 3511                    self.selections.line_mode(),
 3512                    self.cursor_shape,
 3513                    cx,
 3514                )
 3515            });
 3516        }
 3517        let display_map = self
 3518            .display_map
 3519            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3520        let buffer = display_map.buffer_snapshot();
 3521        if self.selections.count() == 1 {
 3522            self.add_selections_state = None;
 3523        }
 3524        self.select_next_state = None;
 3525        self.select_prev_state = None;
 3526        self.select_syntax_node_history.try_clear();
 3527        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3528        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3529        self.take_rename(false, window, cx);
 3530
 3531        let newest_selection = self.selections.newest_anchor();
 3532        let new_cursor_position = newest_selection.head();
 3533        let selection_start = newest_selection.start;
 3534
 3535        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3536            self.push_to_nav_history(
 3537                *old_cursor_position,
 3538                Some(new_cursor_position.to_point(buffer)),
 3539                false,
 3540                effects.nav_history == Some(true),
 3541                cx,
 3542            );
 3543        }
 3544
 3545        if local {
 3546            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3547                self.register_buffer(buffer_id, cx);
 3548            }
 3549
 3550            let mut context_menu = self.context_menu.borrow_mut();
 3551            let completion_menu = match context_menu.as_ref() {
 3552                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3553                Some(CodeContextMenu::CodeActions(_)) => {
 3554                    *context_menu = None;
 3555                    None
 3556                }
 3557                None => None,
 3558            };
 3559            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3560            drop(context_menu);
 3561
 3562            if effects.completions
 3563                && let Some(completion_position) = completion_position
 3564            {
 3565                let start_offset = selection_start.to_offset(buffer);
 3566                let position_matches = start_offset == completion_position.to_offset(buffer);
 3567                let continue_showing = if let Some((snap, ..)) =
 3568                    buffer.point_to_buffer_offset(completion_position)
 3569                    && !snap.capability.editable()
 3570                {
 3571                    false
 3572                } else if position_matches {
 3573                    if self.snippet_stack.is_empty() {
 3574                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3575                            == Some(CharKind::Word)
 3576                    } else {
 3577                        // Snippet choices can be shown even when the cursor is in whitespace.
 3578                        // Dismissing the menu with actions like backspace is handled by
 3579                        // invalidation regions.
 3580                        true
 3581                    }
 3582                } else {
 3583                    false
 3584                };
 3585
 3586                if continue_showing {
 3587                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3588                } else {
 3589                    self.hide_context_menu(window, cx);
 3590                }
 3591            }
 3592
 3593            hide_hover(self, cx);
 3594
 3595            if old_cursor_position.to_display_point(&display_map).row()
 3596                != new_cursor_position.to_display_point(&display_map).row()
 3597            {
 3598                self.available_code_actions.take();
 3599            }
 3600            self.refresh_code_actions(window, cx);
 3601            self.refresh_document_highlights(cx);
 3602            refresh_linked_ranges(self, window, cx);
 3603
 3604            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3605            self.refresh_matching_bracket_highlights(&display_map, cx);
 3606            self.refresh_outline_symbols_at_cursor(cx);
 3607            self.update_visible_edit_prediction(window, cx);
 3608            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3609            self.inline_blame_popover.take();
 3610            if self.git_blame_inline_enabled {
 3611                self.start_inline_blame_timer(window, cx);
 3612            }
 3613        }
 3614
 3615        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3616
 3617        if local && !self.suppress_selection_callback {
 3618            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3619                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3620                callback(cursor_position, window, cx);
 3621            }
 3622        }
 3623
 3624        cx.emit(EditorEvent::SelectionsChanged { local });
 3625
 3626        let selections = &self.selections.disjoint_anchors_arc();
 3627        if selections.len() == 1 {
 3628            cx.emit(SearchEvent::ActiveMatchChanged)
 3629        }
 3630        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3631            let inmemory_selections = selections
 3632                .iter()
 3633                .map(|s| {
 3634                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3635                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3636                })
 3637                .collect();
 3638            self.update_restoration_data(cx, |data| {
 3639                data.selections = inmemory_selections;
 3640            });
 3641
 3642            if WorkspaceSettings::get(None, cx).restore_on_startup
 3643                != RestoreOnStartupBehavior::EmptyTab
 3644                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3645            {
 3646                let snapshot = self.buffer().read(cx).snapshot(cx);
 3647                let selections = selections.clone();
 3648                let background_executor = cx.background_executor().clone();
 3649                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3650                self.serialize_selections = cx.background_spawn(async move {
 3651                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3652                    let db_selections = selections
 3653                        .iter()
 3654                        .map(|selection| {
 3655                            (
 3656                                selection.start.to_offset(&snapshot).0,
 3657                                selection.end.to_offset(&snapshot).0,
 3658                            )
 3659                        })
 3660                        .collect();
 3661
 3662                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3663                        .await
 3664                        .with_context(|| {
 3665                            format!(
 3666                                "persisting editor selections for editor {editor_id}, \
 3667                                workspace {workspace_id:?}"
 3668                            )
 3669                        })
 3670                        .log_err();
 3671                });
 3672            }
 3673        }
 3674
 3675        cx.notify();
 3676    }
 3677
 3678    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3679        use text::ToOffset as _;
 3680        use text::ToPoint as _;
 3681
 3682        if self.mode.is_minimap()
 3683            || WorkspaceSettings::get(None, cx).restore_on_startup
 3684                == RestoreOnStartupBehavior::EmptyTab
 3685        {
 3686            return;
 3687        }
 3688
 3689        if !self.buffer().read(cx).is_singleton() {
 3690            return;
 3691        }
 3692
 3693        let display_snapshot = self
 3694            .display_map
 3695            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3696        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3697            return;
 3698        };
 3699        let inmemory_folds = display_snapshot
 3700            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3701            .map(|fold| {
 3702                fold.range.start.text_anchor.to_point(&snapshot)
 3703                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3704            })
 3705            .collect();
 3706        self.update_restoration_data(cx, |data| {
 3707            data.folds = inmemory_folds;
 3708        });
 3709
 3710        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3711            return;
 3712        };
 3713
 3714        // Get file path for path-based fold storage (survives tab close)
 3715        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3716            project::File::from_dyn(buffer.read(cx).file())
 3717                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3718        }) else {
 3719            return;
 3720        };
 3721
 3722        let background_executor = cx.background_executor().clone();
 3723        const FINGERPRINT_LEN: usize = 32;
 3724        let db_folds = display_snapshot
 3725            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3726            .map(|fold| {
 3727                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3728                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3729
 3730                // Extract fingerprints - content at fold boundaries for validation on restore
 3731                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3732                // content that might change independently.
 3733                // start_fp: first min(32, fold_len) bytes of fold content
 3734                // end_fp: last min(32, fold_len) bytes of fold content
 3735                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3736                let fold_len = end - start;
 3737                let start_fp_end = snapshot
 3738                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3739                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3740                let end_fp_start = snapshot
 3741                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3742                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3743
 3744                (start, end, start_fp, end_fp)
 3745            })
 3746            .collect::<Vec<_>>();
 3747        self.serialize_folds = cx.background_spawn(async move {
 3748            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3749            if db_folds.is_empty() {
 3750                // No folds - delete any persisted folds for this file
 3751                DB.delete_file_folds(workspace_id, file_path)
 3752                    .await
 3753                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3754                    .log_err();
 3755            } else {
 3756                DB.save_file_folds(workspace_id, file_path, db_folds)
 3757                    .await
 3758                    .with_context(|| {
 3759                        format!("persisting file folds for workspace {workspace_id:?}")
 3760                    })
 3761                    .log_err();
 3762            }
 3763        });
 3764    }
 3765
 3766    pub fn sync_selections(
 3767        &mut self,
 3768        other: Entity<Editor>,
 3769        cx: &mut Context<Self>,
 3770    ) -> gpui::Subscription {
 3771        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3772        if !other_selections.is_empty() {
 3773            self.selections
 3774                .change_with(&self.display_snapshot(cx), |selections| {
 3775                    selections.select_anchors(other_selections);
 3776                });
 3777        }
 3778
 3779        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3780            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3781                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3782                if other_selections.is_empty() {
 3783                    return;
 3784                }
 3785                let snapshot = this.display_snapshot(cx);
 3786                this.selections.change_with(&snapshot, |selections| {
 3787                    selections.select_anchors(other_selections);
 3788                });
 3789            }
 3790        });
 3791
 3792        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3793            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3794                let these_selections = this.selections.disjoint_anchors().to_vec();
 3795                if these_selections.is_empty() {
 3796                    return;
 3797                }
 3798                other.update(cx, |other_editor, cx| {
 3799                    let snapshot = other_editor.display_snapshot(cx);
 3800                    other_editor
 3801                        .selections
 3802                        .change_with(&snapshot, |selections| {
 3803                            selections.select_anchors(these_selections);
 3804                        })
 3805                });
 3806            }
 3807        });
 3808
 3809        Subscription::join(other_subscription, this_subscription)
 3810    }
 3811
 3812    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3813        if self.buffer().read(cx).is_singleton() {
 3814            return;
 3815        }
 3816        let snapshot = self.buffer.read(cx).snapshot(cx);
 3817        let buffer_ids: HashSet<BufferId> = self
 3818            .selections
 3819            .disjoint_anchor_ranges()
 3820            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3821            .collect();
 3822        for buffer_id in buffer_ids {
 3823            self.unfold_buffer(buffer_id, cx);
 3824        }
 3825    }
 3826
 3827    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3828    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3829    /// effects of selection change occur at the end of the transaction.
 3830    pub fn change_selections<R>(
 3831        &mut self,
 3832        effects: SelectionEffects,
 3833        window: &mut Window,
 3834        cx: &mut Context<Self>,
 3835        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3836    ) -> R {
 3837        let snapshot = self.display_snapshot(cx);
 3838        if let Some(state) = &mut self.deferred_selection_effects_state {
 3839            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3840            state.effects.completions = effects.completions;
 3841            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3842            let (changed, result) = self.selections.change_with(&snapshot, change);
 3843            state.changed |= changed;
 3844            return result;
 3845        }
 3846        let mut state = DeferredSelectionEffectsState {
 3847            changed: false,
 3848            effects,
 3849            old_cursor_position: self.selections.newest_anchor().head(),
 3850            history_entry: SelectionHistoryEntry {
 3851                selections: self.selections.disjoint_anchors_arc(),
 3852                select_next_state: self.select_next_state.clone(),
 3853                select_prev_state: self.select_prev_state.clone(),
 3854                add_selections_state: self.add_selections_state.clone(),
 3855            },
 3856        };
 3857        let (changed, result) = self.selections.change_with(&snapshot, change);
 3858        state.changed = state.changed || changed;
 3859        if self.defer_selection_effects {
 3860            self.deferred_selection_effects_state = Some(state);
 3861        } else {
 3862            self.apply_selection_effects(state, window, cx);
 3863        }
 3864        result
 3865    }
 3866
 3867    /// Defers the effects of selection change, so that the effects of multiple calls to
 3868    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3869    /// to selection history and the state of popovers based on selection position aren't
 3870    /// erroneously updated.
 3871    pub fn with_selection_effects_deferred<R>(
 3872        &mut self,
 3873        window: &mut Window,
 3874        cx: &mut Context<Self>,
 3875        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3876    ) -> R {
 3877        let already_deferred = self.defer_selection_effects;
 3878        self.defer_selection_effects = true;
 3879        let result = update(self, window, cx);
 3880        if !already_deferred {
 3881            self.defer_selection_effects = false;
 3882            if let Some(state) = self.deferred_selection_effects_state.take() {
 3883                self.apply_selection_effects(state, window, cx);
 3884            }
 3885        }
 3886        result
 3887    }
 3888
 3889    fn apply_selection_effects(
 3890        &mut self,
 3891        state: DeferredSelectionEffectsState,
 3892        window: &mut Window,
 3893        cx: &mut Context<Self>,
 3894    ) {
 3895        if state.changed {
 3896            self.selection_history.push(state.history_entry);
 3897
 3898            if let Some(autoscroll) = state.effects.scroll {
 3899                self.request_autoscroll(autoscroll, cx);
 3900            }
 3901
 3902            let old_cursor_position = &state.old_cursor_position;
 3903
 3904            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3905
 3906            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3907                self.show_signature_help_auto(window, cx);
 3908            }
 3909        }
 3910    }
 3911
 3912    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3913    where
 3914        I: IntoIterator<Item = (Range<S>, T)>,
 3915        S: ToOffset,
 3916        T: Into<Arc<str>>,
 3917    {
 3918        if self.read_only(cx) {
 3919            return;
 3920        }
 3921
 3922        self.buffer
 3923            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3924    }
 3925
 3926    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3927    where
 3928        I: IntoIterator<Item = (Range<S>, T)>,
 3929        S: ToOffset,
 3930        T: Into<Arc<str>>,
 3931    {
 3932        if self.read_only(cx) {
 3933            return;
 3934        }
 3935
 3936        self.buffer.update(cx, |buffer, cx| {
 3937            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3938        });
 3939    }
 3940
 3941    pub fn edit_with_block_indent<I, S, T>(
 3942        &mut self,
 3943        edits: I,
 3944        original_indent_columns: Vec<Option<u32>>,
 3945        cx: &mut Context<Self>,
 3946    ) where
 3947        I: IntoIterator<Item = (Range<S>, T)>,
 3948        S: ToOffset,
 3949        T: Into<Arc<str>>,
 3950    {
 3951        if self.read_only(cx) {
 3952            return;
 3953        }
 3954
 3955        self.buffer.update(cx, |buffer, cx| {
 3956            buffer.edit(
 3957                edits,
 3958                Some(AutoindentMode::Block {
 3959                    original_indent_columns,
 3960                }),
 3961                cx,
 3962            )
 3963        });
 3964    }
 3965
 3966    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3967        self.hide_context_menu(window, cx);
 3968
 3969        match phase {
 3970            SelectPhase::Begin {
 3971                position,
 3972                add,
 3973                click_count,
 3974            } => self.begin_selection(position, add, click_count, window, cx),
 3975            SelectPhase::BeginColumnar {
 3976                position,
 3977                goal_column,
 3978                reset,
 3979                mode,
 3980            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3981            SelectPhase::Extend {
 3982                position,
 3983                click_count,
 3984            } => self.extend_selection(position, click_count, window, cx),
 3985            SelectPhase::Update {
 3986                position,
 3987                goal_column,
 3988                scroll_delta,
 3989            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3990            SelectPhase::End => self.end_selection(window, cx),
 3991        }
 3992    }
 3993
 3994    fn extend_selection(
 3995        &mut self,
 3996        position: DisplayPoint,
 3997        click_count: usize,
 3998        window: &mut Window,
 3999        cx: &mut Context<Self>,
 4000    ) {
 4001        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4002        let tail = self
 4003            .selections
 4004            .newest::<MultiBufferOffset>(&display_map)
 4005            .tail();
 4006        let click_count = click_count.max(match self.selections.select_mode() {
 4007            SelectMode::Character => 1,
 4008            SelectMode::Word(_) => 2,
 4009            SelectMode::Line(_) => 3,
 4010            SelectMode::All => 4,
 4011        });
 4012        self.begin_selection(position, false, click_count, window, cx);
 4013
 4014        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4015
 4016        let current_selection = match self.selections.select_mode() {
 4017            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4018            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4019        };
 4020
 4021        let mut pending_selection = self
 4022            .selections
 4023            .pending_anchor()
 4024            .cloned()
 4025            .expect("extend_selection not called with pending selection");
 4026
 4027        if pending_selection
 4028            .start
 4029            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4030            == Ordering::Greater
 4031        {
 4032            pending_selection.start = current_selection.start;
 4033        }
 4034        if pending_selection
 4035            .end
 4036            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4037            == Ordering::Less
 4038        {
 4039            pending_selection.end = current_selection.end;
 4040            pending_selection.reversed = true;
 4041        }
 4042
 4043        let mut pending_mode = self.selections.pending_mode().unwrap();
 4044        match &mut pending_mode {
 4045            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4046            _ => {}
 4047        }
 4048
 4049        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4050            SelectionEffects::scroll(Autoscroll::fit())
 4051        } else {
 4052            SelectionEffects::no_scroll()
 4053        };
 4054
 4055        self.change_selections(effects, window, cx, |s| {
 4056            s.set_pending(pending_selection.clone(), pending_mode);
 4057            s.set_is_extending(true);
 4058        });
 4059    }
 4060
 4061    fn begin_selection(
 4062        &mut self,
 4063        position: DisplayPoint,
 4064        add: bool,
 4065        click_count: usize,
 4066        window: &mut Window,
 4067        cx: &mut Context<Self>,
 4068    ) {
 4069        if !self.focus_handle.is_focused(window) {
 4070            self.last_focused_descendant = None;
 4071            window.focus(&self.focus_handle, cx);
 4072        }
 4073
 4074        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4075        let buffer = display_map.buffer_snapshot();
 4076        let position = display_map.clip_point(position, Bias::Left);
 4077
 4078        let start;
 4079        let end;
 4080        let mode;
 4081        let mut auto_scroll;
 4082        match click_count {
 4083            1 => {
 4084                start = buffer.anchor_before(position.to_point(&display_map));
 4085                end = start;
 4086                mode = SelectMode::Character;
 4087                auto_scroll = true;
 4088            }
 4089            2 => {
 4090                let position = display_map
 4091                    .clip_point(position, Bias::Left)
 4092                    .to_offset(&display_map, Bias::Left);
 4093                let (range, _) = buffer.surrounding_word(position, None);
 4094                start = buffer.anchor_before(range.start);
 4095                end = buffer.anchor_before(range.end);
 4096                mode = SelectMode::Word(start..end);
 4097                auto_scroll = true;
 4098            }
 4099            3 => {
 4100                let position = display_map
 4101                    .clip_point(position, Bias::Left)
 4102                    .to_point(&display_map);
 4103                let line_start = display_map.prev_line_boundary(position).0;
 4104                let next_line_start = buffer.clip_point(
 4105                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4106                    Bias::Left,
 4107                );
 4108                start = buffer.anchor_before(line_start);
 4109                end = buffer.anchor_before(next_line_start);
 4110                mode = SelectMode::Line(start..end);
 4111                auto_scroll = true;
 4112            }
 4113            _ => {
 4114                start = buffer.anchor_before(MultiBufferOffset(0));
 4115                end = buffer.anchor_before(buffer.len());
 4116                mode = SelectMode::All;
 4117                auto_scroll = false;
 4118            }
 4119        }
 4120        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4121
 4122        let point_to_delete: Option<usize> = {
 4123            let selected_points: Vec<Selection<Point>> =
 4124                self.selections.disjoint_in_range(start..end, &display_map);
 4125
 4126            if !add || click_count > 1 {
 4127                None
 4128            } else if !selected_points.is_empty() {
 4129                Some(selected_points[0].id)
 4130            } else {
 4131                let clicked_point_already_selected =
 4132                    self.selections.disjoint_anchors().iter().find(|selection| {
 4133                        selection.start.to_point(buffer) == start.to_point(buffer)
 4134                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4135                    });
 4136
 4137                clicked_point_already_selected.map(|selection| selection.id)
 4138            }
 4139        };
 4140
 4141        let selections_count = self.selections.count();
 4142        let effects = if auto_scroll {
 4143            SelectionEffects::default()
 4144        } else {
 4145            SelectionEffects::no_scroll()
 4146        };
 4147
 4148        self.change_selections(effects, window, cx, |s| {
 4149            if let Some(point_to_delete) = point_to_delete {
 4150                s.delete(point_to_delete);
 4151
 4152                if selections_count == 1 {
 4153                    s.set_pending_anchor_range(start..end, mode);
 4154                }
 4155            } else {
 4156                if !add {
 4157                    s.clear_disjoint();
 4158                }
 4159
 4160                s.set_pending_anchor_range(start..end, mode);
 4161            }
 4162        });
 4163    }
 4164
 4165    fn begin_columnar_selection(
 4166        &mut self,
 4167        position: DisplayPoint,
 4168        goal_column: u32,
 4169        reset: bool,
 4170        mode: ColumnarMode,
 4171        window: &mut Window,
 4172        cx: &mut Context<Self>,
 4173    ) {
 4174        if !self.focus_handle.is_focused(window) {
 4175            self.last_focused_descendant = None;
 4176            window.focus(&self.focus_handle, cx);
 4177        }
 4178
 4179        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4180
 4181        if reset {
 4182            let pointer_position = display_map
 4183                .buffer_snapshot()
 4184                .anchor_before(position.to_point(&display_map));
 4185
 4186            self.change_selections(
 4187                SelectionEffects::scroll(Autoscroll::newest()),
 4188                window,
 4189                cx,
 4190                |s| {
 4191                    s.clear_disjoint();
 4192                    s.set_pending_anchor_range(
 4193                        pointer_position..pointer_position,
 4194                        SelectMode::Character,
 4195                    );
 4196                },
 4197            );
 4198        };
 4199
 4200        let tail = self.selections.newest::<Point>(&display_map).tail();
 4201        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4202        self.columnar_selection_state = match mode {
 4203            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4204                selection_tail: selection_anchor,
 4205                display_point: if reset {
 4206                    if position.column() != goal_column {
 4207                        Some(DisplayPoint::new(position.row(), goal_column))
 4208                    } else {
 4209                        None
 4210                    }
 4211                } else {
 4212                    None
 4213                },
 4214            }),
 4215            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4216                selection_tail: selection_anchor,
 4217            }),
 4218        };
 4219
 4220        if !reset {
 4221            self.select_columns(position, goal_column, &display_map, window, cx);
 4222        }
 4223    }
 4224
 4225    fn update_selection(
 4226        &mut self,
 4227        position: DisplayPoint,
 4228        goal_column: u32,
 4229        scroll_delta: gpui::Point<f32>,
 4230        window: &mut Window,
 4231        cx: &mut Context<Self>,
 4232    ) {
 4233        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4234
 4235        if self.columnar_selection_state.is_some() {
 4236            self.select_columns(position, goal_column, &display_map, window, cx);
 4237        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4238            let buffer = display_map.buffer_snapshot();
 4239            let head;
 4240            let tail;
 4241            let mode = self.selections.pending_mode().unwrap();
 4242            match &mode {
 4243                SelectMode::Character => {
 4244                    head = position.to_point(&display_map);
 4245                    tail = pending.tail().to_point(buffer);
 4246                }
 4247                SelectMode::Word(original_range) => {
 4248                    let offset = display_map
 4249                        .clip_point(position, Bias::Left)
 4250                        .to_offset(&display_map, Bias::Left);
 4251                    let original_range = original_range.to_offset(buffer);
 4252
 4253                    let head_offset = if buffer.is_inside_word(offset, None)
 4254                        || original_range.contains(&offset)
 4255                    {
 4256                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4257                        if word_range.start < original_range.start {
 4258                            word_range.start
 4259                        } else {
 4260                            word_range.end
 4261                        }
 4262                    } else {
 4263                        offset
 4264                    };
 4265
 4266                    head = head_offset.to_point(buffer);
 4267                    if head_offset <= original_range.start {
 4268                        tail = original_range.end.to_point(buffer);
 4269                    } else {
 4270                        tail = original_range.start.to_point(buffer);
 4271                    }
 4272                }
 4273                SelectMode::Line(original_range) => {
 4274                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4275
 4276                    let position = display_map
 4277                        .clip_point(position, Bias::Left)
 4278                        .to_point(&display_map);
 4279                    let line_start = display_map.prev_line_boundary(position).0;
 4280                    let next_line_start = buffer.clip_point(
 4281                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4282                        Bias::Left,
 4283                    );
 4284
 4285                    if line_start < original_range.start {
 4286                        head = line_start
 4287                    } else {
 4288                        head = next_line_start
 4289                    }
 4290
 4291                    if head <= original_range.start {
 4292                        tail = original_range.end;
 4293                    } else {
 4294                        tail = original_range.start;
 4295                    }
 4296                }
 4297                SelectMode::All => {
 4298                    return;
 4299                }
 4300            };
 4301
 4302            if head < tail {
 4303                pending.start = buffer.anchor_before(head);
 4304                pending.end = buffer.anchor_before(tail);
 4305                pending.reversed = true;
 4306            } else {
 4307                pending.start = buffer.anchor_before(tail);
 4308                pending.end = buffer.anchor_before(head);
 4309                pending.reversed = false;
 4310            }
 4311
 4312            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4313                s.set_pending(pending.clone(), mode);
 4314            });
 4315        } else {
 4316            log::error!("update_selection dispatched with no pending selection");
 4317            return;
 4318        }
 4319
 4320        self.apply_scroll_delta(scroll_delta, window, cx);
 4321        cx.notify();
 4322    }
 4323
 4324    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4325        self.columnar_selection_state.take();
 4326        if let Some(pending_mode) = self.selections.pending_mode() {
 4327            let selections = self
 4328                .selections
 4329                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4330            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4331                s.select(selections);
 4332                s.clear_pending();
 4333                if s.is_extending() {
 4334                    s.set_is_extending(false);
 4335                } else {
 4336                    s.set_select_mode(pending_mode);
 4337                }
 4338            });
 4339        }
 4340    }
 4341
 4342    fn select_columns(
 4343        &mut self,
 4344        head: DisplayPoint,
 4345        goal_column: u32,
 4346        display_map: &DisplaySnapshot,
 4347        window: &mut Window,
 4348        cx: &mut Context<Self>,
 4349    ) {
 4350        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4351            return;
 4352        };
 4353
 4354        let tail = match columnar_state {
 4355            ColumnarSelectionState::FromMouse {
 4356                selection_tail,
 4357                display_point,
 4358            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4359            ColumnarSelectionState::FromSelection { selection_tail } => {
 4360                selection_tail.to_display_point(display_map)
 4361            }
 4362        };
 4363
 4364        let start_row = cmp::min(tail.row(), head.row());
 4365        let end_row = cmp::max(tail.row(), head.row());
 4366        let start_column = cmp::min(tail.column(), goal_column);
 4367        let end_column = cmp::max(tail.column(), goal_column);
 4368        let reversed = start_column < tail.column();
 4369
 4370        let selection_ranges = (start_row.0..=end_row.0)
 4371            .map(DisplayRow)
 4372            .filter_map(|row| {
 4373                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4374                    || start_column <= display_map.line_len(row))
 4375                    && !display_map.is_block_line(row)
 4376                {
 4377                    let start = display_map
 4378                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4379                        .to_point(display_map);
 4380                    let end = display_map
 4381                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4382                        .to_point(display_map);
 4383                    if reversed {
 4384                        Some(end..start)
 4385                    } else {
 4386                        Some(start..end)
 4387                    }
 4388                } else {
 4389                    None
 4390                }
 4391            })
 4392            .collect::<Vec<_>>();
 4393        if selection_ranges.is_empty() {
 4394            return;
 4395        }
 4396
 4397        let ranges = match columnar_state {
 4398            ColumnarSelectionState::FromMouse { .. } => {
 4399                let mut non_empty_ranges = selection_ranges
 4400                    .iter()
 4401                    .filter(|selection_range| selection_range.start != selection_range.end)
 4402                    .peekable();
 4403                if non_empty_ranges.peek().is_some() {
 4404                    non_empty_ranges.cloned().collect()
 4405                } else {
 4406                    selection_ranges
 4407                }
 4408            }
 4409            _ => selection_ranges,
 4410        };
 4411
 4412        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4413            s.select_ranges(ranges);
 4414        });
 4415        cx.notify();
 4416    }
 4417
 4418    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4419        self.selections
 4420            .all_adjusted(snapshot)
 4421            .iter()
 4422            .any(|selection| !selection.is_empty())
 4423    }
 4424
 4425    pub fn has_pending_nonempty_selection(&self) -> bool {
 4426        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4427            Some(Selection { start, end, .. }) => start != end,
 4428            None => false,
 4429        };
 4430
 4431        pending_nonempty_selection
 4432            || (self.columnar_selection_state.is_some()
 4433                && self.selections.disjoint_anchors().len() > 1)
 4434    }
 4435
 4436    pub fn has_pending_selection(&self) -> bool {
 4437        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4438    }
 4439
 4440    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4441        self.selection_mark_mode = false;
 4442        self.selection_drag_state = SelectionDragState::None;
 4443
 4444        if self.dismiss_menus_and_popups(true, window, cx) {
 4445            cx.notify();
 4446            return;
 4447        }
 4448        if self.clear_expanded_diff_hunks(cx) {
 4449            cx.notify();
 4450            return;
 4451        }
 4452        if self.show_git_blame_gutter {
 4453            self.show_git_blame_gutter = false;
 4454            cx.notify();
 4455            return;
 4456        }
 4457
 4458        if self.mode.is_full()
 4459            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4460        {
 4461            cx.notify();
 4462            return;
 4463        }
 4464
 4465        cx.propagate();
 4466    }
 4467
 4468    pub fn dismiss_menus_and_popups(
 4469        &mut self,
 4470        is_user_requested: bool,
 4471        window: &mut Window,
 4472        cx: &mut Context<Self>,
 4473    ) -> bool {
 4474        let mut dismissed = false;
 4475
 4476        dismissed |= self.take_rename(false, window, cx).is_some();
 4477        dismissed |= self.hide_blame_popover(true, cx);
 4478        dismissed |= hide_hover(self, cx);
 4479        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4480        dismissed |= self.hide_context_menu(window, cx).is_some();
 4481        dismissed |= self.mouse_context_menu.take().is_some();
 4482        dismissed |= is_user_requested
 4483            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4484        dismissed |= self.snippet_stack.pop().is_some();
 4485        if self.diff_review_drag_state.is_some() {
 4486            self.cancel_diff_review_drag(cx);
 4487            dismissed = true;
 4488        }
 4489        if !self.diff_review_overlays.is_empty() {
 4490            self.dismiss_all_diff_review_overlays(cx);
 4491            dismissed = true;
 4492        }
 4493
 4494        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4495            self.dismiss_diagnostics(cx);
 4496            dismissed = true;
 4497        }
 4498
 4499        dismissed
 4500    }
 4501
 4502    fn linked_editing_ranges_for(
 4503        &self,
 4504        selection: Range<text::Anchor>,
 4505        cx: &App,
 4506    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4507        if self.linked_edit_ranges.is_empty() {
 4508            return None;
 4509        }
 4510        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4511            selection.end.buffer_id.and_then(|end_buffer_id| {
 4512                if selection.start.buffer_id != Some(end_buffer_id) {
 4513                    return None;
 4514                }
 4515                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4516                let snapshot = buffer.read(cx).snapshot();
 4517                self.linked_edit_ranges
 4518                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4519                    .map(|ranges| (ranges, snapshot, buffer))
 4520            })?;
 4521        use text::ToOffset as TO;
 4522        // find offset from the start of current range to current cursor position
 4523        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4524
 4525        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4526        let start_difference = start_offset - start_byte_offset;
 4527        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4528        let end_difference = end_offset - start_byte_offset;
 4529
 4530        // Current range has associated linked ranges.
 4531        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4532        for range in linked_ranges.iter() {
 4533            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4534            let end_offset = start_offset + end_difference;
 4535            let start_offset = start_offset + start_difference;
 4536            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4537                continue;
 4538            }
 4539            if self.selections.disjoint_anchor_ranges().any(|s| {
 4540                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4541                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4542                {
 4543                    return false;
 4544                }
 4545                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4546                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4547            }) {
 4548                continue;
 4549            }
 4550            let start = buffer_snapshot.anchor_after(start_offset);
 4551            let end = buffer_snapshot.anchor_after(end_offset);
 4552            linked_edits
 4553                .entry(buffer.clone())
 4554                .or_default()
 4555                .push(start..end);
 4556        }
 4557        Some(linked_edits)
 4558    }
 4559
 4560    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4561        let text: Arc<str> = text.into();
 4562
 4563        if self.read_only(cx) {
 4564            return;
 4565        }
 4566
 4567        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4568
 4569        self.unfold_buffers_with_selections(cx);
 4570
 4571        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4572        let mut bracket_inserted = false;
 4573        let mut edits = Vec::new();
 4574        let mut linked_edits = LinkedEdits::new();
 4575        let mut new_selections = Vec::with_capacity(selections.len());
 4576        let mut new_autoclose_regions = Vec::new();
 4577        let snapshot = self.buffer.read(cx).read(cx);
 4578        let mut clear_linked_edit_ranges = false;
 4579        let mut all_selections_read_only = true;
 4580        let mut has_adjacent_edits = false;
 4581        let mut in_adjacent_group = false;
 4582
 4583        let mut regions = self
 4584            .selections_with_autoclose_regions(selections, &snapshot)
 4585            .peekable();
 4586
 4587        while let Some((selection, autoclose_region)) = regions.next() {
 4588            if snapshot
 4589                .point_to_buffer_point(selection.head())
 4590                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4591            {
 4592                continue;
 4593            }
 4594            if snapshot
 4595                .point_to_buffer_point(selection.tail())
 4596                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4597            {
 4598                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4599                continue;
 4600            }
 4601            all_selections_read_only = false;
 4602
 4603            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4604                // Determine if the inserted text matches the opening or closing
 4605                // bracket of any of this language's bracket pairs.
 4606                let mut bracket_pair = None;
 4607                let mut is_bracket_pair_start = false;
 4608                let mut is_bracket_pair_end = false;
 4609                if !text.is_empty() {
 4610                    let mut bracket_pair_matching_end = None;
 4611                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4612                    //  and they are removing the character that triggered IME popup.
 4613                    for (pair, enabled) in scope.brackets() {
 4614                        if !pair.close && !pair.surround {
 4615                            continue;
 4616                        }
 4617
 4618                        if enabled && pair.start.ends_with(text.as_ref()) {
 4619                            let prefix_len = pair.start.len() - text.len();
 4620                            let preceding_text_matches_prefix = prefix_len == 0
 4621                                || (selection.start.column >= (prefix_len as u32)
 4622                                    && snapshot.contains_str_at(
 4623                                        Point::new(
 4624                                            selection.start.row,
 4625                                            selection.start.column - (prefix_len as u32),
 4626                                        ),
 4627                                        &pair.start[..prefix_len],
 4628                                    ));
 4629                            if preceding_text_matches_prefix {
 4630                                bracket_pair = Some(pair.clone());
 4631                                is_bracket_pair_start = true;
 4632                                break;
 4633                            }
 4634                        }
 4635                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4636                        {
 4637                            // take first bracket pair matching end, but don't break in case a later bracket
 4638                            // pair matches start
 4639                            bracket_pair_matching_end = Some(pair.clone());
 4640                        }
 4641                    }
 4642                    if let Some(end) = bracket_pair_matching_end
 4643                        && bracket_pair.is_none()
 4644                    {
 4645                        bracket_pair = Some(end);
 4646                        is_bracket_pair_end = true;
 4647                    }
 4648                }
 4649
 4650                if let Some(bracket_pair) = bracket_pair {
 4651                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4652                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4653                    let auto_surround =
 4654                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4655                    if selection.is_empty() {
 4656                        if is_bracket_pair_start {
 4657                            // If the inserted text is a suffix of an opening bracket and the
 4658                            // selection is preceded by the rest of the opening bracket, then
 4659                            // insert the closing bracket.
 4660                            let following_text_allows_autoclose = snapshot
 4661                                .chars_at(selection.start)
 4662                                .next()
 4663                                .is_none_or(|c| scope.should_autoclose_before(c));
 4664
 4665                            let preceding_text_allows_autoclose = selection.start.column == 0
 4666                                || snapshot
 4667                                    .reversed_chars_at(selection.start)
 4668                                    .next()
 4669                                    .is_none_or(|c| {
 4670                                        bracket_pair.start != bracket_pair.end
 4671                                            || !snapshot
 4672                                                .char_classifier_at(selection.start)
 4673                                                .is_word(c)
 4674                                    });
 4675
 4676                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4677                                && bracket_pair.start.len() == 1
 4678                            {
 4679                                let target = bracket_pair.start.chars().next().unwrap();
 4680                                let mut byte_offset = 0u32;
 4681                                let current_line_count = snapshot
 4682                                    .reversed_chars_at(selection.start)
 4683                                    .take_while(|&c| c != '\n')
 4684                                    .filter(|c| {
 4685                                        byte_offset += c.len_utf8() as u32;
 4686                                        if *c != target {
 4687                                            return false;
 4688                                        }
 4689
 4690                                        let point = Point::new(
 4691                                            selection.start.row,
 4692                                            selection.start.column.saturating_sub(byte_offset),
 4693                                        );
 4694
 4695                                        let is_enabled = snapshot
 4696                                            .language_scope_at(point)
 4697                                            .and_then(|scope| {
 4698                                                scope
 4699                                                    .brackets()
 4700                                                    .find(|(pair, _)| {
 4701                                                        pair.start == bracket_pair.start
 4702                                                    })
 4703                                                    .map(|(_, enabled)| enabled)
 4704                                            })
 4705                                            .unwrap_or(true);
 4706
 4707                                        let is_delimiter = snapshot
 4708                                            .language_scope_at(Point::new(
 4709                                                point.row,
 4710                                                point.column + 1,
 4711                                            ))
 4712                                            .and_then(|scope| {
 4713                                                scope
 4714                                                    .brackets()
 4715                                                    .find(|(pair, _)| {
 4716                                                        pair.start == bracket_pair.start
 4717                                                    })
 4718                                                    .map(|(_, enabled)| !enabled)
 4719                                            })
 4720                                            .unwrap_or(false);
 4721
 4722                                        is_enabled && !is_delimiter
 4723                                    })
 4724                                    .count();
 4725                                current_line_count % 2 == 1
 4726                            } else {
 4727                                false
 4728                            };
 4729
 4730                            if autoclose
 4731                                && bracket_pair.close
 4732                                && following_text_allows_autoclose
 4733                                && preceding_text_allows_autoclose
 4734                                && !is_closing_quote
 4735                            {
 4736                                let anchor = snapshot.anchor_before(selection.end);
 4737                                new_selections.push((selection.map(|_| anchor), text.len()));
 4738                                new_autoclose_regions.push((
 4739                                    anchor,
 4740                                    text.len(),
 4741                                    selection.id,
 4742                                    bracket_pair.clone(),
 4743                                ));
 4744                                edits.push((
 4745                                    selection.range(),
 4746                                    format!("{}{}", text, bracket_pair.end).into(),
 4747                                ));
 4748                                bracket_inserted = true;
 4749                                continue;
 4750                            }
 4751                        }
 4752
 4753                        if let Some(region) = autoclose_region {
 4754                            // If the selection is followed by an auto-inserted closing bracket,
 4755                            // then don't insert that closing bracket again; just move the selection
 4756                            // past the closing bracket.
 4757                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4758                                && text.as_ref() == region.pair.end.as_str()
 4759                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4760                            if should_skip {
 4761                                let anchor = snapshot.anchor_after(selection.end);
 4762                                new_selections
 4763                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4764                                continue;
 4765                            }
 4766                        }
 4767
 4768                        let always_treat_brackets_as_autoclosed = snapshot
 4769                            .language_settings_at(selection.start, cx)
 4770                            .always_treat_brackets_as_autoclosed;
 4771                        if always_treat_brackets_as_autoclosed
 4772                            && is_bracket_pair_end
 4773                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4774                        {
 4775                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4776                            // and the inserted text is a closing bracket and the selection is followed
 4777                            // by the closing bracket then move the selection past the closing bracket.
 4778                            let anchor = snapshot.anchor_after(selection.end);
 4779                            new_selections.push((selection.map(|_| anchor), text.len()));
 4780                            continue;
 4781                        }
 4782                    }
 4783                    // If an opening bracket is 1 character long and is typed while
 4784                    // text is selected, then surround that text with the bracket pair.
 4785                    else if auto_surround
 4786                        && bracket_pair.surround
 4787                        && is_bracket_pair_start
 4788                        && bracket_pair.start.chars().count() == 1
 4789                    {
 4790                        edits.push((selection.start..selection.start, text.clone()));
 4791                        edits.push((
 4792                            selection.end..selection.end,
 4793                            bracket_pair.end.as_str().into(),
 4794                        ));
 4795                        bracket_inserted = true;
 4796                        new_selections.push((
 4797                            Selection {
 4798                                id: selection.id,
 4799                                start: snapshot.anchor_after(selection.start),
 4800                                end: snapshot.anchor_before(selection.end),
 4801                                reversed: selection.reversed,
 4802                                goal: selection.goal,
 4803                            },
 4804                            0,
 4805                        ));
 4806                        continue;
 4807                    }
 4808                }
 4809            }
 4810
 4811            if self.auto_replace_emoji_shortcode
 4812                && selection.is_empty()
 4813                && text.as_ref().ends_with(':')
 4814                && let Some(possible_emoji_short_code) =
 4815                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4816                && !possible_emoji_short_code.is_empty()
 4817                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4818            {
 4819                let emoji_shortcode_start = Point::new(
 4820                    selection.start.row,
 4821                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4822                );
 4823
 4824                // Remove shortcode from buffer
 4825                edits.push((
 4826                    emoji_shortcode_start..selection.start,
 4827                    "".to_string().into(),
 4828                ));
 4829                new_selections.push((
 4830                    Selection {
 4831                        id: selection.id,
 4832                        start: snapshot.anchor_after(emoji_shortcode_start),
 4833                        end: snapshot.anchor_before(selection.start),
 4834                        reversed: selection.reversed,
 4835                        goal: selection.goal,
 4836                    },
 4837                    0,
 4838                ));
 4839
 4840                // Insert emoji
 4841                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4842                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4843                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4844
 4845                continue;
 4846            }
 4847
 4848            let next_is_adjacent = regions
 4849                .peek()
 4850                .is_some_and(|(next, _)| selection.end == next.start);
 4851
 4852            // If not handling any auto-close operation, then just replace the selected
 4853            // text with the given input and move the selection to the end of the
 4854            // newly inserted text.
 4855            let anchor = if in_adjacent_group || next_is_adjacent {
 4856                // After edits the right bias would shift those anchor to the next visible fragment
 4857                // but we want to resolve to the previous one
 4858                snapshot.anchor_before(selection.end)
 4859            } else {
 4860                snapshot.anchor_after(selection.end)
 4861            };
 4862
 4863            if !self.linked_edit_ranges.is_empty() {
 4864                let start_anchor = snapshot.anchor_before(selection.start);
 4865
 4866                let is_word_char = text.chars().next().is_none_or(|char| {
 4867                    let classifier = snapshot
 4868                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4869                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4870                    classifier.is_word(char)
 4871                });
 4872                let is_dot = text.as_ref() == ".";
 4873                let should_apply_linked_edit = is_word_char || is_dot;
 4874
 4875                if should_apply_linked_edit {
 4876                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4877                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4878                } else {
 4879                    clear_linked_edit_ranges = true;
 4880                }
 4881            }
 4882
 4883            new_selections.push((selection.map(|_| anchor), 0));
 4884            edits.push((selection.start..selection.end, text.clone()));
 4885
 4886            has_adjacent_edits |= next_is_adjacent;
 4887            in_adjacent_group = next_is_adjacent;
 4888        }
 4889
 4890        if all_selections_read_only {
 4891            return;
 4892        }
 4893
 4894        drop(regions);
 4895        drop(snapshot);
 4896
 4897        self.transact(window, cx, |this, window, cx| {
 4898            if clear_linked_edit_ranges {
 4899                this.linked_edit_ranges.clear();
 4900            }
 4901            let initial_buffer_versions =
 4902                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4903
 4904            this.buffer.update(cx, |buffer, cx| {
 4905                if has_adjacent_edits {
 4906                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4907                } else {
 4908                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4909                }
 4910            });
 4911            linked_edits.apply(cx);
 4912            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4913            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4914            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4915            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4916                new_anchor_selections,
 4917                &map,
 4918            )
 4919            .zip(new_selection_deltas)
 4920            .map(|(selection, delta)| Selection {
 4921                id: selection.id,
 4922                start: selection.start + delta,
 4923                end: selection.end + delta,
 4924                reversed: selection.reversed,
 4925                goal: SelectionGoal::None,
 4926            })
 4927            .collect::<Vec<_>>();
 4928
 4929            let mut i = 0;
 4930            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4931                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4932                let start = map.buffer_snapshot().anchor_before(position);
 4933                let end = map.buffer_snapshot().anchor_after(position);
 4934                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4935                    match existing_state
 4936                        .range
 4937                        .start
 4938                        .cmp(&start, map.buffer_snapshot())
 4939                    {
 4940                        Ordering::Less => i += 1,
 4941                        Ordering::Greater => break,
 4942                        Ordering::Equal => {
 4943                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4944                                Ordering::Less => i += 1,
 4945                                Ordering::Equal => break,
 4946                                Ordering::Greater => break,
 4947                            }
 4948                        }
 4949                    }
 4950                }
 4951                this.autoclose_regions.insert(
 4952                    i,
 4953                    AutocloseRegion {
 4954                        selection_id,
 4955                        range: start..end,
 4956                        pair,
 4957                    },
 4958                );
 4959            }
 4960
 4961            let had_active_edit_prediction = this.has_active_edit_prediction();
 4962            this.change_selections(
 4963                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4964                window,
 4965                cx,
 4966                |s| s.select(new_selections),
 4967            );
 4968
 4969            if !bracket_inserted
 4970                && let Some(on_type_format_task) =
 4971                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 4972            {
 4973                on_type_format_task.detach_and_log_err(cx);
 4974            }
 4975
 4976            let editor_settings = EditorSettings::get_global(cx);
 4977            if bracket_inserted
 4978                && (editor_settings.auto_signature_help
 4979                    || editor_settings.show_signature_help_after_edits)
 4980            {
 4981                this.show_signature_help(&ShowSignatureHelp, window, cx);
 4982            }
 4983
 4984            let trigger_in_words =
 4985                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 4986            if this.hard_wrap.is_some() {
 4987                let latest: Range<Point> = this.selections.newest(&map).range();
 4988                if latest.is_empty()
 4989                    && this
 4990                        .buffer()
 4991                        .read(cx)
 4992                        .snapshot(cx)
 4993                        .line_len(MultiBufferRow(latest.start.row))
 4994                        == latest.start.column
 4995                {
 4996                    this.rewrap_impl(
 4997                        RewrapOptions {
 4998                            override_language_settings: true,
 4999                            preserve_existing_whitespace: true,
 5000                        },
 5001                        cx,
 5002                    )
 5003                }
 5004            }
 5005            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5006            refresh_linked_ranges(this, window, cx);
 5007            this.refresh_edit_prediction(true, false, window, cx);
 5008            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5009        });
 5010    }
 5011
 5012    fn find_possible_emoji_shortcode_at_position(
 5013        snapshot: &MultiBufferSnapshot,
 5014        position: Point,
 5015    ) -> Option<String> {
 5016        let mut chars = Vec::new();
 5017        let mut found_colon = false;
 5018        for char in snapshot.reversed_chars_at(position).take(100) {
 5019            // Found a possible emoji shortcode in the middle of the buffer
 5020            if found_colon {
 5021                if char.is_whitespace() {
 5022                    chars.reverse();
 5023                    return Some(chars.iter().collect());
 5024                }
 5025                // If the previous character is not a whitespace, we are in the middle of a word
 5026                // and we only want to complete the shortcode if the word is made up of other emojis
 5027                let mut containing_word = String::new();
 5028                for ch in snapshot
 5029                    .reversed_chars_at(position)
 5030                    .skip(chars.len() + 1)
 5031                    .take(100)
 5032                {
 5033                    if ch.is_whitespace() {
 5034                        break;
 5035                    }
 5036                    containing_word.push(ch);
 5037                }
 5038                let containing_word = containing_word.chars().rev().collect::<String>();
 5039                if util::word_consists_of_emojis(containing_word.as_str()) {
 5040                    chars.reverse();
 5041                    return Some(chars.iter().collect());
 5042                }
 5043            }
 5044
 5045            if char.is_whitespace() || !char.is_ascii() {
 5046                return None;
 5047            }
 5048            if char == ':' {
 5049                found_colon = true;
 5050            } else {
 5051                chars.push(char);
 5052            }
 5053        }
 5054        // Found a possible emoji shortcode at the beginning of the buffer
 5055        chars.reverse();
 5056        Some(chars.iter().collect())
 5057    }
 5058
 5059    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5060        if self.read_only(cx) {
 5061            return;
 5062        }
 5063
 5064        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5065        self.transact(window, cx, |this, window, cx| {
 5066            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5067                let selections = this
 5068                    .selections
 5069                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5070                let multi_buffer = this.buffer.read(cx);
 5071                let buffer = multi_buffer.snapshot(cx);
 5072                selections
 5073                    .iter()
 5074                    .map(|selection| {
 5075                        let start_point = selection.start.to_point(&buffer);
 5076                        let mut existing_indent =
 5077                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5078                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5079                        let start = selection.start;
 5080                        let end = selection.end;
 5081                        let selection_is_empty = start == end;
 5082                        let language_scope = buffer.language_scope_at(start);
 5083                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5084                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5085                                &buffer,
 5086                                start..end,
 5087                                language,
 5088                            )
 5089                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5090                                    &buffer,
 5091                                    start..end,
 5092                                );
 5093
 5094                            let mut newline_config = NewlineConfig::Newline {
 5095                                additional_indent: IndentSize::spaces(0),
 5096                                extra_line_additional_indent: if needs_extra_newline {
 5097                                    Some(IndentSize::spaces(0))
 5098                                } else {
 5099                                    None
 5100                                },
 5101                                prevent_auto_indent: false,
 5102                            };
 5103
 5104                            let comment_delimiter = maybe!({
 5105                                if !selection_is_empty {
 5106                                    return None;
 5107                                }
 5108
 5109                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5110                                    return None;
 5111                                }
 5112
 5113                                return comment_delimiter_for_newline(
 5114                                    &start_point,
 5115                                    &buffer,
 5116                                    language,
 5117                                );
 5118                            });
 5119
 5120                            let doc_delimiter = maybe!({
 5121                                if !selection_is_empty {
 5122                                    return None;
 5123                                }
 5124
 5125                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5126                                    return None;
 5127                                }
 5128
 5129                                return documentation_delimiter_for_newline(
 5130                                    &start_point,
 5131                                    &buffer,
 5132                                    language,
 5133                                    &mut newline_config,
 5134                                );
 5135                            });
 5136
 5137                            let list_delimiter = maybe!({
 5138                                if !selection_is_empty {
 5139                                    return None;
 5140                                }
 5141
 5142                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5143                                    return None;
 5144                                }
 5145
 5146                                return list_delimiter_for_newline(
 5147                                    &start_point,
 5148                                    &buffer,
 5149                                    language,
 5150                                    &mut newline_config,
 5151                                );
 5152                            });
 5153
 5154                            (
 5155                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5156                                newline_config,
 5157                            )
 5158                        } else {
 5159                            (
 5160                                None,
 5161                                NewlineConfig::Newline {
 5162                                    additional_indent: IndentSize::spaces(0),
 5163                                    extra_line_additional_indent: None,
 5164                                    prevent_auto_indent: false,
 5165                                },
 5166                            )
 5167                        };
 5168
 5169                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5170                            NewlineConfig::ClearCurrentLine => {
 5171                                let row_start =
 5172                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5173                                (row_start, String::new(), false)
 5174                            }
 5175                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5176                                let row_start =
 5177                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5178                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5179                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5180                                let reduced_indent =
 5181                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5182                                let mut new_text = String::new();
 5183                                new_text.extend(reduced_indent.chars());
 5184                                new_text.push_str(continuation);
 5185                                (row_start, new_text, true)
 5186                            }
 5187                            NewlineConfig::Newline {
 5188                                additional_indent,
 5189                                extra_line_additional_indent,
 5190                                prevent_auto_indent,
 5191                            } => {
 5192                                let auto_indent_mode =
 5193                                    buffer.language_settings_at(start, cx).auto_indent;
 5194                                let preserve_indent =
 5195                                    auto_indent_mode != language::AutoIndentMode::None;
 5196                                let apply_syntax_indent =
 5197                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5198                                let capacity_for_delimiter =
 5199                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5200                                let existing_indent_len = if preserve_indent {
 5201                                    existing_indent.len as usize
 5202                                } else {
 5203                                    0
 5204                                };
 5205                                let extra_line_len = extra_line_additional_indent
 5206                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5207                                    .unwrap_or(0);
 5208                                let mut new_text = String::with_capacity(
 5209                                    1 + capacity_for_delimiter
 5210                                        + existing_indent_len
 5211                                        + additional_indent.len as usize
 5212                                        + extra_line_len,
 5213                                );
 5214                                new_text.push('\n');
 5215                                if preserve_indent {
 5216                                    new_text.extend(existing_indent.chars());
 5217                                }
 5218                                new_text.extend(additional_indent.chars());
 5219                                if let Some(delimiter) = &delimiter {
 5220                                    new_text.push_str(delimiter);
 5221                                }
 5222                                if let Some(extra_indent) = extra_line_additional_indent {
 5223                                    new_text.push('\n');
 5224                                    if preserve_indent {
 5225                                        new_text.extend(existing_indent.chars());
 5226                                    }
 5227                                    new_text.extend(extra_indent.chars());
 5228                                }
 5229                                (
 5230                                    start,
 5231                                    new_text,
 5232                                    *prevent_auto_indent || !apply_syntax_indent,
 5233                                )
 5234                            }
 5235                        };
 5236
 5237                        let anchor = buffer.anchor_after(end);
 5238                        let new_selection = selection.map(|_| anchor);
 5239                        (
 5240                            ((edit_start..end, new_text), prevent_auto_indent),
 5241                            (newline_config.has_extra_line(), new_selection),
 5242                        )
 5243                    })
 5244                    .unzip()
 5245            };
 5246
 5247            let mut auto_indent_edits = Vec::new();
 5248            let mut edits = Vec::new();
 5249            for (edit, prevent_auto_indent) in edits_with_flags {
 5250                if prevent_auto_indent {
 5251                    edits.push(edit);
 5252                } else {
 5253                    auto_indent_edits.push(edit);
 5254                }
 5255            }
 5256            if !edits.is_empty() {
 5257                this.edit(edits, cx);
 5258            }
 5259            if !auto_indent_edits.is_empty() {
 5260                this.edit_with_autoindent(auto_indent_edits, cx);
 5261            }
 5262
 5263            let buffer = this.buffer.read(cx).snapshot(cx);
 5264            let new_selections = selection_info
 5265                .into_iter()
 5266                .map(|(extra_newline_inserted, new_selection)| {
 5267                    let mut cursor = new_selection.end.to_point(&buffer);
 5268                    if extra_newline_inserted {
 5269                        cursor.row -= 1;
 5270                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5271                    }
 5272                    new_selection.map(|_| cursor)
 5273                })
 5274                .collect();
 5275
 5276            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5277            this.refresh_edit_prediction(true, false, window, cx);
 5278            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5279                task.detach_and_log_err(cx);
 5280            }
 5281        });
 5282    }
 5283
 5284    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5285        if self.read_only(cx) {
 5286            return;
 5287        }
 5288
 5289        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5290
 5291        let buffer = self.buffer.read(cx);
 5292        let snapshot = buffer.snapshot(cx);
 5293
 5294        let mut edits = Vec::new();
 5295        let mut rows = Vec::new();
 5296
 5297        for (rows_inserted, selection) in self
 5298            .selections
 5299            .all_adjusted(&self.display_snapshot(cx))
 5300            .into_iter()
 5301            .enumerate()
 5302        {
 5303            let cursor = selection.head();
 5304            let row = cursor.row;
 5305
 5306            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5307
 5308            let newline = "\n".to_string();
 5309            edits.push((start_of_line..start_of_line, newline));
 5310
 5311            rows.push(row + rows_inserted as u32);
 5312        }
 5313
 5314        self.transact(window, cx, |editor, window, cx| {
 5315            editor.edit(edits, cx);
 5316
 5317            editor.change_selections(Default::default(), window, cx, |s| {
 5318                let mut index = 0;
 5319                s.move_cursors_with(&mut |map, _, _| {
 5320                    let row = rows[index];
 5321                    index += 1;
 5322
 5323                    let point = Point::new(row, 0);
 5324                    let boundary = map.next_line_boundary(point).1;
 5325                    let clipped = map.clip_point(boundary, Bias::Left);
 5326
 5327                    (clipped, SelectionGoal::None)
 5328                });
 5329            });
 5330
 5331            let mut indent_edits = Vec::new();
 5332            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5333            for row in rows {
 5334                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5335                for (row, indent) in indents {
 5336                    if indent.len == 0 {
 5337                        continue;
 5338                    }
 5339
 5340                    let text = match indent.kind {
 5341                        IndentKind::Space => " ".repeat(indent.len as usize),
 5342                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5343                    };
 5344                    let point = Point::new(row.0, 0);
 5345                    indent_edits.push((point..point, text));
 5346                }
 5347            }
 5348            editor.edit(indent_edits, cx);
 5349            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5350                format.detach_and_log_err(cx);
 5351            }
 5352        });
 5353    }
 5354
 5355    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5356        if self.read_only(cx) {
 5357            return;
 5358        }
 5359
 5360        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5361
 5362        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5363        let mut rows = Vec::new();
 5364        let mut rows_inserted = 0;
 5365
 5366        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5367            let cursor = selection.head();
 5368            let row = cursor.row;
 5369
 5370            let point = Point::new(row, 0);
 5371            let Some((buffer_handle, buffer_point, _)) =
 5372                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5373            else {
 5374                continue;
 5375            };
 5376
 5377            buffer_edits
 5378                .entry(buffer_handle.entity_id())
 5379                .or_insert_with(|| (buffer_handle, Vec::new()))
 5380                .1
 5381                .push(buffer_point);
 5382
 5383            rows_inserted += 1;
 5384            rows.push(row + rows_inserted);
 5385        }
 5386
 5387        self.transact(window, cx, |editor, window, cx| {
 5388            for (_, (buffer_handle, points)) in &buffer_edits {
 5389                buffer_handle.update(cx, |buffer, cx| {
 5390                    let edits: Vec<_> = points
 5391                        .iter()
 5392                        .map(|point| {
 5393                            let target = Point::new(point.row + 1, 0);
 5394                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5395                            (start_of_line..start_of_line, "\n")
 5396                        })
 5397                        .collect();
 5398                    buffer.edit(edits, None, cx);
 5399                });
 5400            }
 5401
 5402            editor.change_selections(Default::default(), window, cx, |s| {
 5403                let mut index = 0;
 5404                s.move_cursors_with(&mut |map, _, _| {
 5405                    let row = rows[index];
 5406                    index += 1;
 5407
 5408                    let point = Point::new(row, 0);
 5409                    let boundary = map.next_line_boundary(point).1;
 5410                    let clipped = map.clip_point(boundary, Bias::Left);
 5411
 5412                    (clipped, SelectionGoal::None)
 5413                });
 5414            });
 5415
 5416            let mut indent_edits = Vec::new();
 5417            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5418            for row in rows {
 5419                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5420                for (row, indent) in indents {
 5421                    if indent.len == 0 {
 5422                        continue;
 5423                    }
 5424
 5425                    let text = match indent.kind {
 5426                        IndentKind::Space => " ".repeat(indent.len as usize),
 5427                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5428                    };
 5429                    let point = Point::new(row.0, 0);
 5430                    indent_edits.push((point..point, text));
 5431                }
 5432            }
 5433            editor.edit(indent_edits, cx);
 5434            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5435                format.detach_and_log_err(cx);
 5436            }
 5437        });
 5438    }
 5439
 5440    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5441        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5442            original_indent_columns: Vec::new(),
 5443        });
 5444        self.replace_selections(text, autoindent, window, cx, false);
 5445    }
 5446
 5447    /// Replaces the editor's selections with the provided `text`, applying the
 5448    /// given `autoindent_mode` (`None` will skip autoindentation).
 5449    ///
 5450    /// Early returns if the editor is in read-only mode, without applying any
 5451    /// edits.
 5452    fn replace_selections(
 5453        &mut self,
 5454        text: &str,
 5455        autoindent_mode: Option<AutoindentMode>,
 5456        window: &mut Window,
 5457        cx: &mut Context<Self>,
 5458        apply_linked_edits: bool,
 5459    ) {
 5460        if self.read_only(cx) {
 5461            return;
 5462        }
 5463
 5464        let text: Arc<str> = text.into();
 5465        self.transact(window, cx, |this, window, cx| {
 5466            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5467            let linked_edits = if apply_linked_edits {
 5468                this.linked_edits_for_selections(text.clone(), cx)
 5469            } else {
 5470                LinkedEdits::new()
 5471            };
 5472
 5473            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5474                let anchors = {
 5475                    let snapshot = buffer.read(cx);
 5476                    old_selections
 5477                        .iter()
 5478                        .map(|s| {
 5479                            let anchor = snapshot.anchor_after(s.head());
 5480                            s.map(|_| anchor)
 5481                        })
 5482                        .collect::<Vec<_>>()
 5483                };
 5484                buffer.edit(
 5485                    old_selections
 5486                        .iter()
 5487                        .map(|s| (s.start..s.end, text.clone())),
 5488                    autoindent_mode,
 5489                    cx,
 5490                );
 5491                anchors
 5492            });
 5493
 5494            linked_edits.apply(cx);
 5495
 5496            this.change_selections(Default::default(), window, cx, |s| {
 5497                s.select_anchors(selection_anchors);
 5498            });
 5499
 5500            if apply_linked_edits {
 5501                refresh_linked_ranges(this, window, cx);
 5502            }
 5503
 5504            cx.notify();
 5505        });
 5506    }
 5507
 5508    /// Collects linked edits for the current selections, pairing each linked
 5509    /// range with `text`.
 5510    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5511        let mut linked_edits = LinkedEdits::new();
 5512        if !self.linked_edit_ranges.is_empty() {
 5513            for selection in self.selections.disjoint_anchors() {
 5514                let start = selection.start.text_anchor;
 5515                let end = selection.end.text_anchor;
 5516                linked_edits.push(self, start..end, text.clone(), cx);
 5517            }
 5518        }
 5519        linked_edits
 5520    }
 5521
 5522    /// Deletes the content covered by the current selections and applies
 5523    /// linked edits.
 5524    pub fn delete_selections_with_linked_edits(
 5525        &mut self,
 5526        window: &mut Window,
 5527        cx: &mut Context<Self>,
 5528    ) {
 5529        self.replace_selections("", None, window, cx, true);
 5530    }
 5531
 5532    #[cfg(any(test, feature = "test-support"))]
 5533    pub fn set_linked_edit_ranges_for_testing(
 5534        &mut self,
 5535        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5536        cx: &mut Context<Self>,
 5537    ) -> Option<()> {
 5538        let Some((buffer, _)) = self
 5539            .buffer
 5540            .read(cx)
 5541            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5542        else {
 5543            return None;
 5544        };
 5545        let buffer = buffer.read(cx);
 5546        let buffer_id = buffer.remote_id();
 5547        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5548        for (base_range, linked_ranges_points) in ranges {
 5549            let base_anchor =
 5550                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5551            let linked_anchors = linked_ranges_points
 5552                .into_iter()
 5553                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5554                .collect();
 5555            linked_ranges.push((base_anchor, linked_anchors));
 5556        }
 5557        let mut map = HashMap::default();
 5558        map.insert(buffer_id, linked_ranges);
 5559        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5560        Some(())
 5561    }
 5562
 5563    fn trigger_completion_on_input(
 5564        &mut self,
 5565        text: &str,
 5566        trigger_in_words: bool,
 5567        window: &mut Window,
 5568        cx: &mut Context<Self>,
 5569    ) {
 5570        let completions_source = self
 5571            .context_menu
 5572            .borrow()
 5573            .as_ref()
 5574            .and_then(|menu| match menu {
 5575                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5576                CodeContextMenu::CodeActions(_) => None,
 5577            });
 5578
 5579        match completions_source {
 5580            Some(CompletionsMenuSource::Words { .. }) => {
 5581                self.open_or_update_completions_menu(
 5582                    Some(CompletionsMenuSource::Words {
 5583                        ignore_threshold: false,
 5584                    }),
 5585                    None,
 5586                    trigger_in_words,
 5587                    window,
 5588                    cx,
 5589                );
 5590            }
 5591            _ => self.open_or_update_completions_menu(
 5592                None,
 5593                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5594                true,
 5595                window,
 5596                cx,
 5597            ),
 5598        }
 5599    }
 5600
 5601    /// If any empty selections is touching the start of its innermost containing autoclose
 5602    /// region, expand it to select the brackets.
 5603    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5604        let selections = self
 5605            .selections
 5606            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5607        let buffer = self.buffer.read(cx).read(cx);
 5608        let new_selections = self
 5609            .selections_with_autoclose_regions(selections, &buffer)
 5610            .map(|(mut selection, region)| {
 5611                if !selection.is_empty() {
 5612                    return selection;
 5613                }
 5614
 5615                if let Some(region) = region {
 5616                    let mut range = region.range.to_offset(&buffer);
 5617                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5618                        range.start -= region.pair.start.len();
 5619                        if buffer.contains_str_at(range.start, &region.pair.start)
 5620                            && buffer.contains_str_at(range.end, &region.pair.end)
 5621                        {
 5622                            range.end += region.pair.end.len();
 5623                            selection.start = range.start;
 5624                            selection.end = range.end;
 5625
 5626                            return selection;
 5627                        }
 5628                    }
 5629                }
 5630
 5631                let always_treat_brackets_as_autoclosed = buffer
 5632                    .language_settings_at(selection.start, cx)
 5633                    .always_treat_brackets_as_autoclosed;
 5634
 5635                if !always_treat_brackets_as_autoclosed {
 5636                    return selection;
 5637                }
 5638
 5639                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5640                    for (pair, enabled) in scope.brackets() {
 5641                        if !enabled || !pair.close {
 5642                            continue;
 5643                        }
 5644
 5645                        if buffer.contains_str_at(selection.start, &pair.end) {
 5646                            let pair_start_len = pair.start.len();
 5647                            if buffer.contains_str_at(
 5648                                selection.start.saturating_sub_usize(pair_start_len),
 5649                                &pair.start,
 5650                            ) {
 5651                                selection.start -= pair_start_len;
 5652                                selection.end += pair.end.len();
 5653
 5654                                return selection;
 5655                            }
 5656                        }
 5657                    }
 5658                }
 5659
 5660                selection
 5661            })
 5662            .collect();
 5663
 5664        drop(buffer);
 5665        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5666            selections.select(new_selections)
 5667        });
 5668    }
 5669
 5670    /// Iterate the given selections, and for each one, find the smallest surrounding
 5671    /// autoclose region. This uses the ordering of the selections and the autoclose
 5672    /// regions to avoid repeated comparisons.
 5673    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5674        &'a self,
 5675        selections: impl IntoIterator<Item = Selection<D>>,
 5676        buffer: &'a MultiBufferSnapshot,
 5677    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5678        let mut i = 0;
 5679        let mut regions = self.autoclose_regions.as_slice();
 5680        selections.into_iter().map(move |selection| {
 5681            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5682
 5683            let mut enclosing = None;
 5684            while let Some(pair_state) = regions.get(i) {
 5685                if pair_state.range.end.to_offset(buffer) < range.start {
 5686                    regions = &regions[i + 1..];
 5687                    i = 0;
 5688                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5689                    break;
 5690                } else {
 5691                    if pair_state.selection_id == selection.id {
 5692                        enclosing = Some(pair_state);
 5693                    }
 5694                    i += 1;
 5695                }
 5696            }
 5697
 5698            (selection, enclosing)
 5699        })
 5700    }
 5701
 5702    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5703    fn invalidate_autoclose_regions(
 5704        &mut self,
 5705        mut selections: &[Selection<Anchor>],
 5706        buffer: &MultiBufferSnapshot,
 5707    ) {
 5708        self.autoclose_regions.retain(|state| {
 5709            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5710                return false;
 5711            }
 5712
 5713            let mut i = 0;
 5714            while let Some(selection) = selections.get(i) {
 5715                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5716                    selections = &selections[1..];
 5717                    continue;
 5718                }
 5719                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5720                    break;
 5721                }
 5722                if selection.id == state.selection_id {
 5723                    return true;
 5724                } else {
 5725                    i += 1;
 5726                }
 5727            }
 5728            false
 5729        });
 5730    }
 5731
 5732    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5733        let offset = position.to_offset(buffer);
 5734        let (word_range, kind) =
 5735            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5736        if offset > word_range.start && kind == Some(CharKind::Word) {
 5737            Some(
 5738                buffer
 5739                    .text_for_range(word_range.start..offset)
 5740                    .collect::<String>(),
 5741            )
 5742        } else {
 5743            None
 5744        }
 5745    }
 5746
 5747    pub fn visible_excerpts(
 5748        &self,
 5749        lsp_related_only: bool,
 5750        cx: &mut Context<Editor>,
 5751    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5752        let project = self.project().cloned();
 5753        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5754        let multi_buffer = self.buffer().read(cx);
 5755        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5756        multi_buffer_snapshot
 5757            .range_to_buffer_ranges(
 5758                self.multi_buffer_visible_range(&display_snapshot, cx)
 5759                    .to_inclusive(),
 5760            )
 5761            .into_iter()
 5762            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5763            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5764                if !lsp_related_only {
 5765                    return Some((
 5766                        excerpt_id,
 5767                        (
 5768                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5769                            buffer.version().clone(),
 5770                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5771                        ),
 5772                    ));
 5773                }
 5774
 5775                let project = project.as_ref()?.read(cx);
 5776                let buffer_file = project::File::from_dyn(buffer.file())?;
 5777                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5778                let worktree_entry = buffer_worktree
 5779                    .read(cx)
 5780                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5781                if worktree_entry.is_ignored {
 5782                    None
 5783                } else {
 5784                    Some((
 5785                        excerpt_id,
 5786                        (
 5787                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5788                            buffer.version().clone(),
 5789                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5790                        ),
 5791                    ))
 5792                }
 5793            })
 5794            .collect()
 5795    }
 5796
 5797    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5798        TextLayoutDetails {
 5799            text_system: window.text_system().clone(),
 5800            editor_style: self.style.clone().unwrap(),
 5801            rem_size: window.rem_size(),
 5802            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5803            visible_rows: self.visible_line_count(),
 5804            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5805        }
 5806    }
 5807
 5808    fn trigger_on_type_formatting(
 5809        &self,
 5810        input: String,
 5811        window: &mut Window,
 5812        cx: &mut Context<Self>,
 5813    ) -> Option<Task<Result<()>>> {
 5814        if input.chars().count() != 1 {
 5815            return None;
 5816        }
 5817
 5818        let project = self.project()?;
 5819        let position = self.selections.newest_anchor().head();
 5820        let (buffer, buffer_position) = self
 5821            .buffer
 5822            .read(cx)
 5823            .text_anchor_for_position(position, cx)?;
 5824
 5825        let settings = language_settings::language_settings(
 5826            buffer
 5827                .read(cx)
 5828                .language_at(buffer_position)
 5829                .map(|l| l.name()),
 5830            buffer.read(cx).file(),
 5831            cx,
 5832        );
 5833        if !settings.use_on_type_format {
 5834            return None;
 5835        }
 5836
 5837        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5838        // hence we do LSP request & edit on host side only — add formats to host's history.
 5839        let push_to_lsp_host_history = true;
 5840        // If this is not the host, append its history with new edits.
 5841        let push_to_client_history = project.read(cx).is_via_collab();
 5842
 5843        let on_type_formatting = project.update(cx, |project, cx| {
 5844            project.on_type_format(
 5845                buffer.clone(),
 5846                buffer_position,
 5847                input,
 5848                push_to_lsp_host_history,
 5849                cx,
 5850            )
 5851        });
 5852        Some(cx.spawn_in(window, async move |editor, cx| {
 5853            if let Some(transaction) = on_type_formatting.await? {
 5854                if push_to_client_history {
 5855                    buffer.update(cx, |buffer, _| {
 5856                        buffer.push_transaction(transaction, Instant::now());
 5857                        buffer.finalize_last_transaction();
 5858                    });
 5859                }
 5860                editor.update(cx, |editor, cx| {
 5861                    editor.refresh_document_highlights(cx);
 5862                })?;
 5863            }
 5864            Ok(())
 5865        }))
 5866    }
 5867
 5868    pub fn show_word_completions(
 5869        &mut self,
 5870        _: &ShowWordCompletions,
 5871        window: &mut Window,
 5872        cx: &mut Context<Self>,
 5873    ) {
 5874        self.open_or_update_completions_menu(
 5875            Some(CompletionsMenuSource::Words {
 5876                ignore_threshold: true,
 5877            }),
 5878            None,
 5879            false,
 5880            window,
 5881            cx,
 5882        );
 5883    }
 5884
 5885    pub fn show_completions(
 5886        &mut self,
 5887        _: &ShowCompletions,
 5888        window: &mut Window,
 5889        cx: &mut Context<Self>,
 5890    ) {
 5891        self.open_or_update_completions_menu(None, None, false, window, cx);
 5892    }
 5893
 5894    fn open_or_update_completions_menu(
 5895        &mut self,
 5896        requested_source: Option<CompletionsMenuSource>,
 5897        trigger: Option<String>,
 5898        trigger_in_words: bool,
 5899        window: &mut Window,
 5900        cx: &mut Context<Self>,
 5901    ) {
 5902        if self.pending_rename.is_some() {
 5903            return;
 5904        }
 5905
 5906        let completions_source = self
 5907            .context_menu
 5908            .borrow()
 5909            .as_ref()
 5910            .and_then(|menu| match menu {
 5911                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5912                CodeContextMenu::CodeActions(_) => None,
 5913            });
 5914
 5915        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5916
 5917        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5918        // inserted and selected. To handle that case, the start of the selection is used so that
 5919        // the menu starts with all choices.
 5920        let position = self
 5921            .selections
 5922            .newest_anchor()
 5923            .start
 5924            .bias_right(&multibuffer_snapshot);
 5925        if position.diff_base_anchor.is_some() {
 5926            return;
 5927        }
 5928        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5929        let Some(buffer) = buffer_position
 5930            .text_anchor
 5931            .buffer_id
 5932            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5933        else {
 5934            return;
 5935        };
 5936        let buffer_snapshot = buffer.read(cx).snapshot();
 5937
 5938        let menu_is_open = matches!(
 5939            self.context_menu.borrow().as_ref(),
 5940            Some(CodeContextMenu::Completions(_))
 5941        );
 5942
 5943        let language = buffer_snapshot
 5944            .language_at(buffer_position.text_anchor)
 5945            .map(|language| language.name());
 5946
 5947        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5948        let completion_settings = language_settings.completions.clone();
 5949
 5950        let show_completions_on_input = self
 5951            .show_completions_on_input_override
 5952            .unwrap_or(language_settings.show_completions_on_input);
 5953        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5954            return;
 5955        }
 5956
 5957        let query: Option<Arc<String>> =
 5958            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5959                .map(|query| query.into());
 5960
 5961        drop(multibuffer_snapshot);
 5962
 5963        // Hide the current completions menu when query is empty. Without this, cached
 5964        // completions from before the trigger char may be reused (#32774).
 5965        if query.is_none() && menu_is_open {
 5966            self.hide_context_menu(window, cx);
 5967        }
 5968
 5969        let mut ignore_word_threshold = false;
 5970        let provider = match requested_source {
 5971            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5972            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5973                ignore_word_threshold = ignore_threshold;
 5974                None
 5975            }
 5976            Some(CompletionsMenuSource::SnippetChoices)
 5977            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5978                log::error!("bug: SnippetChoices requested_source is not handled");
 5979                None
 5980            }
 5981        };
 5982
 5983        let sort_completions = provider
 5984            .as_ref()
 5985            .is_some_and(|provider| provider.sort_completions());
 5986
 5987        let filter_completions = provider
 5988            .as_ref()
 5989            .is_none_or(|provider| provider.filter_completions());
 5990
 5991        let was_snippets_only = matches!(
 5992            completions_source,
 5993            Some(CompletionsMenuSource::SnippetsOnly)
 5994        );
 5995
 5996        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5997            if filter_completions {
 5998                menu.filter(
 5999                    query.clone().unwrap_or_default(),
 6000                    buffer_position.text_anchor,
 6001                    &buffer,
 6002                    provider.clone(),
 6003                    window,
 6004                    cx,
 6005                );
 6006            }
 6007            // When `is_incomplete` is false, no need to re-query completions when the current query
 6008            // is a suffix of the initial query.
 6009            let was_complete = !menu.is_incomplete;
 6010            if was_complete && !was_snippets_only {
 6011                // If the new query is a suffix of the old query (typing more characters) and
 6012                // the previous result was complete, the existing completions can be filtered.
 6013                //
 6014                // Note that snippet completions are always complete.
 6015                let query_matches = match (&menu.initial_query, &query) {
 6016                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6017                    (None, _) => true,
 6018                    _ => false,
 6019                };
 6020                if query_matches {
 6021                    let position_matches = if menu.initial_position == position {
 6022                        true
 6023                    } else {
 6024                        let snapshot = self.buffer.read(cx).read(cx);
 6025                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6026                    };
 6027                    if position_matches {
 6028                        return;
 6029                    }
 6030                }
 6031            }
 6032        };
 6033
 6034        let Anchor {
 6035            excerpt_id: buffer_excerpt_id,
 6036            text_anchor: buffer_position,
 6037            ..
 6038        } = buffer_position;
 6039
 6040        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6041            buffer_snapshot.surrounding_word(buffer_position, None)
 6042        {
 6043            let word_to_exclude = buffer_snapshot
 6044                .text_for_range(word_range.clone())
 6045                .collect::<String>();
 6046            (
 6047                buffer_snapshot.anchor_before(word_range.start)
 6048                    ..buffer_snapshot.anchor_after(buffer_position),
 6049                Some(word_to_exclude),
 6050            )
 6051        } else {
 6052            (buffer_position..buffer_position, None)
 6053        };
 6054
 6055        let show_completion_documentation = buffer_snapshot
 6056            .settings_at(buffer_position, cx)
 6057            .show_completion_documentation;
 6058
 6059        // The document can be large, so stay in reasonable bounds when searching for words,
 6060        // otherwise completion pop-up might be slow to appear.
 6061        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6062        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6063        let min_word_search = buffer_snapshot.clip_point(
 6064            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6065            Bias::Left,
 6066        );
 6067        let max_word_search = buffer_snapshot.clip_point(
 6068            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6069            Bias::Right,
 6070        );
 6071        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6072            ..buffer_snapshot.point_to_offset(max_word_search);
 6073
 6074        let skip_digits = query
 6075            .as_ref()
 6076            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6077
 6078        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6079            trigger.as_ref().is_none_or(|trigger| {
 6080                provider.is_completion_trigger(
 6081                    &buffer,
 6082                    position.text_anchor,
 6083                    trigger,
 6084                    trigger_in_words,
 6085                    cx,
 6086                )
 6087            })
 6088        });
 6089
 6090        let provider_responses = if let Some(provider) = &provider
 6091            && load_provider_completions
 6092        {
 6093            let trigger_character =
 6094                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6095            let completion_context = CompletionContext {
 6096                trigger_kind: match &trigger_character {
 6097                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6098                    None => CompletionTriggerKind::INVOKED,
 6099                },
 6100                trigger_character,
 6101            };
 6102
 6103            provider.completions(
 6104                buffer_excerpt_id,
 6105                &buffer,
 6106                buffer_position,
 6107                completion_context,
 6108                window,
 6109                cx,
 6110            )
 6111        } else {
 6112            Task::ready(Ok(Vec::new()))
 6113        };
 6114
 6115        let load_word_completions = if !self.word_completions_enabled {
 6116            false
 6117        } else if requested_source
 6118            == Some(CompletionsMenuSource::Words {
 6119                ignore_threshold: true,
 6120            })
 6121        {
 6122            true
 6123        } else {
 6124            load_provider_completions
 6125                && completion_settings.words != WordsCompletionMode::Disabled
 6126                && (ignore_word_threshold || {
 6127                    let words_min_length = completion_settings.words_min_length;
 6128                    // check whether word has at least `words_min_length` characters
 6129                    let query_chars = query.iter().flat_map(|q| q.chars());
 6130                    query_chars.take(words_min_length).count() == words_min_length
 6131                })
 6132        };
 6133
 6134        let mut words = if load_word_completions {
 6135            cx.background_spawn({
 6136                let buffer_snapshot = buffer_snapshot.clone();
 6137                async move {
 6138                    buffer_snapshot.words_in_range(WordsQuery {
 6139                        fuzzy_contents: None,
 6140                        range: word_search_range,
 6141                        skip_digits,
 6142                    })
 6143                }
 6144            })
 6145        } else {
 6146            Task::ready(BTreeMap::default())
 6147        };
 6148
 6149        let snippets = if let Some(provider) = &provider
 6150            && provider.show_snippets()
 6151            && let Some(project) = self.project()
 6152        {
 6153            let char_classifier = buffer_snapshot
 6154                .char_classifier_at(buffer_position)
 6155                .scope_context(Some(CharScopeContext::Completion));
 6156            project.update(cx, |project, cx| {
 6157                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6158            })
 6159        } else {
 6160            Task::ready(Ok(CompletionResponse {
 6161                completions: Vec::new(),
 6162                display_options: Default::default(),
 6163                is_incomplete: false,
 6164            }))
 6165        };
 6166
 6167        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6168
 6169        let id = post_inc(&mut self.next_completion_id);
 6170        let task = cx.spawn_in(window, async move |editor, cx| {
 6171            let Ok(()) = editor.update(cx, |this, _| {
 6172                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6173            }) else {
 6174                return;
 6175            };
 6176
 6177            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6178            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6179            let mut completions = Vec::new();
 6180            let mut is_incomplete = false;
 6181            let mut display_options: Option<CompletionDisplayOptions> = None;
 6182            if let Some(provider_responses) = provider_responses.await.log_err()
 6183                && !provider_responses.is_empty()
 6184            {
 6185                for response in provider_responses {
 6186                    completions.extend(response.completions);
 6187                    is_incomplete = is_incomplete || response.is_incomplete;
 6188                    match display_options.as_mut() {
 6189                        None => {
 6190                            display_options = Some(response.display_options);
 6191                        }
 6192                        Some(options) => options.merge(&response.display_options),
 6193                    }
 6194                }
 6195                if completion_settings.words == WordsCompletionMode::Fallback {
 6196                    words = Task::ready(BTreeMap::default());
 6197                }
 6198            }
 6199            let display_options = display_options.unwrap_or_default();
 6200
 6201            let mut words = words.await;
 6202            if let Some(word_to_exclude) = &word_to_exclude {
 6203                words.remove(word_to_exclude);
 6204            }
 6205            for lsp_completion in &completions {
 6206                words.remove(&lsp_completion.new_text);
 6207            }
 6208            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6209                replace_range: word_replace_range.clone(),
 6210                new_text: word.clone(),
 6211                label: CodeLabel::plain(word, None),
 6212                match_start: None,
 6213                snippet_deduplication_key: None,
 6214                icon_path: None,
 6215                documentation: None,
 6216                source: CompletionSource::BufferWord {
 6217                    word_range,
 6218                    resolved: false,
 6219                },
 6220                insert_text_mode: Some(InsertTextMode::AS_IS),
 6221                confirm: None,
 6222            }));
 6223
 6224            completions.extend(
 6225                snippets
 6226                    .await
 6227                    .into_iter()
 6228                    .flat_map(|response| response.completions),
 6229            );
 6230
 6231            let menu = if completions.is_empty() {
 6232                None
 6233            } else {
 6234                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6235                    let languages = editor
 6236                        .workspace
 6237                        .as_ref()
 6238                        .and_then(|(workspace, _)| workspace.upgrade())
 6239                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6240                    let menu = CompletionsMenu::new(
 6241                        id,
 6242                        requested_source.unwrap_or(if load_provider_completions {
 6243                            CompletionsMenuSource::Normal
 6244                        } else {
 6245                            CompletionsMenuSource::SnippetsOnly
 6246                        }),
 6247                        sort_completions,
 6248                        show_completion_documentation,
 6249                        position,
 6250                        query.clone(),
 6251                        is_incomplete,
 6252                        buffer.clone(),
 6253                        completions.into(),
 6254                        editor
 6255                            .context_menu()
 6256                            .borrow_mut()
 6257                            .as_ref()
 6258                            .map(|menu| menu.primary_scroll_handle()),
 6259                        display_options,
 6260                        snippet_sort_order,
 6261                        languages,
 6262                        language,
 6263                        cx,
 6264                    );
 6265
 6266                    let query = if filter_completions { query } else { None };
 6267                    let matches_task = menu.do_async_filtering(
 6268                        query.unwrap_or_default(),
 6269                        buffer_position,
 6270                        &buffer,
 6271                        cx,
 6272                    );
 6273                    (menu, matches_task)
 6274                }) else {
 6275                    return;
 6276                };
 6277
 6278                let matches = matches_task.await;
 6279
 6280                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6281                    // Newer menu already set, so exit.
 6282                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6283                        editor.context_menu.borrow().as_ref()
 6284                        && prev_menu.id > id
 6285                    {
 6286                        return;
 6287                    };
 6288
 6289                    // Only valid to take prev_menu because either the new menu is immediately set
 6290                    // below, or the menu is hidden.
 6291                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6292                        editor.context_menu.borrow_mut().take()
 6293                    {
 6294                        let position_matches =
 6295                            if prev_menu.initial_position == menu.initial_position {
 6296                                true
 6297                            } else {
 6298                                let snapshot = editor.buffer.read(cx).read(cx);
 6299                                prev_menu.initial_position.to_offset(&snapshot)
 6300                                    == menu.initial_position.to_offset(&snapshot)
 6301                            };
 6302                        if position_matches {
 6303                            // Preserve markdown cache before `set_filter_results` because it will
 6304                            // try to populate the documentation cache.
 6305                            menu.preserve_markdown_cache(prev_menu);
 6306                        }
 6307                    };
 6308
 6309                    menu.set_filter_results(matches, provider, window, cx);
 6310                }) else {
 6311                    return;
 6312                };
 6313
 6314                menu.visible().then_some(menu)
 6315            };
 6316
 6317            editor
 6318                .update_in(cx, |editor, window, cx| {
 6319                    if editor.focus_handle.is_focused(window)
 6320                        && let Some(menu) = menu
 6321                    {
 6322                        *editor.context_menu.borrow_mut() =
 6323                            Some(CodeContextMenu::Completions(menu));
 6324
 6325                        crate::hover_popover::hide_hover(editor, cx);
 6326                        if editor.show_edit_predictions_in_menu() {
 6327                            editor.update_visible_edit_prediction(window, cx);
 6328                        } else {
 6329                            editor
 6330                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6331                        }
 6332
 6333                        cx.notify();
 6334                        return;
 6335                    }
 6336
 6337                    if editor.completion_tasks.len() <= 1 {
 6338                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6339                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6340                        // If it was already hidden and we don't show edit predictions in the menu,
 6341                        // we should also show the edit prediction when available.
 6342                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6343                            editor.update_visible_edit_prediction(window, cx);
 6344                        }
 6345                    }
 6346                })
 6347                .ok();
 6348        });
 6349
 6350        self.completion_tasks.push((id, task));
 6351    }
 6352
 6353    #[cfg(any(test, feature = "test-support"))]
 6354    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6355        let menu = self.context_menu.borrow();
 6356        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6357            let completions = menu.completions.borrow();
 6358            Some(completions.to_vec())
 6359        } else {
 6360            None
 6361        }
 6362    }
 6363
 6364    pub fn with_completions_menu_matching_id<R>(
 6365        &self,
 6366        id: CompletionId,
 6367        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6368    ) -> R {
 6369        let mut context_menu = self.context_menu.borrow_mut();
 6370        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6371            return f(None);
 6372        };
 6373        if completions_menu.id != id {
 6374            return f(None);
 6375        }
 6376        f(Some(completions_menu))
 6377    }
 6378
 6379    pub fn confirm_completion(
 6380        &mut self,
 6381        action: &ConfirmCompletion,
 6382        window: &mut Window,
 6383        cx: &mut Context<Self>,
 6384    ) -> Option<Task<Result<()>>> {
 6385        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6386        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6387    }
 6388
 6389    pub fn confirm_completion_insert(
 6390        &mut self,
 6391        _: &ConfirmCompletionInsert,
 6392        window: &mut Window,
 6393        cx: &mut Context<Self>,
 6394    ) -> Option<Task<Result<()>>> {
 6395        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6396        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6397    }
 6398
 6399    pub fn confirm_completion_replace(
 6400        &mut self,
 6401        _: &ConfirmCompletionReplace,
 6402        window: &mut Window,
 6403        cx: &mut Context<Self>,
 6404    ) -> Option<Task<Result<()>>> {
 6405        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6406        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6407    }
 6408
 6409    pub fn compose_completion(
 6410        &mut self,
 6411        action: &ComposeCompletion,
 6412        window: &mut Window,
 6413        cx: &mut Context<Self>,
 6414    ) -> Option<Task<Result<()>>> {
 6415        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6416        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6417    }
 6418
 6419    fn do_completion(
 6420        &mut self,
 6421        item_ix: Option<usize>,
 6422        intent: CompletionIntent,
 6423        window: &mut Window,
 6424        cx: &mut Context<Editor>,
 6425    ) -> Option<Task<Result<()>>> {
 6426        use language::ToOffset as _;
 6427
 6428        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6429        else {
 6430            return None;
 6431        };
 6432
 6433        let candidate_id = {
 6434            let entries = completions_menu.entries.borrow();
 6435            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6436            if self.show_edit_predictions_in_menu() {
 6437                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6438            }
 6439            mat.candidate_id
 6440        };
 6441
 6442        let completion = completions_menu
 6443            .completions
 6444            .borrow()
 6445            .get(candidate_id)?
 6446            .clone();
 6447        cx.stop_propagation();
 6448
 6449        let buffer_handle = completions_menu.buffer.clone();
 6450
 6451        let CompletionEdit {
 6452            new_text,
 6453            snippet,
 6454            replace_range,
 6455        } = process_completion_for_edit(
 6456            &completion,
 6457            intent,
 6458            &buffer_handle,
 6459            &completions_menu.initial_position.text_anchor,
 6460            cx,
 6461        );
 6462
 6463        let buffer = buffer_handle.read(cx);
 6464        let snapshot = self.buffer.read(cx).snapshot(cx);
 6465        let newest_anchor = self.selections.newest_anchor();
 6466        let replace_range_multibuffer = {
 6467            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6468            excerpt.map_range_from_buffer(replace_range.clone())
 6469        };
 6470        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6471            return None;
 6472        }
 6473
 6474        let old_text = buffer
 6475            .text_for_range(replace_range.clone())
 6476            .collect::<String>();
 6477        let lookbehind = newest_anchor
 6478            .start
 6479            .text_anchor
 6480            .to_offset(buffer)
 6481            .saturating_sub(replace_range.start.0);
 6482        let lookahead = replace_range
 6483            .end
 6484            .0
 6485            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6486        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6487        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6488
 6489        let selections = self
 6490            .selections
 6491            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6492        let mut ranges = Vec::new();
 6493        let mut linked_edits = LinkedEdits::new();
 6494
 6495        let text: Arc<str> = new_text.clone().into();
 6496        for selection in &selections {
 6497            let range = if selection.id == newest_anchor.id {
 6498                replace_range_multibuffer.clone()
 6499            } else {
 6500                let mut range = selection.range();
 6501
 6502                // if prefix is present, don't duplicate it
 6503                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6504                    range.start = range.start.saturating_sub_usize(lookbehind);
 6505
 6506                    // if suffix is also present, mimic the newest cursor and replace it
 6507                    if selection.id != newest_anchor.id
 6508                        && snapshot.contains_str_at(range.end, suffix)
 6509                    {
 6510                        range.end += lookahead;
 6511                    }
 6512                }
 6513                range
 6514            };
 6515
 6516            ranges.push(range.clone());
 6517
 6518            if !self.linked_edit_ranges.is_empty() {
 6519                let start_anchor = snapshot.anchor_before(range.start);
 6520                let end_anchor = snapshot.anchor_after(range.end);
 6521                let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6522                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6523            }
 6524        }
 6525
 6526        let common_prefix_len = old_text
 6527            .chars()
 6528            .zip(new_text.chars())
 6529            .take_while(|(a, b)| a == b)
 6530            .map(|(a, _)| a.len_utf8())
 6531            .sum::<usize>();
 6532
 6533        cx.emit(EditorEvent::InputHandled {
 6534            utf16_range_to_replace: None,
 6535            text: new_text[common_prefix_len..].into(),
 6536        });
 6537
 6538        self.transact(window, cx, |editor, window, cx| {
 6539            if let Some(mut snippet) = snippet {
 6540                snippet.text = new_text.to_string();
 6541                editor
 6542                    .insert_snippet(&ranges, snippet, window, cx)
 6543                    .log_err();
 6544            } else {
 6545                editor.buffer.update(cx, |multi_buffer, cx| {
 6546                    let auto_indent = match completion.insert_text_mode {
 6547                        Some(InsertTextMode::AS_IS) => None,
 6548                        _ => editor.autoindent_mode.clone(),
 6549                    };
 6550                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6551                    multi_buffer.edit(edits, auto_indent, cx);
 6552                });
 6553            }
 6554            linked_edits.apply(cx);
 6555            editor.refresh_edit_prediction(true, false, window, cx);
 6556        });
 6557        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6558
 6559        let show_new_completions_on_confirm = completion
 6560            .confirm
 6561            .as_ref()
 6562            .is_some_and(|confirm| confirm(intent, window, cx));
 6563        if show_new_completions_on_confirm {
 6564            self.open_or_update_completions_menu(None, None, false, window, cx);
 6565        }
 6566
 6567        let provider = self.completion_provider.as_ref()?;
 6568
 6569        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6570        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6571            let CompletionSource::Lsp {
 6572                lsp_completion,
 6573                server_id,
 6574                ..
 6575            } = &completion.source
 6576            else {
 6577                return None;
 6578            };
 6579            let lsp_command = lsp_completion.command.as_ref()?;
 6580            let available_commands = lsp_store
 6581                .read(cx)
 6582                .lsp_server_capabilities
 6583                .get(server_id)
 6584                .and_then(|server_capabilities| {
 6585                    server_capabilities
 6586                        .execute_command_provider
 6587                        .as_ref()
 6588                        .map(|options| options.commands.as_slice())
 6589                })?;
 6590            if available_commands.contains(&lsp_command.command) {
 6591                Some(CodeAction {
 6592                    server_id: *server_id,
 6593                    range: language::Anchor::MIN..language::Anchor::MIN,
 6594                    lsp_action: LspAction::Command(lsp_command.clone()),
 6595                    resolved: false,
 6596                })
 6597            } else {
 6598                None
 6599            }
 6600        });
 6601
 6602        drop(completion);
 6603        let apply_edits = provider.apply_additional_edits_for_completion(
 6604            buffer_handle.clone(),
 6605            completions_menu.completions.clone(),
 6606            candidate_id,
 6607            true,
 6608            cx,
 6609        );
 6610
 6611        let editor_settings = EditorSettings::get_global(cx);
 6612        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6613            // After the code completion is finished, users often want to know what signatures are needed.
 6614            // so we should automatically call signature_help
 6615            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6616        }
 6617
 6618        Some(cx.spawn_in(window, async move |editor, cx| {
 6619            apply_edits.await?;
 6620
 6621            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6622                let title = command.lsp_action.title().to_owned();
 6623                let project_transaction = lsp_store
 6624                    .update(cx, |lsp_store, cx| {
 6625                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6626                    })
 6627                    .await
 6628                    .context("applying post-completion command")?;
 6629                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6630                    Self::open_project_transaction(
 6631                        &editor,
 6632                        workspace.downgrade(),
 6633                        project_transaction,
 6634                        title,
 6635                        cx,
 6636                    )
 6637                    .await?;
 6638                }
 6639            }
 6640
 6641            Ok(())
 6642        }))
 6643    }
 6644
 6645    pub fn toggle_code_actions(
 6646        &mut self,
 6647        action: &ToggleCodeActions,
 6648        window: &mut Window,
 6649        cx: &mut Context<Self>,
 6650    ) {
 6651        let quick_launch = action.quick_launch;
 6652        let mut context_menu = self.context_menu.borrow_mut();
 6653        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6654            if code_actions.deployed_from == action.deployed_from {
 6655                // Toggle if we're selecting the same one
 6656                *context_menu = None;
 6657                cx.notify();
 6658                return;
 6659            } else {
 6660                // Otherwise, clear it and start a new one
 6661                *context_menu = None;
 6662                cx.notify();
 6663            }
 6664        }
 6665        drop(context_menu);
 6666        let snapshot = self.snapshot(window, cx);
 6667        let deployed_from = action.deployed_from.clone();
 6668        let action = action.clone();
 6669        self.completion_tasks.clear();
 6670        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6671
 6672        let multibuffer_point = match &action.deployed_from {
 6673            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6674                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6675            }
 6676            _ => self
 6677                .selections
 6678                .newest::<Point>(&snapshot.display_snapshot)
 6679                .head(),
 6680        };
 6681        let Some((buffer, buffer_row)) = snapshot
 6682            .buffer_snapshot()
 6683            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6684            .and_then(|(buffer_snapshot, range)| {
 6685                self.buffer()
 6686                    .read(cx)
 6687                    .buffer(buffer_snapshot.remote_id())
 6688                    .map(|buffer| (buffer, range.start.row))
 6689            })
 6690        else {
 6691            return;
 6692        };
 6693        let buffer_id = buffer.read(cx).remote_id();
 6694        let tasks = self
 6695            .runnables
 6696            .runnables((buffer_id, buffer_row))
 6697            .map(|t| Arc::new(t.to_owned()));
 6698
 6699        if !self.focus_handle.is_focused(window) {
 6700            return;
 6701        }
 6702        let project = self.project.clone();
 6703
 6704        let code_actions_task = match deployed_from {
 6705            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6706            _ => self.code_actions(buffer_row, window, cx),
 6707        };
 6708
 6709        let runnable_task = match deployed_from {
 6710            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6711            _ => {
 6712                let mut task_context_task = Task::ready(None);
 6713                if let Some(tasks) = &tasks
 6714                    && let Some(project) = project
 6715                {
 6716                    task_context_task =
 6717                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6718                }
 6719
 6720                cx.spawn_in(window, {
 6721                    let buffer = buffer.clone();
 6722                    async move |editor, cx| {
 6723                        let task_context = task_context_task.await;
 6724
 6725                        let resolved_tasks =
 6726                            tasks
 6727                                .zip(task_context.clone())
 6728                                .map(|(tasks, task_context)| ResolvedTasks {
 6729                                    templates: tasks.resolve(&task_context).collect(),
 6730                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6731                                        multibuffer_point.row,
 6732                                        tasks.column,
 6733                                    )),
 6734                                });
 6735                        let debug_scenarios = editor
 6736                            .update(cx, |editor, cx| {
 6737                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6738                            })?
 6739                            .await;
 6740                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6741                    }
 6742                })
 6743            }
 6744        };
 6745
 6746        cx.spawn_in(window, async move |editor, cx| {
 6747            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6748            let code_actions = code_actions_task.await;
 6749            let spawn_straight_away = quick_launch
 6750                && resolved_tasks
 6751                    .as_ref()
 6752                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6753                && code_actions
 6754                    .as_ref()
 6755                    .is_none_or(|actions| actions.is_empty())
 6756                && debug_scenarios.is_empty();
 6757
 6758            editor.update_in(cx, |editor, window, cx| {
 6759                crate::hover_popover::hide_hover(editor, cx);
 6760                let actions = CodeActionContents::new(
 6761                    resolved_tasks,
 6762                    code_actions,
 6763                    debug_scenarios,
 6764                    task_context.unwrap_or_default(),
 6765                );
 6766
 6767                // Don't show the menu if there are no actions available
 6768                if actions.is_empty() {
 6769                    cx.notify();
 6770                    return Task::ready(Ok(()));
 6771                }
 6772
 6773                *editor.context_menu.borrow_mut() =
 6774                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6775                        buffer,
 6776                        actions,
 6777                        selected_item: Default::default(),
 6778                        scroll_handle: UniformListScrollHandle::default(),
 6779                        deployed_from,
 6780                    }));
 6781                cx.notify();
 6782                if spawn_straight_away
 6783                    && let Some(task) = editor.confirm_code_action(
 6784                        &ConfirmCodeAction { item_ix: Some(0) },
 6785                        window,
 6786                        cx,
 6787                    )
 6788                {
 6789                    return task;
 6790                }
 6791
 6792                Task::ready(Ok(()))
 6793            })
 6794        })
 6795        .detach_and_log_err(cx);
 6796    }
 6797
 6798    fn debug_scenarios(
 6799        &mut self,
 6800        resolved_tasks: &Option<ResolvedTasks>,
 6801        buffer: &Entity<Buffer>,
 6802        cx: &mut App,
 6803    ) -> Task<Vec<task::DebugScenario>> {
 6804        maybe!({
 6805            let project = self.project()?;
 6806            let dap_store = project.read(cx).dap_store();
 6807            let mut scenarios = vec![];
 6808            let resolved_tasks = resolved_tasks.as_ref()?;
 6809            let buffer = buffer.read(cx);
 6810            let language = buffer.language()?;
 6811            let file = buffer.file();
 6812            let debug_adapter = language_settings(language.name().into(), file, cx)
 6813                .debuggers
 6814                .first()
 6815                .map(SharedString::from)
 6816                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6817
 6818            dap_store.update(cx, |dap_store, cx| {
 6819                for (_, task) in &resolved_tasks.templates {
 6820                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6821                        task.original_task().clone(),
 6822                        debug_adapter.clone().into(),
 6823                        task.display_label().to_owned().into(),
 6824                        cx,
 6825                    );
 6826                    scenarios.push(maybe_scenario);
 6827                }
 6828            });
 6829            Some(cx.background_spawn(async move {
 6830                futures::future::join_all(scenarios)
 6831                    .await
 6832                    .into_iter()
 6833                    .flatten()
 6834                    .collect::<Vec<_>>()
 6835            }))
 6836        })
 6837        .unwrap_or_else(|| Task::ready(vec![]))
 6838    }
 6839
 6840    fn code_actions(
 6841        &mut self,
 6842        buffer_row: u32,
 6843        window: &mut Window,
 6844        cx: &mut Context<Self>,
 6845    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6846        let mut task = self.code_actions_task.take();
 6847        cx.spawn_in(window, async move |editor, cx| {
 6848            while let Some(prev_task) = task {
 6849                prev_task.await.log_err();
 6850                task = editor
 6851                    .update(cx, |this, _| this.code_actions_task.take())
 6852                    .ok()?;
 6853            }
 6854
 6855            editor
 6856                .update(cx, |editor, cx| {
 6857                    editor
 6858                        .available_code_actions
 6859                        .clone()
 6860                        .and_then(|(location, code_actions)| {
 6861                            let snapshot = location.buffer.read(cx).snapshot();
 6862                            let point_range = location.range.to_point(&snapshot);
 6863                            let point_range = point_range.start.row..=point_range.end.row;
 6864                            if point_range.contains(&buffer_row) {
 6865                                Some(code_actions)
 6866                            } else {
 6867                                None
 6868                            }
 6869                        })
 6870                })
 6871                .ok()
 6872                .flatten()
 6873        })
 6874    }
 6875
 6876    pub fn confirm_code_action(
 6877        &mut self,
 6878        action: &ConfirmCodeAction,
 6879        window: &mut Window,
 6880        cx: &mut Context<Self>,
 6881    ) -> Option<Task<Result<()>>> {
 6882        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6883
 6884        let actions_menu =
 6885            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6886                menu
 6887            } else {
 6888                return None;
 6889            };
 6890
 6891        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6892        let action = actions_menu.actions.get(action_ix)?;
 6893        let title = action.label();
 6894        let buffer = actions_menu.buffer;
 6895        let workspace = self.workspace()?;
 6896
 6897        match action {
 6898            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6899                workspace.update(cx, |workspace, cx| {
 6900                    workspace.schedule_resolved_task(
 6901                        task_source_kind,
 6902                        resolved_task,
 6903                        false,
 6904                        window,
 6905                        cx,
 6906                    );
 6907
 6908                    Some(Task::ready(Ok(())))
 6909                })
 6910            }
 6911            CodeActionsItem::CodeAction {
 6912                excerpt_id,
 6913                action,
 6914                provider,
 6915            } => {
 6916                let apply_code_action =
 6917                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6918                let workspace = workspace.downgrade();
 6919                Some(cx.spawn_in(window, async move |editor, cx| {
 6920                    let project_transaction = apply_code_action.await?;
 6921                    Self::open_project_transaction(
 6922                        &editor,
 6923                        workspace,
 6924                        project_transaction,
 6925                        title,
 6926                        cx,
 6927                    )
 6928                    .await
 6929                }))
 6930            }
 6931            CodeActionsItem::DebugScenario(scenario) => {
 6932                let context = actions_menu.actions.context.into();
 6933
 6934                workspace.update(cx, |workspace, cx| {
 6935                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6936                    workspace.start_debug_session(
 6937                        scenario,
 6938                        context,
 6939                        Some(buffer),
 6940                        None,
 6941                        window,
 6942                        cx,
 6943                    );
 6944                });
 6945                Some(Task::ready(Ok(())))
 6946            }
 6947        }
 6948    }
 6949
 6950    fn open_transaction_for_hidden_buffers(
 6951        workspace: Entity<Workspace>,
 6952        transaction: ProjectTransaction,
 6953        title: String,
 6954        window: &mut Window,
 6955        cx: &mut Context<Self>,
 6956    ) {
 6957        if transaction.0.is_empty() {
 6958            return;
 6959        }
 6960
 6961        let edited_buffers_already_open = {
 6962            let other_editors: Vec<Entity<Editor>> = workspace
 6963                .read(cx)
 6964                .panes()
 6965                .iter()
 6966                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6967                .filter(|editor| editor.entity_id() != cx.entity_id())
 6968                .collect();
 6969
 6970            transaction.0.keys().all(|buffer| {
 6971                other_editors.iter().any(|editor| {
 6972                    let multi_buffer = editor.read(cx).buffer();
 6973                    multi_buffer.read(cx).is_singleton()
 6974                        && multi_buffer
 6975                            .read(cx)
 6976                            .as_singleton()
 6977                            .map_or(false, |singleton| {
 6978                                singleton.entity_id() == buffer.entity_id()
 6979                            })
 6980                })
 6981            })
 6982        };
 6983        if !edited_buffers_already_open {
 6984            let workspace = workspace.downgrade();
 6985            cx.defer_in(window, move |_, window, cx| {
 6986                cx.spawn_in(window, async move |editor, cx| {
 6987                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6988                        .await
 6989                        .ok()
 6990                })
 6991                .detach();
 6992            });
 6993        }
 6994    }
 6995
 6996    pub async fn open_project_transaction(
 6997        editor: &WeakEntity<Editor>,
 6998        workspace: WeakEntity<Workspace>,
 6999        transaction: ProjectTransaction,
 7000        title: String,
 7001        cx: &mut AsyncWindowContext,
 7002    ) -> Result<()> {
 7003        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7004        cx.update(|_, cx| {
 7005            entries.sort_unstable_by_key(|(buffer, _)| {
 7006                buffer.read(cx).file().map(|f| f.path().clone())
 7007            });
 7008        })?;
 7009        if entries.is_empty() {
 7010            return Ok(());
 7011        }
 7012
 7013        // If the project transaction's edits are all contained within this editor, then
 7014        // avoid opening a new editor to display them.
 7015
 7016        if let [(buffer, transaction)] = &*entries {
 7017            let excerpt = editor.update(cx, |editor, cx| {
 7018                editor
 7019                    .buffer()
 7020                    .read(cx)
 7021                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7022            })?;
 7023            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7024                && excerpted_buffer == *buffer
 7025            {
 7026                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7027                    let excerpt_range = excerpt_range.to_offset(buffer);
 7028                    buffer
 7029                        .edited_ranges_for_transaction::<usize>(transaction)
 7030                        .all(|range| {
 7031                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7032                        })
 7033                });
 7034
 7035                if all_edits_within_excerpt {
 7036                    return Ok(());
 7037                }
 7038            }
 7039        }
 7040
 7041        let mut ranges_to_highlight = Vec::new();
 7042        let excerpt_buffer = cx.new(|cx| {
 7043            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7044            for (buffer_handle, transaction) in &entries {
 7045                let edited_ranges = buffer_handle
 7046                    .read(cx)
 7047                    .edited_ranges_for_transaction::<Point>(transaction)
 7048                    .collect::<Vec<_>>();
 7049                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7050                    PathKey::for_buffer(buffer_handle, cx),
 7051                    buffer_handle.clone(),
 7052                    edited_ranges,
 7053                    multibuffer_context_lines(cx),
 7054                    cx,
 7055                );
 7056
 7057                ranges_to_highlight.extend(ranges);
 7058            }
 7059            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7060            multibuffer
 7061        });
 7062
 7063        workspace.update_in(cx, |workspace, window, cx| {
 7064            let project = workspace.project().clone();
 7065            let editor =
 7066                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7067            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7068            editor.update(cx, |editor, cx| {
 7069                editor.highlight_background(
 7070                    HighlightKey::Editor,
 7071                    &ranges_to_highlight,
 7072                    |_, theme| theme.colors().editor_highlighted_line_background,
 7073                    cx,
 7074                );
 7075            });
 7076        })?;
 7077
 7078        Ok(())
 7079    }
 7080
 7081    pub fn clear_code_action_providers(&mut self) {
 7082        self.code_action_providers.clear();
 7083        self.available_code_actions.take();
 7084    }
 7085
 7086    pub fn add_code_action_provider(
 7087        &mut self,
 7088        provider: Rc<dyn CodeActionProvider>,
 7089        window: &mut Window,
 7090        cx: &mut Context<Self>,
 7091    ) {
 7092        if self
 7093            .code_action_providers
 7094            .iter()
 7095            .any(|existing_provider| existing_provider.id() == provider.id())
 7096        {
 7097            return;
 7098        }
 7099
 7100        self.code_action_providers.push(provider);
 7101        self.refresh_code_actions(window, cx);
 7102    }
 7103
 7104    pub fn remove_code_action_provider(
 7105        &mut self,
 7106        id: Arc<str>,
 7107        window: &mut Window,
 7108        cx: &mut Context<Self>,
 7109    ) {
 7110        self.code_action_providers
 7111            .retain(|provider| provider.id() != id);
 7112        self.refresh_code_actions(window, cx);
 7113    }
 7114
 7115    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7116        !self.code_action_providers.is_empty()
 7117            && EditorSettings::get_global(cx).toolbar.code_actions
 7118    }
 7119
 7120    pub fn has_available_code_actions(&self) -> bool {
 7121        self.available_code_actions
 7122            .as_ref()
 7123            .is_some_and(|(_, actions)| !actions.is_empty())
 7124    }
 7125
 7126    fn render_inline_code_actions(
 7127        &self,
 7128        icon_size: ui::IconSize,
 7129        display_row: DisplayRow,
 7130        is_active: bool,
 7131        cx: &mut Context<Self>,
 7132    ) -> AnyElement {
 7133        let show_tooltip = !self.context_menu_visible();
 7134        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7135            .icon_size(icon_size)
 7136            .shape(ui::IconButtonShape::Square)
 7137            .icon_color(ui::Color::Hidden)
 7138            .toggle_state(is_active)
 7139            .when(show_tooltip, |this| {
 7140                this.tooltip({
 7141                    let focus_handle = self.focus_handle.clone();
 7142                    move |_window, cx| {
 7143                        Tooltip::for_action_in(
 7144                            "Toggle Code Actions",
 7145                            &ToggleCodeActions {
 7146                                deployed_from: None,
 7147                                quick_launch: false,
 7148                            },
 7149                            &focus_handle,
 7150                            cx,
 7151                        )
 7152                    }
 7153                })
 7154            })
 7155            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7156                window.focus(&editor.focus_handle(cx), cx);
 7157                editor.toggle_code_actions(
 7158                    &crate::actions::ToggleCodeActions {
 7159                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7160                            display_row,
 7161                        )),
 7162                        quick_launch: false,
 7163                    },
 7164                    window,
 7165                    cx,
 7166                );
 7167            }))
 7168            .into_any_element()
 7169    }
 7170
 7171    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7172        &self.context_menu
 7173    }
 7174
 7175    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7176        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7177            cx.background_executor()
 7178                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7179                .await;
 7180
 7181            let (start_buffer, start, _, end, newest_selection) = this
 7182                .update(cx, |this, cx| {
 7183                    let newest_selection = this.selections.newest_anchor().clone();
 7184                    if newest_selection.head().diff_base_anchor.is_some() {
 7185                        return None;
 7186                    }
 7187                    let display_snapshot = this.display_snapshot(cx);
 7188                    let newest_selection_adjusted =
 7189                        this.selections.newest_adjusted(&display_snapshot);
 7190                    let buffer = this.buffer.read(cx);
 7191
 7192                    let (start_buffer, start) =
 7193                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7194                    let (end_buffer, end) =
 7195                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7196
 7197                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7198                })?
 7199                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7200                .context(
 7201                    "Expected selection to lie in a single buffer when refreshing code actions",
 7202                )?;
 7203            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7204                let providers = this.code_action_providers.clone();
 7205                let tasks = this
 7206                    .code_action_providers
 7207                    .iter()
 7208                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7209                    .collect::<Vec<_>>();
 7210                (providers, tasks)
 7211            })?;
 7212
 7213            let mut actions = Vec::new();
 7214            for (provider, provider_actions) in
 7215                providers.into_iter().zip(future::join_all(tasks).await)
 7216            {
 7217                if let Some(provider_actions) = provider_actions.log_err() {
 7218                    actions.extend(provider_actions.into_iter().map(|action| {
 7219                        AvailableCodeAction {
 7220                            excerpt_id: newest_selection.start.excerpt_id,
 7221                            action,
 7222                            provider: provider.clone(),
 7223                        }
 7224                    }));
 7225                }
 7226            }
 7227
 7228            this.update(cx, |this, cx| {
 7229                this.available_code_actions = if actions.is_empty() {
 7230                    None
 7231                } else {
 7232                    Some((
 7233                        Location {
 7234                            buffer: start_buffer,
 7235                            range: start..end,
 7236                        },
 7237                        actions.into(),
 7238                    ))
 7239                };
 7240                cx.notify();
 7241            })
 7242        }));
 7243    }
 7244
 7245    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7246        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7247            self.show_git_blame_inline = false;
 7248
 7249            self.show_git_blame_inline_delay_task =
 7250                Some(cx.spawn_in(window, async move |this, cx| {
 7251                    cx.background_executor().timer(delay).await;
 7252
 7253                    this.update(cx, |this, cx| {
 7254                        this.show_git_blame_inline = true;
 7255                        cx.notify();
 7256                    })
 7257                    .log_err();
 7258                }));
 7259        }
 7260    }
 7261
 7262    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7263        let snapshot = self.snapshot(window, cx);
 7264        let cursor = self
 7265            .selections
 7266            .newest::<Point>(&snapshot.display_snapshot)
 7267            .head();
 7268        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7269        else {
 7270            return;
 7271        };
 7272
 7273        if self.blame.is_none() {
 7274            self.start_git_blame(true, window, cx);
 7275        }
 7276        let Some(blame) = self.blame.as_ref() else {
 7277            return;
 7278        };
 7279
 7280        let row_info = RowInfo {
 7281            buffer_id: Some(buffer.remote_id()),
 7282            buffer_row: Some(point.row),
 7283            ..Default::default()
 7284        };
 7285        let Some((buffer, blame_entry)) = blame
 7286            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7287            .flatten()
 7288        else {
 7289            return;
 7290        };
 7291
 7292        let anchor = self.selections.newest_anchor().head();
 7293        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7294        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7295            self.show_blame_popover(
 7296                buffer,
 7297                &blame_entry,
 7298                position + last_bounds.origin,
 7299                true,
 7300                cx,
 7301            );
 7302        };
 7303    }
 7304
 7305    fn show_blame_popover(
 7306        &mut self,
 7307        buffer: BufferId,
 7308        blame_entry: &BlameEntry,
 7309        position: gpui::Point<Pixels>,
 7310        ignore_timeout: bool,
 7311        cx: &mut Context<Self>,
 7312    ) {
 7313        if let Some(state) = &mut self.inline_blame_popover {
 7314            state.hide_task.take();
 7315        } else {
 7316            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7317            let blame_entry = blame_entry.clone();
 7318            let show_task = cx.spawn(async move |editor, cx| {
 7319                if !ignore_timeout {
 7320                    cx.background_executor()
 7321                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7322                        .await;
 7323                }
 7324                editor
 7325                    .update(cx, |editor, cx| {
 7326                        editor.inline_blame_popover_show_task.take();
 7327                        let Some(blame) = editor.blame.as_ref() else {
 7328                            return;
 7329                        };
 7330                        let blame = blame.read(cx);
 7331                        let details = blame.details_for_entry(buffer, &blame_entry);
 7332                        let markdown = cx.new(|cx| {
 7333                            Markdown::new(
 7334                                details
 7335                                    .as_ref()
 7336                                    .map(|message| message.message.clone())
 7337                                    .unwrap_or_default(),
 7338                                None,
 7339                                None,
 7340                                cx,
 7341                            )
 7342                        });
 7343                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7344                            position,
 7345                            hide_task: None,
 7346                            popover_bounds: None,
 7347                            popover_state: InlineBlamePopoverState {
 7348                                scroll_handle: ScrollHandle::new(),
 7349                                commit_message: details,
 7350                                markdown,
 7351                            },
 7352                            keyboard_grace: ignore_timeout,
 7353                        });
 7354                        cx.notify();
 7355                    })
 7356                    .ok();
 7357            });
 7358            self.inline_blame_popover_show_task = Some(show_task);
 7359        }
 7360    }
 7361
 7362    pub fn has_mouse_context_menu(&self) -> bool {
 7363        self.mouse_context_menu.is_some()
 7364    }
 7365
 7366    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7367        self.inline_blame_popover_show_task.take();
 7368        if let Some(state) = &mut self.inline_blame_popover {
 7369            let hide_task = cx.spawn(async move |editor, cx| {
 7370                if !ignore_timeout {
 7371                    cx.background_executor()
 7372                        .timer(std::time::Duration::from_millis(100))
 7373                        .await;
 7374                }
 7375                editor
 7376                    .update(cx, |editor, cx| {
 7377                        editor.inline_blame_popover.take();
 7378                        cx.notify();
 7379                    })
 7380                    .ok();
 7381            });
 7382            state.hide_task = Some(hide_task);
 7383            true
 7384        } else {
 7385            false
 7386        }
 7387    }
 7388
 7389    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7390        if self.pending_rename.is_some() {
 7391            return None;
 7392        }
 7393
 7394        let provider = self.semantics_provider.clone()?;
 7395        let buffer = self.buffer.read(cx);
 7396        let newest_selection = self.selections.newest_anchor().clone();
 7397        let cursor_position = newest_selection.head();
 7398        let (cursor_buffer, cursor_buffer_position) =
 7399            buffer.text_anchor_for_position(cursor_position, cx)?;
 7400        let (tail_buffer, tail_buffer_position) =
 7401            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7402        if cursor_buffer != tail_buffer {
 7403            return None;
 7404        }
 7405
 7406        let snapshot = cursor_buffer.read(cx).snapshot();
 7407        let word_ranges = cx.background_spawn(async move {
 7408            // this might look odd to put on the background thread, but
 7409            // `surrounding_word` can be quite expensive as it calls into
 7410            // tree-sitter language scopes
 7411            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7412            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7413            (start_word_range, end_word_range)
 7414        });
 7415
 7416        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7417        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7418            let (start_word_range, end_word_range) = word_ranges.await;
 7419            if start_word_range != end_word_range {
 7420                this.update(cx, |this, cx| {
 7421                    this.document_highlights_task.take();
 7422                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7423                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7424                })
 7425                .ok();
 7426                return;
 7427            }
 7428            cx.background_executor()
 7429                .timer(Duration::from_millis(debounce))
 7430                .await;
 7431
 7432            let highlights = if let Some(highlights) = cx.update(|cx| {
 7433                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7434            }) {
 7435                highlights.await.log_err()
 7436            } else {
 7437                None
 7438            };
 7439
 7440            if let Some(highlights) = highlights {
 7441                this.update(cx, |this, cx| {
 7442                    if this.pending_rename.is_some() {
 7443                        return;
 7444                    }
 7445
 7446                    let buffer = this.buffer.read(cx);
 7447                    if buffer
 7448                        .text_anchor_for_position(cursor_position, cx)
 7449                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7450                    {
 7451                        return;
 7452                    }
 7453
 7454                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7455                    let mut write_ranges = Vec::new();
 7456                    let mut read_ranges = Vec::new();
 7457                    for highlight in highlights {
 7458                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7459                        for (excerpt_id, _, excerpt_range) in
 7460                            buffer.excerpts_for_buffer(buffer_id, cx)
 7461                        {
 7462                            let start = highlight
 7463                                .range
 7464                                .start
 7465                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7466                            let end = highlight
 7467                                .range
 7468                                .end
 7469                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7470                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7471                                continue;
 7472                            }
 7473
 7474                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7475                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7476                                write_ranges.push(range);
 7477                            } else {
 7478                                read_ranges.push(range);
 7479                            }
 7480                        }
 7481                    }
 7482
 7483                    this.highlight_background(
 7484                        HighlightKey::DocumentHighlightRead,
 7485                        &read_ranges,
 7486                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7487                        cx,
 7488                    );
 7489                    this.highlight_background(
 7490                        HighlightKey::DocumentHighlightWrite,
 7491                        &write_ranges,
 7492                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7493                        cx,
 7494                    );
 7495                    cx.notify();
 7496                })
 7497                .log_err();
 7498            }
 7499        }));
 7500        None
 7501    }
 7502
 7503    fn prepare_highlight_query_from_selection(
 7504        &mut self,
 7505        snapshot: &DisplaySnapshot,
 7506        cx: &mut Context<Editor>,
 7507    ) -> Option<(String, Range<Anchor>)> {
 7508        if matches!(self.mode, EditorMode::SingleLine) {
 7509            return None;
 7510        }
 7511        if !EditorSettings::get_global(cx).selection_highlight {
 7512            return None;
 7513        }
 7514        if self.selections.count() != 1 || self.selections.line_mode() {
 7515            return None;
 7516        }
 7517        let selection = self.selections.newest::<Point>(&snapshot);
 7518        // If the selection spans multiple rows OR it is empty
 7519        if selection.start.row != selection.end.row
 7520            || selection.start.column == selection.end.column
 7521        {
 7522            return None;
 7523        }
 7524        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7525        let query = snapshot
 7526            .buffer_snapshot()
 7527            .text_for_range(selection_anchor_range.clone())
 7528            .collect::<String>();
 7529        if query.trim().is_empty() {
 7530            return None;
 7531        }
 7532        Some((query, selection_anchor_range))
 7533    }
 7534
 7535    #[ztracing::instrument(skip_all)]
 7536    fn update_selection_occurrence_highlights(
 7537        &mut self,
 7538        multi_buffer_snapshot: MultiBufferSnapshot,
 7539        query_text: String,
 7540        query_range: Range<Anchor>,
 7541        multi_buffer_range_to_query: Range<Point>,
 7542        use_debounce: bool,
 7543        window: &mut Window,
 7544        cx: &mut Context<Editor>,
 7545    ) -> Task<()> {
 7546        cx.spawn_in(window, async move |editor, cx| {
 7547            if use_debounce {
 7548                cx.background_executor()
 7549                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7550                    .await;
 7551            }
 7552            let match_task = cx.background_spawn(async move {
 7553                let buffer_ranges = multi_buffer_snapshot
 7554                    .range_to_buffer_ranges(
 7555                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7556                    )
 7557                    .into_iter()
 7558                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7559                let mut match_ranges = Vec::new();
 7560                let Ok(regex) = project::search::SearchQuery::text(
 7561                    query_text,
 7562                    false,
 7563                    false,
 7564                    false,
 7565                    Default::default(),
 7566                    Default::default(),
 7567                    false,
 7568                    None,
 7569                ) else {
 7570                    return Vec::default();
 7571                };
 7572                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7573                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7574                    match_ranges.extend(
 7575                        regex
 7576                            .search(
 7577                                buffer_snapshot,
 7578                                Some(search_range.start.0..search_range.end.0),
 7579                            )
 7580                            .await
 7581                            .into_iter()
 7582                            .filter_map(|match_range| {
 7583                                let match_start = buffer_snapshot
 7584                                    .anchor_after(search_range.start + match_range.start);
 7585                                let match_end = buffer_snapshot
 7586                                    .anchor_before(search_range.start + match_range.end);
 7587                                let match_anchor_range =
 7588                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7589                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7590                            }),
 7591                    );
 7592                }
 7593                match_ranges
 7594            });
 7595            let match_ranges = match_task.await;
 7596            editor
 7597                .update_in(cx, |editor, _, cx| {
 7598                    if use_debounce {
 7599                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7600                        editor.debounced_selection_highlight_complete = true;
 7601                    } else if editor.debounced_selection_highlight_complete {
 7602                        return;
 7603                    }
 7604                    if !match_ranges.is_empty() {
 7605                        editor.highlight_background(
 7606                            HighlightKey::SelectedTextHighlight,
 7607                            &match_ranges,
 7608                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7609                            cx,
 7610                        )
 7611                    }
 7612                })
 7613                .log_err();
 7614        })
 7615    }
 7616
 7617    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7618        struct NewlineFold;
 7619        let type_id = std::any::TypeId::of::<NewlineFold>();
 7620        if !self.mode.is_single_line() {
 7621            return;
 7622        }
 7623        let snapshot = self.snapshot(window, cx);
 7624        if snapshot.buffer_snapshot().max_point().row == 0 {
 7625            return;
 7626        }
 7627        let task = cx.background_spawn(async move {
 7628            let new_newlines = snapshot
 7629                .buffer_chars_at(MultiBufferOffset(0))
 7630                .filter_map(|(c, i)| {
 7631                    if c == '\n' {
 7632                        Some(
 7633                            snapshot.buffer_snapshot().anchor_after(i)
 7634                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7635                        )
 7636                    } else {
 7637                        None
 7638                    }
 7639                })
 7640                .collect::<Vec<_>>();
 7641            let existing_newlines = snapshot
 7642                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7643                .filter_map(|fold| {
 7644                    if fold.placeholder.type_tag == Some(type_id) {
 7645                        Some(fold.range.start..fold.range.end)
 7646                    } else {
 7647                        None
 7648                    }
 7649                })
 7650                .collect::<Vec<_>>();
 7651
 7652            (new_newlines, existing_newlines)
 7653        });
 7654        self.folding_newlines = cx.spawn(async move |this, cx| {
 7655            let (new_newlines, existing_newlines) = task.await;
 7656            if new_newlines == existing_newlines {
 7657                return;
 7658            }
 7659            let placeholder = FoldPlaceholder {
 7660                render: Arc::new(move |_, _, cx| {
 7661                    div()
 7662                        .bg(cx.theme().status().hint_background)
 7663                        .border_b_1()
 7664                        .size_full()
 7665                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7666                        .border_color(cx.theme().status().hint)
 7667                        .child("\\n")
 7668                        .into_any()
 7669                }),
 7670                constrain_width: false,
 7671                merge_adjacent: false,
 7672                type_tag: Some(type_id),
 7673                collapsed_text: None,
 7674            };
 7675            let creases = new_newlines
 7676                .into_iter()
 7677                .map(|range| Crease::simple(range, placeholder.clone()))
 7678                .collect();
 7679            this.update(cx, |this, cx| {
 7680                this.display_map.update(cx, |display_map, cx| {
 7681                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7682                    display_map.fold(creases, cx);
 7683                });
 7684            })
 7685            .ok();
 7686        });
 7687    }
 7688
 7689    #[ztracing::instrument(skip_all)]
 7690    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7691        if !self.lsp_data_enabled() {
 7692            return;
 7693        }
 7694        let cursor = self.selections.newest_anchor().head();
 7695        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7696
 7697        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7698            self.outline_symbols_at_cursor =
 7699                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7700            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7701            cx.notify();
 7702        } else {
 7703            let syntax = cx.theme().syntax().clone();
 7704            let background_task = cx.background_spawn(async move {
 7705                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7706            });
 7707            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7708                cx.spawn(async move |this, cx| {
 7709                    let symbols = background_task.await;
 7710                    this.update(cx, |this, cx| {
 7711                        this.outline_symbols_at_cursor = symbols;
 7712                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7713                        cx.notify();
 7714                    })
 7715                    .ok();
 7716                });
 7717        }
 7718    }
 7719
 7720    #[ztracing::instrument(skip_all)]
 7721    fn refresh_selected_text_highlights(
 7722        &mut self,
 7723        snapshot: &DisplaySnapshot,
 7724        on_buffer_edit: bool,
 7725        window: &mut Window,
 7726        cx: &mut Context<Editor>,
 7727    ) {
 7728        let Some((query_text, query_range)) =
 7729            self.prepare_highlight_query_from_selection(snapshot, cx)
 7730        else {
 7731            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7732            self.quick_selection_highlight_task.take();
 7733            self.debounced_selection_highlight_task.take();
 7734            self.debounced_selection_highlight_complete = false;
 7735            return;
 7736        };
 7737        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7738        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7739        let query_changed = self
 7740            .quick_selection_highlight_task
 7741            .as_ref()
 7742            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7743        if query_changed {
 7744            self.debounced_selection_highlight_complete = false;
 7745        }
 7746        if on_buffer_edit || query_changed {
 7747            self.quick_selection_highlight_task = Some((
 7748                query_range.clone(),
 7749                self.update_selection_occurrence_highlights(
 7750                    snapshot.buffer.clone(),
 7751                    query_text.clone(),
 7752                    query_range.clone(),
 7753                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7754                    false,
 7755                    window,
 7756                    cx,
 7757                ),
 7758            ));
 7759        }
 7760        if on_buffer_edit
 7761            || self
 7762                .debounced_selection_highlight_task
 7763                .as_ref()
 7764                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7765        {
 7766            let multi_buffer_start = multi_buffer_snapshot
 7767                .anchor_before(MultiBufferOffset(0))
 7768                .to_point(&multi_buffer_snapshot);
 7769            let multi_buffer_end = multi_buffer_snapshot
 7770                .anchor_after(multi_buffer_snapshot.len())
 7771                .to_point(&multi_buffer_snapshot);
 7772            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7773            self.debounced_selection_highlight_task = Some((
 7774                query_range.clone(),
 7775                self.update_selection_occurrence_highlights(
 7776                    snapshot.buffer.clone(),
 7777                    query_text,
 7778                    query_range,
 7779                    multi_buffer_full_range,
 7780                    true,
 7781                    window,
 7782                    cx,
 7783                ),
 7784            ));
 7785        }
 7786    }
 7787
 7788    pub fn multi_buffer_visible_range(
 7789        &self,
 7790        display_snapshot: &DisplaySnapshot,
 7791        cx: &App,
 7792    ) -> Range<Point> {
 7793        let visible_start = self
 7794            .scroll_manager
 7795            .native_anchor(display_snapshot, cx)
 7796            .anchor
 7797            .to_point(display_snapshot.buffer_snapshot())
 7798            .to_display_point(display_snapshot);
 7799
 7800        let mut target_end = visible_start;
 7801        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7802
 7803        visible_start.to_point(display_snapshot)
 7804            ..display_snapshot
 7805                .clip_point(target_end, Bias::Right)
 7806                .to_point(display_snapshot)
 7807    }
 7808
 7809    pub fn refresh_edit_prediction(
 7810        &mut self,
 7811        debounce: bool,
 7812        user_requested: bool,
 7813        window: &mut Window,
 7814        cx: &mut Context<Self>,
 7815    ) -> Option<()> {
 7816        let provider = self.edit_prediction_provider()?;
 7817        let cursor = self.selections.newest_anchor().head();
 7818        let (buffer, cursor_buffer_position) =
 7819            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7820
 7821        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7822            return None;
 7823        }
 7824
 7825        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7826            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7827            return None;
 7828        }
 7829
 7830        self.update_visible_edit_prediction(window, cx);
 7831
 7832        if !user_requested
 7833            && (!self.should_show_edit_predictions()
 7834                || !self.is_focused(window)
 7835                || buffer.read(cx).is_empty())
 7836        {
 7837            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7838            return None;
 7839        }
 7840
 7841        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7842        Some(())
 7843    }
 7844
 7845    fn show_edit_predictions_in_menu(&self) -> bool {
 7846        match self.edit_prediction_settings {
 7847            EditPredictionSettings::Disabled => false,
 7848            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7849        }
 7850    }
 7851
 7852    pub fn edit_predictions_enabled(&self) -> bool {
 7853        match self.edit_prediction_settings {
 7854            EditPredictionSettings::Disabled => false,
 7855            EditPredictionSettings::Enabled { .. } => true,
 7856        }
 7857    }
 7858
 7859    fn edit_prediction_requires_modifier(&self) -> bool {
 7860        match self.edit_prediction_settings {
 7861            EditPredictionSettings::Disabled => false,
 7862            EditPredictionSettings::Enabled {
 7863                preview_requires_modifier,
 7864                ..
 7865            } => preview_requires_modifier,
 7866        }
 7867    }
 7868
 7869    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7870        if self.edit_prediction_provider.is_none() {
 7871            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7872            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7873            return;
 7874        }
 7875
 7876        let selection = self.selections.newest_anchor();
 7877        let cursor = selection.head();
 7878
 7879        if let Some((buffer, cursor_buffer_position)) =
 7880            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7881        {
 7882            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7883                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7884                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7885                return;
 7886            }
 7887            self.edit_prediction_settings =
 7888                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7889        }
 7890    }
 7891
 7892    fn edit_prediction_settings_at_position(
 7893        &self,
 7894        buffer: &Entity<Buffer>,
 7895        buffer_position: language::Anchor,
 7896        cx: &App,
 7897    ) -> EditPredictionSettings {
 7898        if !self.mode.is_full()
 7899            || !self.show_edit_predictions_override.unwrap_or(true)
 7900            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7901        {
 7902            return EditPredictionSettings::Disabled;
 7903        }
 7904
 7905        let buffer = buffer.read(cx);
 7906
 7907        let file = buffer.file();
 7908
 7909        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7910            return EditPredictionSettings::Disabled;
 7911        };
 7912
 7913        let by_provider = matches!(
 7914            self.menu_edit_predictions_policy,
 7915            MenuEditPredictionsPolicy::ByProvider
 7916        );
 7917
 7918        let show_in_menu = by_provider
 7919            && self
 7920                .edit_prediction_provider
 7921                .as_ref()
 7922                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7923
 7924        let preview_requires_modifier =
 7925            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7926
 7927        EditPredictionSettings::Enabled {
 7928            show_in_menu,
 7929            preview_requires_modifier,
 7930        }
 7931    }
 7932
 7933    fn should_show_edit_predictions(&self) -> bool {
 7934        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7935    }
 7936
 7937    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7938        matches!(
 7939            self.edit_prediction_preview,
 7940            EditPredictionPreview::Active { .. }
 7941        )
 7942    }
 7943
 7944    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7945        let cursor = self.selections.newest_anchor().head();
 7946        if let Some((buffer, cursor_position)) =
 7947            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7948        {
 7949            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7950        } else {
 7951            false
 7952        }
 7953    }
 7954
 7955    pub fn supports_minimap(&self, cx: &App) -> bool {
 7956        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7957    }
 7958
 7959    fn edit_predictions_enabled_in_buffer(
 7960        &self,
 7961        buffer: &Entity<Buffer>,
 7962        buffer_position: language::Anchor,
 7963        cx: &App,
 7964    ) -> bool {
 7965        maybe!({
 7966            if self.read_only(cx) {
 7967                return Some(false);
 7968            }
 7969            let provider = self.edit_prediction_provider()?;
 7970            if !provider.is_enabled(buffer, buffer_position, cx) {
 7971                return Some(false);
 7972            }
 7973            let buffer = buffer.read(cx);
 7974            let Some(file) = buffer.file() else {
 7975                return Some(true);
 7976            };
 7977            let settings = all_language_settings(Some(file), cx);
 7978            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7979        })
 7980        .unwrap_or(false)
 7981    }
 7982
 7983    pub fn show_edit_prediction(
 7984        &mut self,
 7985        _: &ShowEditPrediction,
 7986        window: &mut Window,
 7987        cx: &mut Context<Self>,
 7988    ) {
 7989        if !self.has_active_edit_prediction() {
 7990            self.refresh_edit_prediction(false, true, window, cx);
 7991            return;
 7992        }
 7993
 7994        self.update_visible_edit_prediction(window, cx);
 7995    }
 7996
 7997    pub fn display_cursor_names(
 7998        &mut self,
 7999        _: &DisplayCursorNames,
 8000        window: &mut Window,
 8001        cx: &mut Context<Self>,
 8002    ) {
 8003        self.show_cursor_names(window, cx);
 8004    }
 8005
 8006    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8007        self.show_cursor_names = true;
 8008        cx.notify();
 8009        cx.spawn_in(window, async move |this, cx| {
 8010            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8011            this.update(cx, |this, cx| {
 8012                this.show_cursor_names = false;
 8013                cx.notify()
 8014            })
 8015            .ok()
 8016        })
 8017        .detach();
 8018    }
 8019
 8020    pub fn accept_partial_edit_prediction(
 8021        &mut self,
 8022        granularity: EditPredictionGranularity,
 8023        window: &mut Window,
 8024        cx: &mut Context<Self>,
 8025    ) {
 8026        if self.show_edit_predictions_in_menu() {
 8027            self.hide_context_menu(window, cx);
 8028        }
 8029
 8030        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8031            return;
 8032        };
 8033
 8034        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8035            return;
 8036        }
 8037
 8038        match &active_edit_prediction.completion {
 8039            EditPrediction::MoveWithin { target, .. } => {
 8040                let target = *target;
 8041
 8042                if matches!(granularity, EditPredictionGranularity::Full) {
 8043                    if let Some(position_map) = &self.last_position_map {
 8044                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8045                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8046
 8047                        if is_visible || !self.edit_prediction_requires_modifier() {
 8048                            self.unfold_ranges(&[target..target], true, false, cx);
 8049                            self.change_selections(
 8050                                SelectionEffects::scroll(Autoscroll::newest()),
 8051                                window,
 8052                                cx,
 8053                                |selections| {
 8054                                    selections.select_anchor_ranges([target..target]);
 8055                                },
 8056                            );
 8057                            self.clear_row_highlights::<EditPredictionPreview>();
 8058                            self.edit_prediction_preview
 8059                                .set_previous_scroll_position(None);
 8060                        } else {
 8061                            // Highlight and request scroll
 8062                            self.edit_prediction_preview
 8063                                .set_previous_scroll_position(Some(
 8064                                    position_map.snapshot.scroll_anchor,
 8065                                ));
 8066                            self.highlight_rows::<EditPredictionPreview>(
 8067                                target..target,
 8068                                cx.theme().colors().editor_highlighted_line_background,
 8069                                RowHighlightOptions {
 8070                                    autoscroll: true,
 8071                                    ..Default::default()
 8072                                },
 8073                                cx,
 8074                            );
 8075                            self.request_autoscroll(Autoscroll::fit(), cx);
 8076                        }
 8077                    }
 8078                } else {
 8079                    self.change_selections(
 8080                        SelectionEffects::scroll(Autoscroll::newest()),
 8081                        window,
 8082                        cx,
 8083                        |selections| {
 8084                            selections.select_anchor_ranges([target..target]);
 8085                        },
 8086                    );
 8087                }
 8088            }
 8089            EditPrediction::MoveOutside { snapshot, target } => {
 8090                if let Some(workspace) = self.workspace() {
 8091                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8092                        .detach_and_log_err(cx);
 8093                }
 8094            }
 8095            EditPrediction::Edit {
 8096                edits,
 8097                cursor_position,
 8098                ..
 8099            } => {
 8100                self.report_edit_prediction_event(
 8101                    active_edit_prediction.completion_id.clone(),
 8102                    true,
 8103                    cx,
 8104                );
 8105
 8106                match granularity {
 8107                    EditPredictionGranularity::Full => {
 8108                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8109
 8110                        // Compute fallback cursor position BEFORE applying the edit,
 8111                        // so the anchor tracks through the edit correctly
 8112                        let fallback_cursor_target = {
 8113                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8114                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8115                        };
 8116
 8117                        self.buffer.update(cx, |buffer, cx| {
 8118                            buffer.edit(edits.iter().cloned(), None, cx)
 8119                        });
 8120
 8121                        if let Some(provider) = self.edit_prediction_provider() {
 8122                            provider.accept(cx);
 8123                        }
 8124
 8125                        // Resolve cursor position after the edit is applied
 8126                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8127                            // The anchor tracks through the edit, then we add the offset
 8128                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8129                            let base_offset = anchor.to_offset(&snapshot).0;
 8130                            let target_offset =
 8131                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8132                            snapshot.anchor_after(target_offset)
 8133                        } else {
 8134                            fallback_cursor_target
 8135                        };
 8136
 8137                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8138                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8139                        });
 8140
 8141                        let selections = self.selections.disjoint_anchors_arc();
 8142                        if let Some(transaction_id_now) =
 8143                            self.buffer.read(cx).last_transaction_id(cx)
 8144                        {
 8145                            if transaction_id_prev != Some(transaction_id_now) {
 8146                                self.selection_history
 8147                                    .insert_transaction(transaction_id_now, selections);
 8148                            }
 8149                        }
 8150
 8151                        self.update_visible_edit_prediction(window, cx);
 8152                        if self.active_edit_prediction.is_none() {
 8153                            self.refresh_edit_prediction(true, true, window, cx);
 8154                        }
 8155                        cx.notify();
 8156                    }
 8157                    _ => {
 8158                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8159                        let cursor_offset = self
 8160                            .selections
 8161                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8162                            .head();
 8163
 8164                        let insertion = edits.iter().find_map(|(range, text)| {
 8165                            let range = range.to_offset(&snapshot);
 8166                            if range.is_empty() && range.start == cursor_offset {
 8167                                Some(text)
 8168                            } else {
 8169                                None
 8170                            }
 8171                        });
 8172
 8173                        if let Some(text) = insertion {
 8174                            let text_to_insert = match granularity {
 8175                                EditPredictionGranularity::Word => {
 8176                                    let mut partial = text
 8177                                        .chars()
 8178                                        .by_ref()
 8179                                        .take_while(|c| c.is_alphabetic())
 8180                                        .collect::<String>();
 8181                                    if partial.is_empty() {
 8182                                        partial = text
 8183                                            .chars()
 8184                                            .by_ref()
 8185                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8186                                            .collect::<String>();
 8187                                    }
 8188                                    partial
 8189                                }
 8190                                EditPredictionGranularity::Line => {
 8191                                    if let Some(line) = text.split_inclusive('\n').next() {
 8192                                        line.to_string()
 8193                                    } else {
 8194                                        text.to_string()
 8195                                    }
 8196                                }
 8197                                EditPredictionGranularity::Full => unreachable!(),
 8198                            };
 8199
 8200                            cx.emit(EditorEvent::InputHandled {
 8201                                utf16_range_to_replace: None,
 8202                                text: text_to_insert.clone().into(),
 8203                            });
 8204
 8205                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8206                            self.refresh_edit_prediction(true, true, window, cx);
 8207                            cx.notify();
 8208                        } else {
 8209                            self.accept_partial_edit_prediction(
 8210                                EditPredictionGranularity::Full,
 8211                                window,
 8212                                cx,
 8213                            );
 8214                        }
 8215                    }
 8216                }
 8217            }
 8218        }
 8219
 8220        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8221    }
 8222
 8223    pub fn accept_next_word_edit_prediction(
 8224        &mut self,
 8225        _: &AcceptNextWordEditPrediction,
 8226        window: &mut Window,
 8227        cx: &mut Context<Self>,
 8228    ) {
 8229        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8230    }
 8231
 8232    pub fn accept_next_line_edit_prediction(
 8233        &mut self,
 8234        _: &AcceptNextLineEditPrediction,
 8235        window: &mut Window,
 8236        cx: &mut Context<Self>,
 8237    ) {
 8238        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8239    }
 8240
 8241    pub fn accept_edit_prediction(
 8242        &mut self,
 8243        _: &AcceptEditPrediction,
 8244        window: &mut Window,
 8245        cx: &mut Context<Self>,
 8246    ) {
 8247        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8248    }
 8249
 8250    fn discard_edit_prediction(
 8251        &mut self,
 8252        reason: EditPredictionDiscardReason,
 8253        cx: &mut Context<Self>,
 8254    ) -> bool {
 8255        if reason == EditPredictionDiscardReason::Rejected {
 8256            let completion_id = self
 8257                .active_edit_prediction
 8258                .as_ref()
 8259                .and_then(|active_completion| active_completion.completion_id.clone());
 8260
 8261            self.report_edit_prediction_event(completion_id, false, cx);
 8262        }
 8263
 8264        if let Some(provider) = self.edit_prediction_provider() {
 8265            provider.discard(reason, cx);
 8266        }
 8267
 8268        self.take_active_edit_prediction(cx)
 8269    }
 8270
 8271    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8272        let Some(provider) = self.edit_prediction_provider() else {
 8273            return;
 8274        };
 8275
 8276        let Some((_, buffer, _)) = self
 8277            .buffer
 8278            .read(cx)
 8279            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8280        else {
 8281            return;
 8282        };
 8283
 8284        let extension = buffer
 8285            .read(cx)
 8286            .file()
 8287            .and_then(|file| Some(file.path().extension()?.to_string()));
 8288
 8289        let event_type = match accepted {
 8290            true => "Edit Prediction Accepted",
 8291            false => "Edit Prediction Discarded",
 8292        };
 8293        telemetry::event!(
 8294            event_type,
 8295            provider = provider.name(),
 8296            prediction_id = id,
 8297            suggestion_accepted = accepted,
 8298            file_extension = extension,
 8299        );
 8300    }
 8301
 8302    fn open_editor_at_anchor(
 8303        snapshot: &language::BufferSnapshot,
 8304        target: language::Anchor,
 8305        workspace: &Entity<Workspace>,
 8306        window: &mut Window,
 8307        cx: &mut App,
 8308    ) -> Task<Result<()>> {
 8309        workspace.update(cx, |workspace, cx| {
 8310            let path = snapshot.file().map(|file| file.full_path(cx));
 8311            let Some(path) =
 8312                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8313            else {
 8314                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8315            };
 8316            let target = text::ToPoint::to_point(&target, snapshot);
 8317            let item = workspace.open_path(path, None, true, window, cx);
 8318            window.spawn(cx, async move |cx| {
 8319                let Some(editor) = item.await?.downcast::<Editor>() else {
 8320                    return Ok(());
 8321                };
 8322                editor
 8323                    .update_in(cx, |editor, window, cx| {
 8324                        editor.go_to_singleton_buffer_point(target, window, cx);
 8325                    })
 8326                    .ok();
 8327                anyhow::Ok(())
 8328            })
 8329        })
 8330    }
 8331
 8332    pub fn has_active_edit_prediction(&self) -> bool {
 8333        self.active_edit_prediction.is_some()
 8334    }
 8335
 8336    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8337        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8338            return false;
 8339        };
 8340
 8341        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8342        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8343        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8344        true
 8345    }
 8346
 8347    /// Returns true when we're displaying the edit prediction popover below the cursor
 8348    /// like we are not previewing and the LSP autocomplete menu is visible
 8349    /// or we are in `when_holding_modifier` mode.
 8350    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8351        if self.edit_prediction_preview_is_active()
 8352            || !self.show_edit_predictions_in_menu()
 8353            || !self.edit_predictions_enabled()
 8354        {
 8355            return false;
 8356        }
 8357
 8358        if self.has_visible_completions_menu() {
 8359            return true;
 8360        }
 8361
 8362        has_completion && self.edit_prediction_requires_modifier()
 8363    }
 8364
 8365    fn handle_modifiers_changed(
 8366        &mut self,
 8367        modifiers: Modifiers,
 8368        position_map: &PositionMap,
 8369        window: &mut Window,
 8370        cx: &mut Context<Self>,
 8371    ) {
 8372        // Ensure that the edit prediction preview is updated, even when not
 8373        // enabled, if there's an active edit prediction preview.
 8374        if self.show_edit_predictions_in_menu()
 8375            || matches!(
 8376                self.edit_prediction_preview,
 8377                EditPredictionPreview::Active { .. }
 8378            )
 8379        {
 8380            self.update_edit_prediction_preview(&modifiers, window, cx);
 8381        }
 8382
 8383        self.update_selection_mode(&modifiers, position_map, window, cx);
 8384
 8385        let mouse_position = window.mouse_position();
 8386        if !position_map.text_hitbox.is_hovered(window) {
 8387            return;
 8388        }
 8389
 8390        self.update_hovered_link(
 8391            position_map.point_for_position(mouse_position),
 8392            &position_map.snapshot,
 8393            modifiers,
 8394            window,
 8395            cx,
 8396        )
 8397    }
 8398
 8399    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8400        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8401            MultiCursorModifier::Alt => modifiers.secondary(),
 8402            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8403        }
 8404    }
 8405
 8406    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8407        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8408            MultiCursorModifier::Alt => modifiers.alt,
 8409            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8410        }
 8411    }
 8412
 8413    fn columnar_selection_mode(
 8414        modifiers: &Modifiers,
 8415        cx: &mut Context<Self>,
 8416    ) -> Option<ColumnarMode> {
 8417        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8418            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8419                Some(ColumnarMode::FromMouse)
 8420            } else if Self::is_alt_pressed(modifiers, cx) {
 8421                Some(ColumnarMode::FromSelection)
 8422            } else {
 8423                None
 8424            }
 8425        } else {
 8426            None
 8427        }
 8428    }
 8429
 8430    fn update_selection_mode(
 8431        &mut self,
 8432        modifiers: &Modifiers,
 8433        position_map: &PositionMap,
 8434        window: &mut Window,
 8435        cx: &mut Context<Self>,
 8436    ) {
 8437        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8438            return;
 8439        };
 8440        if self.selections.pending_anchor().is_none() {
 8441            return;
 8442        }
 8443
 8444        let mouse_position = window.mouse_position();
 8445        let point_for_position = position_map.point_for_position(mouse_position);
 8446        let position = point_for_position.previous_valid;
 8447
 8448        self.select(
 8449            SelectPhase::BeginColumnar {
 8450                position,
 8451                reset: false,
 8452                mode,
 8453                goal_column: point_for_position.exact_unclipped.column(),
 8454            },
 8455            window,
 8456            cx,
 8457        );
 8458    }
 8459
 8460    fn update_edit_prediction_preview(
 8461        &mut self,
 8462        modifiers: &Modifiers,
 8463        window: &mut Window,
 8464        cx: &mut Context<Self>,
 8465    ) {
 8466        let mut modifiers_held = false;
 8467
 8468        // Check bindings for all granularities.
 8469        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8470        let granularities = [
 8471            EditPredictionGranularity::Full,
 8472            EditPredictionGranularity::Line,
 8473            EditPredictionGranularity::Word,
 8474        ];
 8475
 8476        for granularity in granularities {
 8477            if let Some(keystroke) = self
 8478                .accept_edit_prediction_keybind(granularity, window, cx)
 8479                .keystroke()
 8480            {
 8481                modifiers_held = modifiers_held
 8482                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8483            }
 8484        }
 8485
 8486        if modifiers_held {
 8487            if matches!(
 8488                self.edit_prediction_preview,
 8489                EditPredictionPreview::Inactive { .. }
 8490            ) {
 8491                self.edit_prediction_preview = EditPredictionPreview::Active {
 8492                    previous_scroll_position: None,
 8493                    since: Instant::now(),
 8494                };
 8495
 8496                self.update_visible_edit_prediction(window, cx);
 8497                cx.notify();
 8498            }
 8499        } else if let EditPredictionPreview::Active {
 8500            previous_scroll_position,
 8501            since,
 8502        } = self.edit_prediction_preview
 8503        {
 8504            if let (Some(previous_scroll_position), Some(position_map)) =
 8505                (previous_scroll_position, self.last_position_map.as_ref())
 8506            {
 8507                self.set_scroll_position(
 8508                    previous_scroll_position
 8509                        .scroll_position(&position_map.snapshot.display_snapshot),
 8510                    window,
 8511                    cx,
 8512                );
 8513            }
 8514
 8515            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8516                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8517            };
 8518            self.clear_row_highlights::<EditPredictionPreview>();
 8519            self.update_visible_edit_prediction(window, cx);
 8520            cx.notify();
 8521        }
 8522    }
 8523
 8524    fn update_visible_edit_prediction(
 8525        &mut self,
 8526        _window: &mut Window,
 8527        cx: &mut Context<Self>,
 8528    ) -> Option<()> {
 8529        if self.ime_transaction.is_some() {
 8530            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8531            return None;
 8532        }
 8533
 8534        let selection = self.selections.newest_anchor();
 8535        let cursor = selection.head();
 8536        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8537
 8538        // Check project-level disable_ai setting for the current buffer
 8539        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8540            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8541                return None;
 8542            }
 8543        }
 8544        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8545        let excerpt_id = cursor.excerpt_id;
 8546
 8547        let show_in_menu = self.show_edit_predictions_in_menu();
 8548        let completions_menu_has_precedence = !show_in_menu
 8549            && (self.context_menu.borrow().is_some()
 8550                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8551
 8552        if completions_menu_has_precedence
 8553            || !offset_selection.is_empty()
 8554            || self
 8555                .active_edit_prediction
 8556                .as_ref()
 8557                .is_some_and(|completion| {
 8558                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8559                        return false;
 8560                    };
 8561                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8562                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8563                    !invalidation_range.contains(&offset_selection.head())
 8564                })
 8565        {
 8566            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8567            return None;
 8568        }
 8569
 8570        self.take_active_edit_prediction(cx);
 8571        let Some(provider) = self.edit_prediction_provider() else {
 8572            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8573            return None;
 8574        };
 8575
 8576        let (buffer, cursor_buffer_position) =
 8577            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8578
 8579        self.edit_prediction_settings =
 8580            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8581
 8582        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8583
 8584        if self.edit_prediction_indent_conflict {
 8585            let cursor_point = cursor.to_point(&multibuffer);
 8586            let mut suggested_indent = None;
 8587            multibuffer.suggested_indents_callback(
 8588                cursor_point.row..cursor_point.row + 1,
 8589                &mut |_, indent| {
 8590                    suggested_indent = Some(indent);
 8591                    ControlFlow::Break(())
 8592                },
 8593                cx,
 8594            );
 8595
 8596            if let Some(indent) = suggested_indent
 8597                && indent.len == cursor_point.column
 8598            {
 8599                self.edit_prediction_indent_conflict = false;
 8600            }
 8601        }
 8602
 8603        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8604
 8605        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8606        {
 8607            edit_prediction_types::EditPrediction::Local {
 8608                id,
 8609                edits,
 8610                cursor_position,
 8611                edit_preview,
 8612            } => (id, edits, cursor_position, edit_preview),
 8613            edit_prediction_types::EditPrediction::Jump {
 8614                id,
 8615                snapshot,
 8616                target,
 8617            } => {
 8618                if let Some(provider) = &self.edit_prediction_provider {
 8619                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8620                }
 8621                self.stale_edit_prediction_in_menu = None;
 8622                self.active_edit_prediction = Some(EditPredictionState {
 8623                    inlay_ids: vec![],
 8624                    completion: EditPrediction::MoveOutside { snapshot, target },
 8625                    completion_id: id,
 8626                    invalidation_range: None,
 8627                });
 8628                cx.notify();
 8629                return Some(());
 8630            }
 8631        };
 8632
 8633        let edits = edits
 8634            .into_iter()
 8635            .flat_map(|(range, new_text)| {
 8636                Some((
 8637                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8638                    new_text,
 8639                ))
 8640            })
 8641            .collect::<Vec<_>>();
 8642        if edits.is_empty() {
 8643            return None;
 8644        }
 8645
 8646        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8647            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8648            Some((anchor, predicted.offset))
 8649        });
 8650
 8651        let first_edit_start = edits.first().unwrap().0.start;
 8652        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8653        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8654
 8655        let last_edit_end = edits.last().unwrap().0.end;
 8656        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8657        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8658
 8659        let cursor_row = cursor.to_point(&multibuffer).row;
 8660
 8661        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8662
 8663        let mut inlay_ids = Vec::new();
 8664        let invalidation_row_range;
 8665        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8666            Some(cursor_row..edit_end_row)
 8667        } else if cursor_row > edit_end_row {
 8668            Some(edit_start_row..cursor_row)
 8669        } else {
 8670            None
 8671        };
 8672        let supports_jump = self
 8673            .edit_prediction_provider
 8674            .as_ref()
 8675            .map(|provider| provider.provider.supports_jump_to_edit())
 8676            .unwrap_or(true);
 8677
 8678        let is_move = supports_jump
 8679            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8680        let completion = if is_move {
 8681            if let Some(provider) = &self.edit_prediction_provider {
 8682                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8683            }
 8684            invalidation_row_range =
 8685                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8686            let target = first_edit_start;
 8687            EditPrediction::MoveWithin { target, snapshot }
 8688        } else {
 8689            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8690                && !self.edit_predictions_hidden_for_vim_mode;
 8691
 8692            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8693                if provider.show_tab_accept_marker() {
 8694                    EditDisplayMode::TabAccept
 8695                } else {
 8696                    EditDisplayMode::Inline
 8697                }
 8698            } else {
 8699                EditDisplayMode::DiffPopover
 8700            };
 8701
 8702            if show_completions_in_buffer {
 8703                if let Some(provider) = &self.edit_prediction_provider {
 8704                    let suggestion_display_type = match display_mode {
 8705                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8706                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8707                            SuggestionDisplayType::GhostText
 8708                        }
 8709                    };
 8710                    provider.provider.did_show(suggestion_display_type, cx);
 8711                }
 8712                if edits
 8713                    .iter()
 8714                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8715                {
 8716                    let mut inlays = Vec::new();
 8717                    for (range, new_text) in &edits {
 8718                        let inlay = Inlay::edit_prediction(
 8719                            post_inc(&mut self.next_inlay_id),
 8720                            range.start,
 8721                            new_text.as_ref(),
 8722                        );
 8723                        inlay_ids.push(inlay.id);
 8724                        inlays.push(inlay);
 8725                    }
 8726
 8727                    self.splice_inlays(&[], inlays, cx);
 8728                } else {
 8729                    let background_color = cx.theme().status().deleted_background;
 8730                    self.highlight_text(
 8731                        HighlightKey::EditPredictionHighlight,
 8732                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8733                        HighlightStyle {
 8734                            background_color: Some(background_color),
 8735                            ..Default::default()
 8736                        },
 8737                        cx,
 8738                    );
 8739                }
 8740            }
 8741
 8742            invalidation_row_range = edit_start_row..edit_end_row;
 8743
 8744            EditPrediction::Edit {
 8745                edits,
 8746                cursor_position,
 8747                edit_preview,
 8748                display_mode,
 8749                snapshot,
 8750            }
 8751        };
 8752
 8753        let invalidation_range = multibuffer
 8754            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8755            ..multibuffer.anchor_after(Point::new(
 8756                invalidation_row_range.end,
 8757                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8758            ));
 8759
 8760        self.stale_edit_prediction_in_menu = None;
 8761        self.active_edit_prediction = Some(EditPredictionState {
 8762            inlay_ids,
 8763            completion,
 8764            completion_id,
 8765            invalidation_range: Some(invalidation_range),
 8766        });
 8767
 8768        cx.notify();
 8769
 8770        Some(())
 8771    }
 8772
 8773    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8774        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8775    }
 8776
 8777    /// Get all display points of breakpoints that will be rendered within editor
 8778    ///
 8779    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8780    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8781    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8782    fn active_breakpoints(
 8783        &self,
 8784        range: Range<DisplayRow>,
 8785        window: &mut Window,
 8786        cx: &mut Context<Self>,
 8787    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8788        let mut breakpoint_display_points = HashMap::default();
 8789
 8790        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8791            return breakpoint_display_points;
 8792        };
 8793
 8794        let snapshot = self.snapshot(window, cx);
 8795
 8796        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8797        let Some(project) = self.project() else {
 8798            return breakpoint_display_points;
 8799        };
 8800
 8801        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8802            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8803
 8804        for (buffer_snapshot, range, excerpt_id) in
 8805            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8806        {
 8807            let Some(buffer) = project
 8808                .read(cx)
 8809                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8810            else {
 8811                continue;
 8812            };
 8813            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8814                &buffer,
 8815                Some(
 8816                    buffer_snapshot.anchor_before(range.start)
 8817                        ..buffer_snapshot.anchor_after(range.end),
 8818                ),
 8819                buffer_snapshot,
 8820                cx,
 8821            );
 8822            for (breakpoint, state) in breakpoints {
 8823                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8824                let position = multi_buffer_anchor
 8825                    .to_point(&multi_buffer_snapshot)
 8826                    .to_display_point(&snapshot);
 8827
 8828                breakpoint_display_points.insert(
 8829                    position.row(),
 8830                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8831                );
 8832            }
 8833        }
 8834
 8835        breakpoint_display_points
 8836    }
 8837
 8838    fn breakpoint_context_menu(
 8839        &self,
 8840        anchor: Anchor,
 8841        window: &mut Window,
 8842        cx: &mut Context<Self>,
 8843    ) -> Entity<ui::ContextMenu> {
 8844        let weak_editor = cx.weak_entity();
 8845        let focus_handle = self.focus_handle(cx);
 8846
 8847        let row = self
 8848            .buffer
 8849            .read(cx)
 8850            .snapshot(cx)
 8851            .summary_for_anchor::<Point>(&anchor)
 8852            .row;
 8853
 8854        let breakpoint = self
 8855            .breakpoint_at_row(row, window, cx)
 8856            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8857
 8858        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8859            "Edit Log Breakpoint"
 8860        } else {
 8861            "Set Log Breakpoint"
 8862        };
 8863
 8864        let condition_breakpoint_msg = if breakpoint
 8865            .as_ref()
 8866            .is_some_and(|bp| bp.1.condition.is_some())
 8867        {
 8868            "Edit Condition Breakpoint"
 8869        } else {
 8870            "Set Condition Breakpoint"
 8871        };
 8872
 8873        let hit_condition_breakpoint_msg = if breakpoint
 8874            .as_ref()
 8875            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8876        {
 8877            "Edit Hit Condition Breakpoint"
 8878        } else {
 8879            "Set Hit Condition Breakpoint"
 8880        };
 8881
 8882        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8883            "Unset Breakpoint"
 8884        } else {
 8885            "Set Breakpoint"
 8886        };
 8887
 8888        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8889
 8890        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8891            BreakpointState::Enabled => Some("Disable"),
 8892            BreakpointState::Disabled => Some("Enable"),
 8893        });
 8894
 8895        let (anchor, breakpoint) =
 8896            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8897
 8898        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8899            menu.on_blur_subscription(Subscription::new(|| {}))
 8900                .context(focus_handle)
 8901                .when(run_to_cursor, |this| {
 8902                    let weak_editor = weak_editor.clone();
 8903                    this.entry("Run to Cursor", None, move |window, cx| {
 8904                        weak_editor
 8905                            .update(cx, |editor, cx| {
 8906                                editor.change_selections(
 8907                                    SelectionEffects::no_scroll(),
 8908                                    window,
 8909                                    cx,
 8910                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8911                                );
 8912                            })
 8913                            .ok();
 8914
 8915                        window.dispatch_action(Box::new(RunToCursor), cx);
 8916                    })
 8917                    .separator()
 8918                })
 8919                .when_some(toggle_state_msg, |this, msg| {
 8920                    this.entry(msg, None, {
 8921                        let weak_editor = weak_editor.clone();
 8922                        let breakpoint = breakpoint.clone();
 8923                        move |_window, cx| {
 8924                            weak_editor
 8925                                .update(cx, |this, cx| {
 8926                                    this.edit_breakpoint_at_anchor(
 8927                                        anchor,
 8928                                        breakpoint.as_ref().clone(),
 8929                                        BreakpointEditAction::InvertState,
 8930                                        cx,
 8931                                    );
 8932                                })
 8933                                .log_err();
 8934                        }
 8935                    })
 8936                })
 8937                .entry(set_breakpoint_msg, None, {
 8938                    let weak_editor = weak_editor.clone();
 8939                    let breakpoint = breakpoint.clone();
 8940                    move |_window, cx| {
 8941                        weak_editor
 8942                            .update(cx, |this, cx| {
 8943                                this.edit_breakpoint_at_anchor(
 8944                                    anchor,
 8945                                    breakpoint.as_ref().clone(),
 8946                                    BreakpointEditAction::Toggle,
 8947                                    cx,
 8948                                );
 8949                            })
 8950                            .log_err();
 8951                    }
 8952                })
 8953                .entry(log_breakpoint_msg, None, {
 8954                    let breakpoint = breakpoint.clone();
 8955                    let weak_editor = weak_editor.clone();
 8956                    move |window, cx| {
 8957                        weak_editor
 8958                            .update(cx, |this, cx| {
 8959                                this.add_edit_breakpoint_block(
 8960                                    anchor,
 8961                                    breakpoint.as_ref(),
 8962                                    BreakpointPromptEditAction::Log,
 8963                                    window,
 8964                                    cx,
 8965                                );
 8966                            })
 8967                            .log_err();
 8968                    }
 8969                })
 8970                .entry(condition_breakpoint_msg, None, {
 8971                    let breakpoint = breakpoint.clone();
 8972                    let weak_editor = weak_editor.clone();
 8973                    move |window, cx| {
 8974                        weak_editor
 8975                            .update(cx, |this, cx| {
 8976                                this.add_edit_breakpoint_block(
 8977                                    anchor,
 8978                                    breakpoint.as_ref(),
 8979                                    BreakpointPromptEditAction::Condition,
 8980                                    window,
 8981                                    cx,
 8982                                );
 8983                            })
 8984                            .log_err();
 8985                    }
 8986                })
 8987                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8988                    weak_editor
 8989                        .update(cx, |this, cx| {
 8990                            this.add_edit_breakpoint_block(
 8991                                anchor,
 8992                                breakpoint.as_ref(),
 8993                                BreakpointPromptEditAction::HitCondition,
 8994                                window,
 8995                                cx,
 8996                            );
 8997                        })
 8998                        .log_err();
 8999                })
 9000        })
 9001    }
 9002
 9003    fn render_breakpoint(
 9004        &self,
 9005        position: Anchor,
 9006        row: DisplayRow,
 9007        breakpoint: &Breakpoint,
 9008        state: Option<BreakpointSessionState>,
 9009        cx: &mut Context<Self>,
 9010    ) -> IconButton {
 9011        let is_rejected = state.is_some_and(|s| !s.verified);
 9012        // Is it a breakpoint that shows up when hovering over gutter?
 9013        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9014            (false, false),
 9015            |PhantomBreakpointIndicator {
 9016                 is_active,
 9017                 display_row,
 9018                 collides_with_existing_breakpoint,
 9019             }| {
 9020                (
 9021                    is_active && display_row == row,
 9022                    collides_with_existing_breakpoint,
 9023                )
 9024            },
 9025        );
 9026
 9027        let (color, icon) = {
 9028            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9029                (false, false) => ui::IconName::DebugBreakpoint,
 9030                (true, false) => ui::IconName::DebugLogBreakpoint,
 9031                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9032                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9033            };
 9034
 9035            let theme_colors = cx.theme().colors();
 9036
 9037            let color = if is_phantom {
 9038                if collides_with_existing {
 9039                    Color::Custom(
 9040                        theme_colors
 9041                            .debugger_accent
 9042                            .blend(theme_colors.text.opacity(0.6)),
 9043                    )
 9044                } else {
 9045                    Color::Hint
 9046                }
 9047            } else if is_rejected {
 9048                Color::Disabled
 9049            } else {
 9050                Color::Debugger
 9051            };
 9052
 9053            (color, icon)
 9054        };
 9055
 9056        let breakpoint = Arc::from(breakpoint.clone());
 9057
 9058        let alt_as_text = gpui::Keystroke {
 9059            modifiers: Modifiers::secondary_key(),
 9060            ..Default::default()
 9061        };
 9062        let primary_action_text = if breakpoint.is_disabled() {
 9063            "Enable breakpoint"
 9064        } else if is_phantom && !collides_with_existing {
 9065            "Set breakpoint"
 9066        } else {
 9067            "Unset breakpoint"
 9068        };
 9069        let focus_handle = self.focus_handle.clone();
 9070
 9071        let meta = if is_rejected {
 9072            SharedString::from("No executable code is associated with this line.")
 9073        } else if collides_with_existing && !breakpoint.is_disabled() {
 9074            SharedString::from(format!(
 9075                "{alt_as_text}-click to disable,\nright-click for more options."
 9076            ))
 9077        } else {
 9078            SharedString::from("Right-click for more options.")
 9079        };
 9080        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9081            .icon_size(IconSize::XSmall)
 9082            .size(ui::ButtonSize::None)
 9083            .when(is_rejected, |this| {
 9084                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9085            })
 9086            .icon_color(color)
 9087            .style(ButtonStyle::Transparent)
 9088            .on_click(cx.listener({
 9089                move |editor, event: &ClickEvent, window, cx| {
 9090                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9091                        BreakpointEditAction::InvertState
 9092                    } else {
 9093                        BreakpointEditAction::Toggle
 9094                    };
 9095
 9096                    window.focus(&editor.focus_handle(cx), cx);
 9097                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9098                    editor.edit_breakpoint_at_anchor(
 9099                        position,
 9100                        breakpoint.as_ref().clone(),
 9101                        edit_action,
 9102                        cx,
 9103                    );
 9104                }
 9105            }))
 9106            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9107                editor.set_breakpoint_context_menu(
 9108                    row,
 9109                    Some(position),
 9110                    event.position(),
 9111                    window,
 9112                    cx,
 9113                );
 9114            }))
 9115            .tooltip(move |_window, cx| {
 9116                Tooltip::with_meta_in(
 9117                    primary_action_text,
 9118                    Some(&ToggleBreakpoint),
 9119                    meta.clone(),
 9120                    &focus_handle,
 9121                    cx,
 9122                )
 9123            })
 9124    }
 9125
 9126    fn build_tasks_context(
 9127        project: &Entity<Project>,
 9128        buffer: &Entity<Buffer>,
 9129        buffer_row: u32,
 9130        tasks: &Arc<RunnableTasks>,
 9131        cx: &mut Context<Self>,
 9132    ) -> Task<Option<task::TaskContext>> {
 9133        let position = Point::new(buffer_row, tasks.column);
 9134        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9135        let location = Location {
 9136            buffer: buffer.clone(),
 9137            range: range_start..range_start,
 9138        };
 9139        // Fill in the environmental variables from the tree-sitter captures
 9140        let mut captured_task_variables = TaskVariables::default();
 9141        for (capture_name, value) in tasks.extra_variables.clone() {
 9142            captured_task_variables.insert(
 9143                task::VariableName::Custom(capture_name.into()),
 9144                value.clone(),
 9145            );
 9146        }
 9147        project.update(cx, |project, cx| {
 9148            project.task_store().update(cx, |task_store, cx| {
 9149                task_store.task_context_for_location(captured_task_variables, location, cx)
 9150            })
 9151        })
 9152    }
 9153
 9154    pub fn context_menu_visible(&self) -> bool {
 9155        !self.edit_prediction_preview_is_active()
 9156            && self
 9157                .context_menu
 9158                .borrow()
 9159                .as_ref()
 9160                .is_some_and(|menu| menu.visible())
 9161    }
 9162
 9163    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9164        self.context_menu
 9165            .borrow()
 9166            .as_ref()
 9167            .map(|menu| menu.origin())
 9168    }
 9169
 9170    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9171        self.context_menu_options = Some(options);
 9172    }
 9173
 9174    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9175    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9176
 9177    fn render_edit_prediction_popover(
 9178        &mut self,
 9179        text_bounds: &Bounds<Pixels>,
 9180        content_origin: gpui::Point<Pixels>,
 9181        right_margin: Pixels,
 9182        editor_snapshot: &EditorSnapshot,
 9183        visible_row_range: Range<DisplayRow>,
 9184        scroll_top: ScrollOffset,
 9185        scroll_bottom: ScrollOffset,
 9186        line_layouts: &[LineWithInvisibles],
 9187        line_height: Pixels,
 9188        scroll_position: gpui::Point<ScrollOffset>,
 9189        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9190        newest_selection_head: Option<DisplayPoint>,
 9191        editor_width: Pixels,
 9192        style: &EditorStyle,
 9193        window: &mut Window,
 9194        cx: &mut App,
 9195    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9196        if self.mode().is_minimap() {
 9197            return None;
 9198        }
 9199        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9200
 9201        if self.edit_prediction_visible_in_cursor_popover(true) {
 9202            return None;
 9203        }
 9204
 9205        match &active_edit_prediction.completion {
 9206            EditPrediction::MoveWithin { target, .. } => {
 9207                let target_display_point = target.to_display_point(editor_snapshot);
 9208
 9209                if self.edit_prediction_requires_modifier() {
 9210                    if !self.edit_prediction_preview_is_active() {
 9211                        return None;
 9212                    }
 9213
 9214                    self.render_edit_prediction_modifier_jump_popover(
 9215                        text_bounds,
 9216                        content_origin,
 9217                        visible_row_range,
 9218                        line_layouts,
 9219                        line_height,
 9220                        scroll_pixel_position,
 9221                        newest_selection_head,
 9222                        target_display_point,
 9223                        window,
 9224                        cx,
 9225                    )
 9226                } else {
 9227                    self.render_edit_prediction_eager_jump_popover(
 9228                        text_bounds,
 9229                        content_origin,
 9230                        editor_snapshot,
 9231                        visible_row_range,
 9232                        scroll_top,
 9233                        scroll_bottom,
 9234                        line_height,
 9235                        scroll_pixel_position,
 9236                        target_display_point,
 9237                        editor_width,
 9238                        window,
 9239                        cx,
 9240                    )
 9241                }
 9242            }
 9243            EditPrediction::Edit {
 9244                display_mode: EditDisplayMode::Inline,
 9245                ..
 9246            } => None,
 9247            EditPrediction::Edit {
 9248                display_mode: EditDisplayMode::TabAccept,
 9249                edits,
 9250                ..
 9251            } => {
 9252                let range = &edits.first()?.0;
 9253                let target_display_point = range.end.to_display_point(editor_snapshot);
 9254
 9255                self.render_edit_prediction_end_of_line_popover(
 9256                    "Accept",
 9257                    editor_snapshot,
 9258                    visible_row_range,
 9259                    target_display_point,
 9260                    line_height,
 9261                    scroll_pixel_position,
 9262                    content_origin,
 9263                    editor_width,
 9264                    window,
 9265                    cx,
 9266                )
 9267            }
 9268            EditPrediction::Edit {
 9269                edits,
 9270                edit_preview,
 9271                display_mode: EditDisplayMode::DiffPopover,
 9272                snapshot,
 9273                ..
 9274            } => self.render_edit_prediction_diff_popover(
 9275                text_bounds,
 9276                content_origin,
 9277                right_margin,
 9278                editor_snapshot,
 9279                visible_row_range,
 9280                line_layouts,
 9281                line_height,
 9282                scroll_position,
 9283                scroll_pixel_position,
 9284                newest_selection_head,
 9285                editor_width,
 9286                style,
 9287                edits,
 9288                edit_preview,
 9289                snapshot,
 9290                window,
 9291                cx,
 9292            ),
 9293            EditPrediction::MoveOutside { snapshot, .. } => {
 9294                let mut element = self
 9295                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9296                    .into_any();
 9297
 9298                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9299                let origin_x = text_bounds.size.width - size.width - px(30.);
 9300                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9301                element.prepaint_at(origin, window, cx);
 9302
 9303                Some((element, origin))
 9304            }
 9305        }
 9306    }
 9307
 9308    fn render_edit_prediction_modifier_jump_popover(
 9309        &mut self,
 9310        text_bounds: &Bounds<Pixels>,
 9311        content_origin: gpui::Point<Pixels>,
 9312        visible_row_range: Range<DisplayRow>,
 9313        line_layouts: &[LineWithInvisibles],
 9314        line_height: Pixels,
 9315        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9316        newest_selection_head: Option<DisplayPoint>,
 9317        target_display_point: DisplayPoint,
 9318        window: &mut Window,
 9319        cx: &mut App,
 9320    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9321        let scrolled_content_origin =
 9322            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9323
 9324        const SCROLL_PADDING_Y: Pixels = px(12.);
 9325
 9326        if target_display_point.row() < visible_row_range.start {
 9327            return self.render_edit_prediction_scroll_popover(
 9328                &|_| SCROLL_PADDING_Y,
 9329                IconName::ArrowUp,
 9330                visible_row_range,
 9331                line_layouts,
 9332                newest_selection_head,
 9333                scrolled_content_origin,
 9334                window,
 9335                cx,
 9336            );
 9337        } else if target_display_point.row() >= visible_row_range.end {
 9338            return self.render_edit_prediction_scroll_popover(
 9339                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9340                IconName::ArrowDown,
 9341                visible_row_range,
 9342                line_layouts,
 9343                newest_selection_head,
 9344                scrolled_content_origin,
 9345                window,
 9346                cx,
 9347            );
 9348        }
 9349
 9350        const POLE_WIDTH: Pixels = px(2.);
 9351
 9352        let line_layout =
 9353            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9354        let target_column = target_display_point.column() as usize;
 9355
 9356        let target_x = line_layout.x_for_index(target_column);
 9357        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9358            - scroll_pixel_position.y;
 9359
 9360        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9361
 9362        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9363        border_color.l += 0.001;
 9364
 9365        let mut element = v_flex()
 9366            .items_end()
 9367            .when(flag_on_right, |el| el.items_start())
 9368            .child(if flag_on_right {
 9369                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9370                    .rounded_bl(px(0.))
 9371                    .rounded_tl(px(0.))
 9372                    .border_l_2()
 9373                    .border_color(border_color)
 9374            } else {
 9375                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9376                    .rounded_br(px(0.))
 9377                    .rounded_tr(px(0.))
 9378                    .border_r_2()
 9379                    .border_color(border_color)
 9380            })
 9381            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9382            .into_any();
 9383
 9384        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9385
 9386        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9387            - point(
 9388                if flag_on_right {
 9389                    POLE_WIDTH
 9390                } else {
 9391                    size.width - POLE_WIDTH
 9392                },
 9393                size.height - line_height,
 9394            );
 9395
 9396        origin.x = origin.x.max(content_origin.x);
 9397
 9398        element.prepaint_at(origin, window, cx);
 9399
 9400        Some((element, origin))
 9401    }
 9402
 9403    fn render_edit_prediction_scroll_popover(
 9404        &mut self,
 9405        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9406        scroll_icon: IconName,
 9407        visible_row_range: Range<DisplayRow>,
 9408        line_layouts: &[LineWithInvisibles],
 9409        newest_selection_head: Option<DisplayPoint>,
 9410        scrolled_content_origin: gpui::Point<Pixels>,
 9411        window: &mut Window,
 9412        cx: &mut App,
 9413    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9414        let mut element = self
 9415            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9416            .into_any();
 9417
 9418        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9419
 9420        let cursor = newest_selection_head?;
 9421        let cursor_row_layout =
 9422            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9423        let cursor_column = cursor.column() as usize;
 9424
 9425        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9426
 9427        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9428
 9429        element.prepaint_at(origin, window, cx);
 9430        Some((element, origin))
 9431    }
 9432
 9433    fn render_edit_prediction_eager_jump_popover(
 9434        &mut self,
 9435        text_bounds: &Bounds<Pixels>,
 9436        content_origin: gpui::Point<Pixels>,
 9437        editor_snapshot: &EditorSnapshot,
 9438        visible_row_range: Range<DisplayRow>,
 9439        scroll_top: ScrollOffset,
 9440        scroll_bottom: ScrollOffset,
 9441        line_height: Pixels,
 9442        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9443        target_display_point: DisplayPoint,
 9444        editor_width: Pixels,
 9445        window: &mut Window,
 9446        cx: &mut App,
 9447    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9448        if target_display_point.row().as_f64() < scroll_top {
 9449            let mut element = self
 9450                .render_edit_prediction_line_popover(
 9451                    "Jump to Edit",
 9452                    Some(IconName::ArrowUp),
 9453                    window,
 9454                    cx,
 9455                )
 9456                .into_any();
 9457
 9458            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9459            let offset = point(
 9460                (text_bounds.size.width - size.width) / 2.,
 9461                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9462            );
 9463
 9464            let origin = text_bounds.origin + offset;
 9465            element.prepaint_at(origin, window, cx);
 9466            Some((element, origin))
 9467        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9468            let mut element = self
 9469                .render_edit_prediction_line_popover(
 9470                    "Jump to Edit",
 9471                    Some(IconName::ArrowDown),
 9472                    window,
 9473                    cx,
 9474                )
 9475                .into_any();
 9476
 9477            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9478            let offset = point(
 9479                (text_bounds.size.width - size.width) / 2.,
 9480                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9481            );
 9482
 9483            let origin = text_bounds.origin + offset;
 9484            element.prepaint_at(origin, window, cx);
 9485            Some((element, origin))
 9486        } else {
 9487            self.render_edit_prediction_end_of_line_popover(
 9488                "Jump to Edit",
 9489                editor_snapshot,
 9490                visible_row_range,
 9491                target_display_point,
 9492                line_height,
 9493                scroll_pixel_position,
 9494                content_origin,
 9495                editor_width,
 9496                window,
 9497                cx,
 9498            )
 9499        }
 9500    }
 9501
 9502    fn render_edit_prediction_end_of_line_popover(
 9503        self: &mut Editor,
 9504        label: &'static str,
 9505        editor_snapshot: &EditorSnapshot,
 9506        visible_row_range: Range<DisplayRow>,
 9507        target_display_point: DisplayPoint,
 9508        line_height: Pixels,
 9509        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9510        content_origin: gpui::Point<Pixels>,
 9511        editor_width: Pixels,
 9512        window: &mut Window,
 9513        cx: &mut App,
 9514    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9515        let target_line_end = DisplayPoint::new(
 9516            target_display_point.row(),
 9517            editor_snapshot.line_len(target_display_point.row()),
 9518        );
 9519
 9520        let mut element = self
 9521            .render_edit_prediction_line_popover(label, None, window, cx)
 9522            .into_any();
 9523
 9524        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9525
 9526        let line_origin =
 9527            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9528
 9529        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9530        let mut origin = start_point
 9531            + line_origin
 9532            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9533        origin.x = origin.x.max(content_origin.x);
 9534
 9535        let max_x = content_origin.x + editor_width - size.width;
 9536
 9537        if origin.x > max_x {
 9538            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9539
 9540            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9541                origin.y += offset;
 9542                IconName::ArrowUp
 9543            } else {
 9544                origin.y -= offset;
 9545                IconName::ArrowDown
 9546            };
 9547
 9548            element = self
 9549                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9550                .into_any();
 9551
 9552            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9553
 9554            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9555        }
 9556
 9557        element.prepaint_at(origin, window, cx);
 9558        Some((element, origin))
 9559    }
 9560
 9561    fn render_edit_prediction_diff_popover(
 9562        self: &Editor,
 9563        text_bounds: &Bounds<Pixels>,
 9564        content_origin: gpui::Point<Pixels>,
 9565        right_margin: Pixels,
 9566        editor_snapshot: &EditorSnapshot,
 9567        visible_row_range: Range<DisplayRow>,
 9568        line_layouts: &[LineWithInvisibles],
 9569        line_height: Pixels,
 9570        scroll_position: gpui::Point<ScrollOffset>,
 9571        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9572        newest_selection_head: Option<DisplayPoint>,
 9573        editor_width: Pixels,
 9574        style: &EditorStyle,
 9575        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9576        edit_preview: &Option<language::EditPreview>,
 9577        snapshot: &language::BufferSnapshot,
 9578        window: &mut Window,
 9579        cx: &mut App,
 9580    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9581        let edit_start = edits
 9582            .first()
 9583            .unwrap()
 9584            .0
 9585            .start
 9586            .to_display_point(editor_snapshot);
 9587        let edit_end = edits
 9588            .last()
 9589            .unwrap()
 9590            .0
 9591            .end
 9592            .to_display_point(editor_snapshot);
 9593
 9594        let is_visible = visible_row_range.contains(&edit_start.row())
 9595            || visible_row_range.contains(&edit_end.row());
 9596        if !is_visible {
 9597            return None;
 9598        }
 9599
 9600        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9601            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9602        } else {
 9603            // Fallback for providers without edit_preview
 9604            crate::edit_prediction_fallback_text(edits, cx)
 9605        };
 9606
 9607        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9608        let line_count = highlighted_edits.text.lines().count();
 9609
 9610        const BORDER_WIDTH: Pixels = px(1.);
 9611
 9612        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9613        let has_keybind = keybind.is_some();
 9614
 9615        let mut element = h_flex()
 9616            .items_start()
 9617            .child(
 9618                h_flex()
 9619                    .bg(cx.theme().colors().editor_background)
 9620                    .border(BORDER_WIDTH)
 9621                    .shadow_xs()
 9622                    .border_color(cx.theme().colors().border)
 9623                    .rounded_l_lg()
 9624                    .when(line_count > 1, |el| el.rounded_br_lg())
 9625                    .pr_1()
 9626                    .child(styled_text),
 9627            )
 9628            .child(
 9629                h_flex()
 9630                    .h(line_height + BORDER_WIDTH * 2.)
 9631                    .px_1p5()
 9632                    .gap_1()
 9633                    // Workaround: For some reason, there's a gap if we don't do this
 9634                    .ml(-BORDER_WIDTH)
 9635                    .shadow(vec![gpui::BoxShadow {
 9636                        color: gpui::black().opacity(0.05),
 9637                        offset: point(px(1.), px(1.)),
 9638                        blur_radius: px(2.),
 9639                        spread_radius: px(0.),
 9640                    }])
 9641                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9642                    .border(BORDER_WIDTH)
 9643                    .border_color(cx.theme().colors().border)
 9644                    .rounded_r_lg()
 9645                    .id("edit_prediction_diff_popover_keybind")
 9646                    .when(!has_keybind, |el| {
 9647                        let status_colors = cx.theme().status();
 9648
 9649                        el.bg(status_colors.error_background)
 9650                            .border_color(status_colors.error.opacity(0.6))
 9651                            .child(Icon::new(IconName::Info).color(Color::Error))
 9652                            .cursor_default()
 9653                            .hoverable_tooltip(move |_window, cx| {
 9654                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9655                            })
 9656                    })
 9657                    .children(keybind),
 9658            )
 9659            .into_any();
 9660
 9661        let longest_row =
 9662            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9663        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9664            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9665        } else {
 9666            layout_line(
 9667                longest_row,
 9668                editor_snapshot,
 9669                style,
 9670                editor_width,
 9671                |_| false,
 9672                window,
 9673                cx,
 9674            )
 9675            .width
 9676        };
 9677
 9678        let viewport_bounds =
 9679            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9680                right: -right_margin,
 9681                ..Default::default()
 9682            });
 9683
 9684        let x_after_longest = Pixels::from(
 9685            ScrollPixelOffset::from(
 9686                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9687            ) - scroll_pixel_position.x,
 9688        );
 9689
 9690        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9691
 9692        // Fully visible if it can be displayed within the window (allow overlapping other
 9693        // panes). However, this is only allowed if the popover starts within text_bounds.
 9694        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9695            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9696
 9697        let mut origin = if can_position_to_the_right {
 9698            point(
 9699                x_after_longest,
 9700                text_bounds.origin.y
 9701                    + Pixels::from(
 9702                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9703                            - scroll_pixel_position.y,
 9704                    ),
 9705            )
 9706        } else {
 9707            let cursor_row = newest_selection_head.map(|head| head.row());
 9708            let above_edit = edit_start
 9709                .row()
 9710                .0
 9711                .checked_sub(line_count as u32)
 9712                .map(DisplayRow);
 9713            let below_edit = Some(edit_end.row() + 1);
 9714            let above_cursor =
 9715                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9716            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9717
 9718            // Place the edit popover adjacent to the edit if there is a location
 9719            // available that is onscreen and does not obscure the cursor. Otherwise,
 9720            // place it adjacent to the cursor.
 9721            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9722                .into_iter()
 9723                .flatten()
 9724                .find(|&start_row| {
 9725                    let end_row = start_row + line_count as u32;
 9726                    visible_row_range.contains(&start_row)
 9727                        && visible_row_range.contains(&end_row)
 9728                        && cursor_row
 9729                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9730                })?;
 9731
 9732            content_origin
 9733                + point(
 9734                    Pixels::from(-scroll_pixel_position.x),
 9735                    Pixels::from(
 9736                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9737                    ),
 9738                )
 9739        };
 9740
 9741        origin.x -= BORDER_WIDTH;
 9742
 9743        window.with_content_mask(
 9744            Some(gpui::ContentMask {
 9745                bounds: *text_bounds,
 9746            }),
 9747            |window| {
 9748                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9749            },
 9750        );
 9751
 9752        // Do not return an element, since it will already be drawn due to defer_draw.
 9753        None
 9754    }
 9755
 9756    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9757        px(30.)
 9758    }
 9759
 9760    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9761        if self.read_only(cx) {
 9762            cx.theme().players().read_only()
 9763        } else {
 9764            self.style.as_ref().unwrap().local_player
 9765        }
 9766    }
 9767
 9768    fn render_edit_prediction_accept_keybind(
 9769        &self,
 9770        window: &mut Window,
 9771        cx: &mut App,
 9772    ) -> Option<AnyElement> {
 9773        let accept_binding =
 9774            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9775        let accept_keystroke = accept_binding.keystroke()?;
 9776
 9777        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9778
 9779        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9780            Color::Accent
 9781        } else {
 9782            Color::Muted
 9783        };
 9784
 9785        h_flex()
 9786            .px_0p5()
 9787            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9788            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9789            .text_size(TextSize::XSmall.rems(cx))
 9790            .child(h_flex().children(ui::render_modifiers(
 9791                accept_keystroke.modifiers(),
 9792                PlatformStyle::platform(),
 9793                Some(modifiers_color),
 9794                Some(IconSize::XSmall.rems().into()),
 9795                true,
 9796            )))
 9797            .when(is_platform_style_mac, |parent| {
 9798                parent.child(accept_keystroke.key().to_string())
 9799            })
 9800            .when(!is_platform_style_mac, |parent| {
 9801                parent.child(
 9802                    Key::new(
 9803                        util::capitalize(accept_keystroke.key()),
 9804                        Some(Color::Default),
 9805                    )
 9806                    .size(Some(IconSize::XSmall.rems().into())),
 9807                )
 9808            })
 9809            .into_any()
 9810            .into()
 9811    }
 9812
 9813    fn render_edit_prediction_line_popover(
 9814        &self,
 9815        label: impl Into<SharedString>,
 9816        icon: Option<IconName>,
 9817        window: &mut Window,
 9818        cx: &mut App,
 9819    ) -> Stateful<Div> {
 9820        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9821
 9822        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9823        let has_keybind = keybind.is_some();
 9824        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9825
 9826        h_flex()
 9827            .id("ep-line-popover")
 9828            .py_0p5()
 9829            .pl_1()
 9830            .pr(padding_right)
 9831            .gap_1()
 9832            .rounded_md()
 9833            .border_1()
 9834            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9835            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9836            .shadow_xs()
 9837            .when(!has_keybind, |el| {
 9838                let status_colors = cx.theme().status();
 9839
 9840                el.bg(status_colors.error_background)
 9841                    .border_color(status_colors.error.opacity(0.6))
 9842                    .pl_2()
 9843                    .child(Icon::new(icons.error).color(Color::Error))
 9844                    .cursor_default()
 9845                    .hoverable_tooltip(move |_window, cx| {
 9846                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9847                    })
 9848            })
 9849            .children(keybind)
 9850            .child(
 9851                Label::new(label)
 9852                    .size(LabelSize::Small)
 9853                    .when(!has_keybind, |el| {
 9854                        el.color(cx.theme().status().error.into()).strikethrough()
 9855                    }),
 9856            )
 9857            .when(!has_keybind, |el| {
 9858                el.child(
 9859                    h_flex().ml_1().child(
 9860                        Icon::new(IconName::Info)
 9861                            .size(IconSize::Small)
 9862                            .color(cx.theme().status().error.into()),
 9863                    ),
 9864                )
 9865            })
 9866            .when_some(icon, |element, icon| {
 9867                element.child(
 9868                    div()
 9869                        .mt(px(1.5))
 9870                        .child(Icon::new(icon).size(IconSize::Small)),
 9871                )
 9872            })
 9873    }
 9874
 9875    fn render_edit_prediction_jump_outside_popover(
 9876        &self,
 9877        snapshot: &BufferSnapshot,
 9878        window: &mut Window,
 9879        cx: &mut App,
 9880    ) -> Stateful<Div> {
 9881        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9882        let has_keybind = keybind.is_some();
 9883        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9884
 9885        let file_name = snapshot
 9886            .file()
 9887            .map(|file| SharedString::new(file.file_name(cx)))
 9888            .unwrap_or(SharedString::new_static("untitled"));
 9889
 9890        h_flex()
 9891            .id("ep-jump-outside-popover")
 9892            .py_1()
 9893            .px_2()
 9894            .gap_1()
 9895            .rounded_md()
 9896            .border_1()
 9897            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9898            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9899            .shadow_xs()
 9900            .when(!has_keybind, |el| {
 9901                let status_colors = cx.theme().status();
 9902
 9903                el.bg(status_colors.error_background)
 9904                    .border_color(status_colors.error.opacity(0.6))
 9905                    .pl_2()
 9906                    .child(Icon::new(icons.error).color(Color::Error))
 9907                    .cursor_default()
 9908                    .hoverable_tooltip(move |_window, cx| {
 9909                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9910                    })
 9911            })
 9912            .children(keybind)
 9913            .child(
 9914                Label::new(file_name)
 9915                    .size(LabelSize::Small)
 9916                    .buffer_font(cx)
 9917                    .when(!has_keybind, |el| {
 9918                        el.color(cx.theme().status().error.into()).strikethrough()
 9919                    }),
 9920            )
 9921            .when(!has_keybind, |el| {
 9922                el.child(
 9923                    h_flex().ml_1().child(
 9924                        Icon::new(IconName::Info)
 9925                            .size(IconSize::Small)
 9926                            .color(cx.theme().status().error.into()),
 9927                    ),
 9928                )
 9929            })
 9930            .child(
 9931                div()
 9932                    .mt(px(1.5))
 9933                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
 9934            )
 9935    }
 9936
 9937    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
 9938        let accent_color = cx.theme().colors().text_accent;
 9939        let editor_bg_color = cx.theme().colors().editor_background;
 9940        editor_bg_color.blend(accent_color.opacity(0.1))
 9941    }
 9942
 9943    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
 9944        let accent_color = cx.theme().colors().text_accent;
 9945        let editor_bg_color = cx.theme().colors().editor_background;
 9946        editor_bg_color.blend(accent_color.opacity(0.6))
 9947    }
 9948    fn get_prediction_provider_icons(
 9949        provider: &Option<RegisteredEditPredictionDelegate>,
 9950        cx: &App,
 9951    ) -> edit_prediction_types::EditPredictionIconSet {
 9952        match provider {
 9953            Some(provider) => provider.provider.icons(cx),
 9954            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
 9955        }
 9956    }
 9957
 9958    fn render_edit_prediction_cursor_popover(
 9959        &self,
 9960        min_width: Pixels,
 9961        max_width: Pixels,
 9962        cursor_point: Point,
 9963        style: &EditorStyle,
 9964        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
 9965        _window: &Window,
 9966        cx: &mut Context<Editor>,
 9967    ) -> Option<AnyElement> {
 9968        let provider = self.edit_prediction_provider.as_ref()?;
 9969        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9970
 9971        let is_refreshing = provider.provider.is_refreshing(cx);
 9972
 9973        fn pending_completion_container(icon: IconName) -> Div {
 9974            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
 9975        }
 9976
 9977        let completion = match &self.active_edit_prediction {
 9978            Some(prediction) => {
 9979                if !self.has_visible_completions_menu() {
 9980                    const RADIUS: Pixels = px(6.);
 9981                    const BORDER_WIDTH: Pixels = px(1.);
 9982
 9983                    return Some(
 9984                        h_flex()
 9985                            .elevation_2(cx)
 9986                            .border(BORDER_WIDTH)
 9987                            .border_color(cx.theme().colors().border)
 9988                            .when(accept_keystroke.is_none(), |el| {
 9989                                el.border_color(cx.theme().status().error)
 9990                            })
 9991                            .rounded(RADIUS)
 9992                            .rounded_tl(px(0.))
 9993                            .overflow_hidden()
 9994                            .child(div().px_1p5().child(match &prediction.completion {
 9995                                EditPrediction::MoveWithin { target, snapshot } => {
 9996                                    use text::ToPoint as _;
 9997                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
 9998                                    {
 9999                                        Icon::new(icons.down)
10000                                    } else {
10001                                        Icon::new(icons.up)
10002                                    }
10003                                }
10004                                EditPrediction::MoveOutside { .. } => {
10005                                    // TODO [zeta2] custom icon for external jump?
10006                                    Icon::new(icons.base)
10007                                }
10008                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10009                            }))
10010                            .child(
10011                                h_flex()
10012                                    .gap_1()
10013                                    .py_1()
10014                                    .px_2()
10015                                    .rounded_r(RADIUS - BORDER_WIDTH)
10016                                    .border_l_1()
10017                                    .border_color(cx.theme().colors().border)
10018                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10019                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10020                                        el.child(
10021                                            Label::new("Hold")
10022                                                .size(LabelSize::Small)
10023                                                .when(accept_keystroke.is_none(), |el| {
10024                                                    el.strikethrough()
10025                                                })
10026                                                .line_height_style(LineHeightStyle::UiLabel),
10027                                        )
10028                                    })
10029                                    .id("edit_prediction_cursor_popover_keybind")
10030                                    .when(accept_keystroke.is_none(), |el| {
10031                                        let status_colors = cx.theme().status();
10032
10033                                        el.bg(status_colors.error_background)
10034                                            .border_color(status_colors.error.opacity(0.6))
10035                                            .child(Icon::new(IconName::Info).color(Color::Error))
10036                                            .cursor_default()
10037                                            .hoverable_tooltip(move |_window, cx| {
10038                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10039                                                    .into()
10040                                            })
10041                                    })
10042                                    .when_some(
10043                                        accept_keystroke.as_ref(),
10044                                        |el, accept_keystroke| {
10045                                            el.child(h_flex().children(ui::render_modifiers(
10046                                                accept_keystroke.modifiers(),
10047                                                PlatformStyle::platform(),
10048                                                Some(Color::Default),
10049                                                Some(IconSize::XSmall.rems().into()),
10050                                                false,
10051                                            )))
10052                                        },
10053                                    ),
10054                            )
10055                            .into_any(),
10056                    );
10057                }
10058
10059                self.render_edit_prediction_cursor_popover_preview(
10060                    prediction,
10061                    cursor_point,
10062                    style,
10063                    cx,
10064                )?
10065            }
10066
10067            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10068                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10069                    stale_completion,
10070                    cursor_point,
10071                    style,
10072                    cx,
10073                )?,
10074
10075                None => pending_completion_container(icons.base)
10076                    .child(Label::new("...").size(LabelSize::Small)),
10077            },
10078
10079            None => pending_completion_container(icons.base)
10080                .child(Label::new("...").size(LabelSize::Small)),
10081        };
10082
10083        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10084            completion
10085                .with_animation(
10086                    "loading-completion",
10087                    Animation::new(Duration::from_secs(2))
10088                        .repeat()
10089                        .with_easing(pulsating_between(0.4, 0.8)),
10090                    |label, delta| label.opacity(delta),
10091                )
10092                .into_any_element()
10093        } else {
10094            completion.into_any_element()
10095        };
10096
10097        let has_completion = self.active_edit_prediction.is_some();
10098
10099        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10100        Some(
10101            h_flex()
10102                .min_w(min_width)
10103                .max_w(max_width)
10104                .flex_1()
10105                .elevation_2(cx)
10106                .border_color(cx.theme().colors().border)
10107                .child(
10108                    div()
10109                        .flex_1()
10110                        .py_1()
10111                        .px_2()
10112                        .overflow_hidden()
10113                        .child(completion),
10114                )
10115                .when_some(accept_keystroke, |el, accept_keystroke| {
10116                    if !accept_keystroke.modifiers().modified() {
10117                        return el;
10118                    }
10119
10120                    el.child(
10121                        h_flex()
10122                            .h_full()
10123                            .border_l_1()
10124                            .rounded_r_lg()
10125                            .border_color(cx.theme().colors().border)
10126                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10127                            .gap_1()
10128                            .py_1()
10129                            .px_2()
10130                            .child(
10131                                h_flex()
10132                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10133                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10134                                    .child(h_flex().children(ui::render_modifiers(
10135                                        accept_keystroke.modifiers(),
10136                                        PlatformStyle::platform(),
10137                                        Some(if !has_completion {
10138                                            Color::Muted
10139                                        } else {
10140                                            Color::Default
10141                                        }),
10142                                        None,
10143                                        false,
10144                                    ))),
10145                            )
10146                            .child(Label::new("Preview").into_any_element())
10147                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10148                    )
10149                })
10150                .into_any(),
10151        )
10152    }
10153
10154    fn render_edit_prediction_cursor_popover_preview(
10155        &self,
10156        completion: &EditPredictionState,
10157        cursor_point: Point,
10158        style: &EditorStyle,
10159        cx: &mut Context<Editor>,
10160    ) -> Option<Div> {
10161        use text::ToPoint as _;
10162
10163        fn render_relative_row_jump(
10164            prefix: impl Into<String>,
10165            current_row: u32,
10166            target_row: u32,
10167        ) -> Div {
10168            let (row_diff, arrow) = if target_row < current_row {
10169                (current_row - target_row, IconName::ArrowUp)
10170            } else {
10171                (target_row - current_row, IconName::ArrowDown)
10172            };
10173
10174            h_flex()
10175                .child(
10176                    Label::new(format!("{}{}", prefix.into(), row_diff))
10177                        .color(Color::Muted)
10178                        .size(LabelSize::Small),
10179                )
10180                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10181        }
10182
10183        let supports_jump = self
10184            .edit_prediction_provider
10185            .as_ref()
10186            .map(|provider| provider.provider.supports_jump_to_edit())
10187            .unwrap_or(true);
10188
10189        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10190
10191        match &completion.completion {
10192            EditPrediction::MoveWithin {
10193                target, snapshot, ..
10194            } => {
10195                if !supports_jump {
10196                    return None;
10197                }
10198
10199                Some(
10200                    h_flex()
10201                        .px_2()
10202                        .gap_2()
10203                        .flex_1()
10204                        .child(
10205                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10206                                Icon::new(icons.down)
10207                            } else {
10208                                Icon::new(icons.up)
10209                            },
10210                        )
10211                        .child(Label::new("Jump to Edit")),
10212                )
10213            }
10214            EditPrediction::MoveOutside { snapshot, .. } => {
10215                let file_name = snapshot
10216                    .file()
10217                    .map(|file| file.file_name(cx))
10218                    .unwrap_or("untitled");
10219                Some(
10220                    h_flex()
10221                        .px_2()
10222                        .gap_2()
10223                        .flex_1()
10224                        .child(Icon::new(icons.base))
10225                        .child(Label::new(format!("Jump to {file_name}"))),
10226                )
10227            }
10228            EditPrediction::Edit {
10229                edits,
10230                edit_preview,
10231                snapshot,
10232                ..
10233            } => {
10234                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10235
10236                let (highlighted_edits, has_more_lines) =
10237                    if let Some(edit_preview) = edit_preview.as_ref() {
10238                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10239                            .first_line_preview()
10240                    } else {
10241                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10242                    };
10243
10244                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10245                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10246
10247                let preview = h_flex()
10248                    .gap_1()
10249                    .min_w_16()
10250                    .child(styled_text)
10251                    .when(has_more_lines, |parent| parent.child(""));
10252
10253                let left = if supports_jump && first_edit_row != cursor_point.row {
10254                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10255                        .into_any_element()
10256                } else {
10257                    Icon::new(icons.base).into_any_element()
10258                };
10259
10260                Some(
10261                    h_flex()
10262                        .h_full()
10263                        .flex_1()
10264                        .gap_2()
10265                        .pr_1()
10266                        .overflow_x_hidden()
10267                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10268                        .child(left)
10269                        .child(preview),
10270                )
10271            }
10272        }
10273    }
10274
10275    pub fn render_context_menu(
10276        &mut self,
10277        max_height_in_lines: u32,
10278        window: &mut Window,
10279        cx: &mut Context<Editor>,
10280    ) -> Option<AnyElement> {
10281        let menu = self.context_menu.borrow();
10282        let menu = menu.as_ref()?;
10283        if !menu.visible() {
10284            return None;
10285        };
10286        self.style
10287            .as_ref()
10288            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10289    }
10290
10291    fn render_context_menu_aside(
10292        &mut self,
10293        max_size: Size<Pixels>,
10294        window: &mut Window,
10295        cx: &mut Context<Editor>,
10296    ) -> Option<AnyElement> {
10297        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10298            if menu.visible() {
10299                menu.render_aside(max_size, window, cx)
10300            } else {
10301                None
10302            }
10303        })
10304    }
10305
10306    fn hide_context_menu(
10307        &mut self,
10308        window: &mut Window,
10309        cx: &mut Context<Self>,
10310    ) -> Option<CodeContextMenu> {
10311        cx.notify();
10312        self.completion_tasks.clear();
10313        let context_menu = self.context_menu.borrow_mut().take();
10314        self.stale_edit_prediction_in_menu.take();
10315        self.update_visible_edit_prediction(window, cx);
10316        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10317            && let Some(completion_provider) = &self.completion_provider
10318        {
10319            completion_provider.selection_changed(None, window, cx);
10320        }
10321        context_menu
10322    }
10323
10324    fn show_snippet_choices(
10325        &mut self,
10326        choices: &Vec<String>,
10327        selection: Range<Anchor>,
10328        cx: &mut Context<Self>,
10329    ) {
10330        let Some((_, buffer, _)) = self
10331            .buffer()
10332            .read(cx)
10333            .excerpt_containing(selection.start, cx)
10334        else {
10335            return;
10336        };
10337        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10338        else {
10339            return;
10340        };
10341        if buffer != end_buffer {
10342            log::error!("expected anchor range to have matching buffer IDs");
10343            return;
10344        }
10345
10346        let id = post_inc(&mut self.next_completion_id);
10347        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10348        let mut context_menu = self.context_menu.borrow_mut();
10349        let old_menu = context_menu.take();
10350        *context_menu = Some(CodeContextMenu::Completions(
10351            CompletionsMenu::new_snippet_choices(
10352                id,
10353                true,
10354                choices,
10355                selection,
10356                buffer,
10357                old_menu.map(|menu| menu.primary_scroll_handle()),
10358                snippet_sort_order,
10359            ),
10360        ));
10361    }
10362
10363    pub fn insert_snippet(
10364        &mut self,
10365        insertion_ranges: &[Range<MultiBufferOffset>],
10366        snippet: Snippet,
10367        window: &mut Window,
10368        cx: &mut Context<Self>,
10369    ) -> Result<()> {
10370        struct Tabstop<T> {
10371            is_end_tabstop: bool,
10372            ranges: Vec<Range<T>>,
10373            choices: Option<Vec<String>>,
10374        }
10375
10376        let tabstops = self.buffer.update(cx, |buffer, cx| {
10377            let snippet_text: Arc<str> = snippet.text.clone().into();
10378            let edits = insertion_ranges
10379                .iter()
10380                .cloned()
10381                .map(|range| (range, snippet_text.clone()));
10382            let autoindent_mode = AutoindentMode::Block {
10383                original_indent_columns: Vec::new(),
10384            };
10385            buffer.edit(edits, Some(autoindent_mode), cx);
10386
10387            let snapshot = &*buffer.read(cx);
10388            let snippet = &snippet;
10389            snippet
10390                .tabstops
10391                .iter()
10392                .map(|tabstop| {
10393                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10394                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10395                    });
10396                    let mut tabstop_ranges = tabstop
10397                        .ranges
10398                        .iter()
10399                        .flat_map(|tabstop_range| {
10400                            let mut delta = 0_isize;
10401                            insertion_ranges.iter().map(move |insertion_range| {
10402                                let insertion_start = insertion_range.start + delta;
10403                                delta += snippet.text.len() as isize
10404                                    - (insertion_range.end - insertion_range.start) as isize;
10405
10406                                let start =
10407                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10408                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10409                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10410                            })
10411                        })
10412                        .collect::<Vec<_>>();
10413                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10414
10415                    Tabstop {
10416                        is_end_tabstop,
10417                        ranges: tabstop_ranges,
10418                        choices: tabstop.choices.clone(),
10419                    }
10420                })
10421                .collect::<Vec<_>>()
10422        });
10423        if let Some(tabstop) = tabstops.first() {
10424            self.change_selections(Default::default(), window, cx, |s| {
10425                // Reverse order so that the first range is the newest created selection.
10426                // Completions will use it and autoscroll will prioritize it.
10427                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10428            });
10429
10430            if let Some(choices) = &tabstop.choices
10431                && let Some(selection) = tabstop.ranges.first()
10432            {
10433                self.show_snippet_choices(choices, selection.clone(), cx)
10434            }
10435
10436            // If we're already at the last tabstop and it's at the end of the snippet,
10437            // we're done, we don't need to keep the state around.
10438            if !tabstop.is_end_tabstop {
10439                let choices = tabstops
10440                    .iter()
10441                    .map(|tabstop| tabstop.choices.clone())
10442                    .collect();
10443
10444                let ranges = tabstops
10445                    .into_iter()
10446                    .map(|tabstop| tabstop.ranges)
10447                    .collect::<Vec<_>>();
10448
10449                self.snippet_stack.push(SnippetState {
10450                    active_index: 0,
10451                    ranges,
10452                    choices,
10453                });
10454            }
10455
10456            // Check whether the just-entered snippet ends with an auto-closable bracket.
10457            if self.autoclose_regions.is_empty() {
10458                let snapshot = self.buffer.read(cx).snapshot(cx);
10459                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10460                    let selection_head = selection.head();
10461                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10462                        continue;
10463                    };
10464
10465                    let mut bracket_pair = None;
10466                    let max_lookup_length = scope
10467                        .brackets()
10468                        .map(|(pair, _)| {
10469                            pair.start
10470                                .as_str()
10471                                .chars()
10472                                .count()
10473                                .max(pair.end.as_str().chars().count())
10474                        })
10475                        .max();
10476                    if let Some(max_lookup_length) = max_lookup_length {
10477                        let next_text = snapshot
10478                            .chars_at(selection_head)
10479                            .take(max_lookup_length)
10480                            .collect::<String>();
10481                        let prev_text = snapshot
10482                            .reversed_chars_at(selection_head)
10483                            .take(max_lookup_length)
10484                            .collect::<String>();
10485
10486                        for (pair, enabled) in scope.brackets() {
10487                            if enabled
10488                                && pair.close
10489                                && prev_text.starts_with(pair.start.as_str())
10490                                && next_text.starts_with(pair.end.as_str())
10491                            {
10492                                bracket_pair = Some(pair.clone());
10493                                break;
10494                            }
10495                        }
10496                    }
10497
10498                    if let Some(pair) = bracket_pair {
10499                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10500                        let autoclose_enabled =
10501                            self.use_autoclose && snapshot_settings.use_autoclose;
10502                        if autoclose_enabled {
10503                            let start = snapshot.anchor_after(selection_head);
10504                            let end = snapshot.anchor_after(selection_head);
10505                            self.autoclose_regions.push(AutocloseRegion {
10506                                selection_id: selection.id,
10507                                range: start..end,
10508                                pair,
10509                            });
10510                        }
10511                    }
10512                }
10513            }
10514        }
10515        Ok(())
10516    }
10517
10518    pub fn move_to_next_snippet_tabstop(
10519        &mut self,
10520        window: &mut Window,
10521        cx: &mut Context<Self>,
10522    ) -> bool {
10523        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10524    }
10525
10526    pub fn move_to_prev_snippet_tabstop(
10527        &mut self,
10528        window: &mut Window,
10529        cx: &mut Context<Self>,
10530    ) -> bool {
10531        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10532    }
10533
10534    pub fn move_to_snippet_tabstop(
10535        &mut self,
10536        bias: Bias,
10537        window: &mut Window,
10538        cx: &mut Context<Self>,
10539    ) -> bool {
10540        if let Some(mut snippet) = self.snippet_stack.pop() {
10541            match bias {
10542                Bias::Left => {
10543                    if snippet.active_index > 0 {
10544                        snippet.active_index -= 1;
10545                    } else {
10546                        self.snippet_stack.push(snippet);
10547                        return false;
10548                    }
10549                }
10550                Bias::Right => {
10551                    if snippet.active_index + 1 < snippet.ranges.len() {
10552                        snippet.active_index += 1;
10553                    } else {
10554                        self.snippet_stack.push(snippet);
10555                        return false;
10556                    }
10557                }
10558            }
10559            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10560                self.change_selections(Default::default(), window, cx, |s| {
10561                    // Reverse order so that the first range is the newest created selection.
10562                    // Completions will use it and autoscroll will prioritize it.
10563                    s.select_ranges(current_ranges.iter().rev().cloned())
10564                });
10565
10566                if let Some(choices) = &snippet.choices[snippet.active_index]
10567                    && let Some(selection) = current_ranges.first()
10568                {
10569                    self.show_snippet_choices(choices, selection.clone(), cx);
10570                }
10571
10572                // If snippet state is not at the last tabstop, push it back on the stack
10573                if snippet.active_index + 1 < snippet.ranges.len() {
10574                    self.snippet_stack.push(snippet);
10575                }
10576                return true;
10577            }
10578        }
10579
10580        false
10581    }
10582
10583    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10584        self.transact(window, cx, |this, window, cx| {
10585            this.select_all(&SelectAll, window, cx);
10586            this.insert("", window, cx);
10587        });
10588    }
10589
10590    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10591        if self.read_only(cx) {
10592            return;
10593        }
10594        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10595        self.transact(window, cx, |this, window, cx| {
10596            this.select_autoclose_pair(window, cx);
10597
10598            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10599
10600            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10601            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10602            for selection in &mut selections {
10603                if selection.is_empty() {
10604                    let old_head = selection.head();
10605                    let mut new_head =
10606                        movement::left(&display_map, old_head.to_display_point(&display_map))
10607                            .to_point(&display_map);
10608                    if let Some((buffer, line_buffer_range)) = display_map
10609                        .buffer_snapshot()
10610                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10611                    {
10612                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10613                        let indent_len = match indent_size.kind {
10614                            IndentKind::Space => {
10615                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10616                            }
10617                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10618                        };
10619                        if old_head.column <= indent_size.len && old_head.column > 0 {
10620                            let indent_len = indent_len.get();
10621                            new_head = cmp::min(
10622                                new_head,
10623                                MultiBufferPoint::new(
10624                                    old_head.row,
10625                                    ((old_head.column - 1) / indent_len) * indent_len,
10626                                ),
10627                            );
10628                        }
10629                    }
10630
10631                    selection.set_head(new_head, SelectionGoal::None);
10632                }
10633            }
10634
10635            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10636            this.insert("", window, cx);
10637            linked_edits.apply_with_left_expansion(cx);
10638            this.refresh_edit_prediction(true, false, window, cx);
10639            refresh_linked_ranges(this, window, cx);
10640        });
10641    }
10642
10643    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10644        if self.read_only(cx) {
10645            return;
10646        }
10647        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10648        self.transact(window, cx, |this, window, cx| {
10649            this.change_selections(Default::default(), window, cx, |s| {
10650                s.move_with(&mut |map, selection| {
10651                    if selection.is_empty() {
10652                        let cursor = movement::right(map, selection.head());
10653                        selection.end = cursor;
10654                        selection.reversed = true;
10655                        selection.goal = SelectionGoal::None;
10656                    }
10657                })
10658            });
10659            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10660            this.insert("", window, cx);
10661            linked_edits.apply(cx);
10662            this.refresh_edit_prediction(true, false, window, cx);
10663            refresh_linked_ranges(this, window, cx);
10664        });
10665    }
10666
10667    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10668        if self.mode.is_single_line() {
10669            cx.propagate();
10670            return;
10671        }
10672
10673        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10674        if self.move_to_prev_snippet_tabstop(window, cx) {
10675            return;
10676        }
10677        self.outdent(&Outdent, window, cx);
10678    }
10679
10680    pub fn next_snippet_tabstop(
10681        &mut self,
10682        _: &NextSnippetTabstop,
10683        window: &mut Window,
10684        cx: &mut Context<Self>,
10685    ) {
10686        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10687            cx.propagate();
10688            return;
10689        }
10690
10691        if self.move_to_next_snippet_tabstop(window, cx) {
10692            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10693            return;
10694        }
10695        cx.propagate();
10696    }
10697
10698    pub fn previous_snippet_tabstop(
10699        &mut self,
10700        _: &PreviousSnippetTabstop,
10701        window: &mut Window,
10702        cx: &mut Context<Self>,
10703    ) {
10704        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10705            cx.propagate();
10706            return;
10707        }
10708
10709        if self.move_to_prev_snippet_tabstop(window, cx) {
10710            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10711            return;
10712        }
10713        cx.propagate();
10714    }
10715
10716    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10717        if self.mode.is_single_line() {
10718            cx.propagate();
10719            return;
10720        }
10721
10722        if self.move_to_next_snippet_tabstop(window, cx) {
10723            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10724            return;
10725        }
10726        if self.read_only(cx) {
10727            return;
10728        }
10729        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10730        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10731        let buffer = self.buffer.read(cx);
10732        let snapshot = buffer.snapshot(cx);
10733        let rows_iter = selections.iter().map(|s| s.head().row);
10734        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10735
10736        let has_some_cursor_in_whitespace = selections
10737            .iter()
10738            .filter(|selection| selection.is_empty())
10739            .any(|selection| {
10740                let cursor = selection.head();
10741                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10742                cursor.column < current_indent.len
10743            });
10744
10745        let mut edits = Vec::new();
10746        let mut prev_edited_row = 0;
10747        let mut row_delta = 0;
10748        for selection in &mut selections {
10749            if selection.start.row != prev_edited_row {
10750                row_delta = 0;
10751            }
10752            prev_edited_row = selection.end.row;
10753
10754            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10755            if selection.is_empty() {
10756                let cursor = selection.head();
10757                let settings = buffer.language_settings_at(cursor, cx);
10758                if settings.indent_list_on_tab {
10759                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10760                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10761                            row_delta = Self::indent_selection(
10762                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10763                            );
10764                            continue;
10765                        }
10766                    }
10767                }
10768            }
10769
10770            // If the selection is non-empty, then increase the indentation of the selected lines.
10771            if !selection.is_empty() {
10772                row_delta =
10773                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10774                continue;
10775            }
10776
10777            let cursor = selection.head();
10778            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10779            if let Some(suggested_indent) =
10780                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10781            {
10782                // Don't do anything if already at suggested indent
10783                // and there is any other cursor which is not
10784                if has_some_cursor_in_whitespace
10785                    && cursor.column == current_indent.len
10786                    && current_indent.len == suggested_indent.len
10787                {
10788                    continue;
10789                }
10790
10791                // Adjust line and move cursor to suggested indent
10792                // if cursor is not at suggested indent
10793                if cursor.column < suggested_indent.len
10794                    && cursor.column <= current_indent.len
10795                    && current_indent.len <= suggested_indent.len
10796                {
10797                    selection.start = Point::new(cursor.row, suggested_indent.len);
10798                    selection.end = selection.start;
10799                    if row_delta == 0 {
10800                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10801                            cursor.row,
10802                            current_indent,
10803                            suggested_indent,
10804                        ));
10805                        row_delta = suggested_indent.len - current_indent.len;
10806                    }
10807                    continue;
10808                }
10809
10810                // If current indent is more than suggested indent
10811                // only move cursor to current indent and skip indent
10812                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10813                    selection.start = Point::new(cursor.row, current_indent.len);
10814                    selection.end = selection.start;
10815                    continue;
10816                }
10817            }
10818
10819            // Otherwise, insert a hard or soft tab.
10820            let settings = buffer.language_settings_at(cursor, cx);
10821            let tab_size = if settings.hard_tabs {
10822                IndentSize::tab()
10823            } else {
10824                let tab_size = settings.tab_size.get();
10825                let indent_remainder = snapshot
10826                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10827                    .flat_map(str::chars)
10828                    .fold(row_delta % tab_size, |counter: u32, c| {
10829                        if c == '\t' {
10830                            0
10831                        } else {
10832                            (counter + 1) % tab_size
10833                        }
10834                    });
10835
10836                let chars_to_next_tab_stop = tab_size - indent_remainder;
10837                IndentSize::spaces(chars_to_next_tab_stop)
10838            };
10839            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10840            selection.end = selection.start;
10841            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10842            row_delta += tab_size.len;
10843        }
10844
10845        self.transact(window, cx, |this, window, cx| {
10846            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10847            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10848            this.refresh_edit_prediction(true, false, window, cx);
10849        });
10850    }
10851
10852    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10853        if self.read_only(cx) {
10854            return;
10855        }
10856        if self.mode.is_single_line() {
10857            cx.propagate();
10858            return;
10859        }
10860
10861        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10862        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
10863        let mut prev_edited_row = 0;
10864        let mut row_delta = 0;
10865        let mut edits = Vec::new();
10866        let buffer = self.buffer.read(cx);
10867        let snapshot = buffer.snapshot(cx);
10868        for selection in &mut selections {
10869            if selection.start.row != prev_edited_row {
10870                row_delta = 0;
10871            }
10872            prev_edited_row = selection.end.row;
10873
10874            row_delta =
10875                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10876        }
10877
10878        self.transact(window, cx, |this, window, cx| {
10879            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10880            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10881        });
10882    }
10883
10884    fn indent_selection(
10885        buffer: &MultiBuffer,
10886        snapshot: &MultiBufferSnapshot,
10887        selection: &mut Selection<Point>,
10888        edits: &mut Vec<(Range<Point>, String)>,
10889        delta_for_start_row: u32,
10890        cx: &App,
10891    ) -> u32 {
10892        let settings = buffer.language_settings_at(selection.start, cx);
10893        let tab_size = settings.tab_size.get();
10894        let indent_kind = if settings.hard_tabs {
10895            IndentKind::Tab
10896        } else {
10897            IndentKind::Space
10898        };
10899        let mut start_row = selection.start.row;
10900        let mut end_row = selection.end.row + 1;
10901
10902        // If a selection ends at the beginning of a line, don't indent
10903        // that last line.
10904        if selection.end.column == 0 && selection.end.row > selection.start.row {
10905            end_row -= 1;
10906        }
10907
10908        // Avoid re-indenting a row that has already been indented by a
10909        // previous selection, but still update this selection's column
10910        // to reflect that indentation.
10911        if delta_for_start_row > 0 {
10912            start_row += 1;
10913            selection.start.column += delta_for_start_row;
10914            if selection.end.row == selection.start.row {
10915                selection.end.column += delta_for_start_row;
10916            }
10917        }
10918
10919        let mut delta_for_end_row = 0;
10920        let has_multiple_rows = start_row + 1 != end_row;
10921        for row in start_row..end_row {
10922            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
10923            let indent_delta = match (current_indent.kind, indent_kind) {
10924                (IndentKind::Space, IndentKind::Space) => {
10925                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
10926                    IndentSize::spaces(columns_to_next_tab_stop)
10927                }
10928                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
10929                (_, IndentKind::Tab) => IndentSize::tab(),
10930            };
10931
10932            let start = if has_multiple_rows || current_indent.len < selection.start.column {
10933                0
10934            } else {
10935                selection.start.column
10936            };
10937            let row_start = Point::new(row, start);
10938            edits.push((
10939                row_start..row_start,
10940                indent_delta.chars().collect::<String>(),
10941            ));
10942
10943            // Update this selection's endpoints to reflect the indentation.
10944            if row == selection.start.row {
10945                selection.start.column += indent_delta.len;
10946            }
10947            if row == selection.end.row {
10948                selection.end.column += indent_delta.len;
10949                delta_for_end_row = indent_delta.len;
10950            }
10951        }
10952
10953        if selection.start.row == selection.end.row {
10954            delta_for_start_row + delta_for_end_row
10955        } else {
10956            delta_for_end_row
10957        }
10958    }
10959
10960    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10961        if self.read_only(cx) {
10962            return;
10963        }
10964        if self.mode.is_single_line() {
10965            cx.propagate();
10966            return;
10967        }
10968
10969        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10970        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10971        let selections = self.selections.all::<Point>(&display_map);
10972        let mut deletion_ranges = Vec::new();
10973        let mut last_outdent = None;
10974        {
10975            let buffer = self.buffer.read(cx);
10976            let snapshot = buffer.snapshot(cx);
10977            for selection in &selections {
10978                let settings = buffer.language_settings_at(selection.start, cx);
10979                let tab_size = settings.tab_size.get();
10980                let mut rows = selection.spanned_rows(false, &display_map);
10981
10982                // Avoid re-outdenting a row that has already been outdented by a
10983                // previous selection.
10984                if let Some(last_row) = last_outdent
10985                    && last_row == rows.start
10986                {
10987                    rows.start = rows.start.next_row();
10988                }
10989                let has_multiple_rows = rows.len() > 1;
10990                for row in rows.iter_rows() {
10991                    let indent_size = snapshot.indent_size_for_line(row);
10992                    if indent_size.len > 0 {
10993                        let deletion_len = match indent_size.kind {
10994                            IndentKind::Space => {
10995                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
10996                                if columns_to_prev_tab_stop == 0 {
10997                                    tab_size
10998                                } else {
10999                                    columns_to_prev_tab_stop
11000                                }
11001                            }
11002                            IndentKind::Tab => 1,
11003                        };
11004                        let start = if has_multiple_rows
11005                            || deletion_len > selection.start.column
11006                            || indent_size.len < selection.start.column
11007                        {
11008                            0
11009                        } else {
11010                            selection.start.column - deletion_len
11011                        };
11012                        deletion_ranges.push(
11013                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11014                        );
11015                        last_outdent = Some(row);
11016                    }
11017                }
11018            }
11019        }
11020
11021        self.transact(window, cx, |this, window, cx| {
11022            this.buffer.update(cx, |buffer, cx| {
11023                let empty_str: Arc<str> = Arc::default();
11024                buffer.edit(
11025                    deletion_ranges
11026                        .into_iter()
11027                        .map(|range| (range, empty_str.clone())),
11028                    None,
11029                    cx,
11030                );
11031            });
11032            let selections = this
11033                .selections
11034                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11035            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11036        });
11037    }
11038
11039    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11040        if self.read_only(cx) {
11041            return;
11042        }
11043        if self.mode.is_single_line() {
11044            cx.propagate();
11045            return;
11046        }
11047
11048        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11049        let selections = self
11050            .selections
11051            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11052            .into_iter()
11053            .map(|s| s.range());
11054
11055        self.transact(window, cx, |this, window, cx| {
11056            this.buffer.update(cx, |buffer, cx| {
11057                buffer.autoindent_ranges(selections, cx);
11058            });
11059            let selections = this
11060                .selections
11061                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11062            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11063        });
11064    }
11065
11066    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11067        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11068        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11069        let selections = self.selections.all::<Point>(&display_map);
11070
11071        let mut new_cursors = Vec::new();
11072        let mut edit_ranges = Vec::new();
11073        let mut selections = selections.iter().peekable();
11074        while let Some(selection) = selections.next() {
11075            let mut rows = selection.spanned_rows(false, &display_map);
11076
11077            // Accumulate contiguous regions of rows that we want to delete.
11078            while let Some(next_selection) = selections.peek() {
11079                let next_rows = next_selection.spanned_rows(false, &display_map);
11080                if next_rows.start <= rows.end {
11081                    rows.end = next_rows.end;
11082                    selections.next().unwrap();
11083                } else {
11084                    break;
11085                }
11086            }
11087
11088            let buffer = display_map.buffer_snapshot();
11089            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11090            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11091                // If there's a line after the range, delete the \n from the end of the row range
11092                (
11093                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11094                    rows.end,
11095                )
11096            } else {
11097                // If there isn't a line after the range, delete the \n from the line before the
11098                // start of the row range
11099                edit_start = edit_start.saturating_sub_usize(1);
11100                (buffer.len(), rows.start.previous_row())
11101            };
11102
11103            let text_layout_details = self.text_layout_details(window, cx);
11104            let x = display_map.x_for_display_point(
11105                selection.head().to_display_point(&display_map),
11106                &text_layout_details,
11107            );
11108            let row = Point::new(target_row.0, 0)
11109                .to_display_point(&display_map)
11110                .row();
11111            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11112
11113            new_cursors.push((
11114                selection.id,
11115                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11116                SelectionGoal::None,
11117            ));
11118            edit_ranges.push(edit_start..edit_end);
11119        }
11120
11121        self.transact(window, cx, |this, window, cx| {
11122            let buffer = this.buffer.update(cx, |buffer, cx| {
11123                let empty_str: Arc<str> = Arc::default();
11124                buffer.edit(
11125                    edit_ranges
11126                        .into_iter()
11127                        .map(|range| (range, empty_str.clone())),
11128                    None,
11129                    cx,
11130                );
11131                buffer.snapshot(cx)
11132            });
11133            let new_selections = new_cursors
11134                .into_iter()
11135                .map(|(id, cursor, goal)| {
11136                    let cursor = cursor.to_point(&buffer);
11137                    Selection {
11138                        id,
11139                        start: cursor,
11140                        end: cursor,
11141                        reversed: false,
11142                        goal,
11143                    }
11144                })
11145                .collect();
11146
11147            this.change_selections(Default::default(), window, cx, |s| {
11148                s.select(new_selections);
11149            });
11150        });
11151    }
11152
11153    pub fn join_lines_impl(
11154        &mut self,
11155        insert_whitespace: bool,
11156        window: &mut Window,
11157        cx: &mut Context<Self>,
11158    ) {
11159        if self.read_only(cx) {
11160            return;
11161        }
11162        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11163        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11164            let start = MultiBufferRow(selection.start.row);
11165            // Treat single line selections as if they include the next line. Otherwise this action
11166            // would do nothing for single line selections individual cursors.
11167            let end = if selection.start.row == selection.end.row {
11168                MultiBufferRow(selection.start.row + 1)
11169            } else if selection.end.column == 0 {
11170                // If the selection ends at the start of a line, it's logically at the end of the
11171                // previous line (plus its newline).
11172                // Don't include the end line unless there's only one line selected.
11173                if selection.start.row + 1 == selection.end.row {
11174                    MultiBufferRow(selection.end.row)
11175                } else {
11176                    MultiBufferRow(selection.end.row - 1)
11177                }
11178            } else {
11179                MultiBufferRow(selection.end.row)
11180            };
11181
11182            if let Some(last_row_range) = row_ranges.last_mut()
11183                && start <= last_row_range.end
11184            {
11185                last_row_range.end = end;
11186                continue;
11187            }
11188            row_ranges.push(start..end);
11189        }
11190
11191        let snapshot = self.buffer.read(cx).snapshot(cx);
11192        let mut cursor_positions = Vec::new();
11193        for row_range in &row_ranges {
11194            let anchor = snapshot.anchor_before(Point::new(
11195                row_range.end.previous_row().0,
11196                snapshot.line_len(row_range.end.previous_row()),
11197            ));
11198            cursor_positions.push(anchor..anchor);
11199        }
11200
11201        self.transact(window, cx, |this, window, cx| {
11202            for row_range in row_ranges.into_iter().rev() {
11203                for row in row_range.iter_rows().rev() {
11204                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11205                    let next_line_row = row.next_row();
11206                    let indent = snapshot.indent_size_for_line(next_line_row);
11207                    let mut join_start_column = indent.len;
11208
11209                    if let Some(language_scope) =
11210                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11211                    {
11212                        let line_end =
11213                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11214                        let line_text_after_indent = snapshot
11215                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11216                            .collect::<String>();
11217
11218                        if !line_text_after_indent.is_empty() {
11219                            let block_prefix = language_scope
11220                                .block_comment()
11221                                .map(|c| c.prefix.as_ref())
11222                                .filter(|p| !p.is_empty());
11223                            let doc_prefix = language_scope
11224                                .documentation_comment()
11225                                .map(|c| c.prefix.as_ref())
11226                                .filter(|p| !p.is_empty());
11227                            let all_prefixes = language_scope
11228                                .line_comment_prefixes()
11229                                .iter()
11230                                .map(|p| p.as_ref())
11231                                .chain(block_prefix)
11232                                .chain(doc_prefix)
11233                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11234
11235                            let mut longest_prefix_len = None;
11236                            for prefix in all_prefixes {
11237                                let trimmed = prefix.trim_end();
11238                                if line_text_after_indent.starts_with(trimmed) {
11239                                    let candidate_len =
11240                                        if line_text_after_indent.starts_with(prefix) {
11241                                            prefix.len()
11242                                        } else {
11243                                            trimmed.len()
11244                                        };
11245                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11246                                        longest_prefix_len = Some(candidate_len);
11247                                    }
11248                                }
11249                            }
11250
11251                            if let Some(prefix_len) = longest_prefix_len {
11252                                join_start_column =
11253                                    join_start_column.saturating_add(prefix_len as u32);
11254                            }
11255                        }
11256                    }
11257
11258                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11259
11260                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11261                        && insert_whitespace
11262                    {
11263                        " "
11264                    } else {
11265                        ""
11266                    };
11267
11268                    this.buffer.update(cx, |buffer, cx| {
11269                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11270                    });
11271                }
11272            }
11273
11274            this.change_selections(Default::default(), window, cx, |s| {
11275                s.select_anchor_ranges(cursor_positions)
11276            });
11277        });
11278    }
11279
11280    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11281        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11282        self.join_lines_impl(true, window, cx);
11283    }
11284
11285    pub fn sort_lines_case_sensitive(
11286        &mut self,
11287        _: &SortLinesCaseSensitive,
11288        window: &mut Window,
11289        cx: &mut Context<Self>,
11290    ) {
11291        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11292    }
11293
11294    pub fn sort_lines_by_length(
11295        &mut self,
11296        _: &SortLinesByLength,
11297        window: &mut Window,
11298        cx: &mut Context<Self>,
11299    ) {
11300        self.manipulate_immutable_lines(window, cx, |lines| {
11301            lines.sort_by_key(|&line| line.chars().count())
11302        })
11303    }
11304
11305    pub fn sort_lines_case_insensitive(
11306        &mut self,
11307        _: &SortLinesCaseInsensitive,
11308        window: &mut Window,
11309        cx: &mut Context<Self>,
11310    ) {
11311        self.manipulate_immutable_lines(window, cx, |lines| {
11312            lines.sort_by_key(|line| line.to_lowercase())
11313        })
11314    }
11315
11316    pub fn unique_lines_case_insensitive(
11317        &mut self,
11318        _: &UniqueLinesCaseInsensitive,
11319        window: &mut Window,
11320        cx: &mut Context<Self>,
11321    ) {
11322        self.manipulate_immutable_lines(window, cx, |lines| {
11323            let mut seen = HashSet::default();
11324            lines.retain(|line| seen.insert(line.to_lowercase()));
11325        })
11326    }
11327
11328    pub fn unique_lines_case_sensitive(
11329        &mut self,
11330        _: &UniqueLinesCaseSensitive,
11331        window: &mut Window,
11332        cx: &mut Context<Self>,
11333    ) {
11334        self.manipulate_immutable_lines(window, cx, |lines| {
11335            let mut seen = HashSet::default();
11336            lines.retain(|line| seen.insert(*line));
11337        })
11338    }
11339
11340    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11341        let snapshot = self.buffer.read(cx).snapshot(cx);
11342        for selection in self.selections.disjoint_anchors_arc().iter() {
11343            if snapshot
11344                .language_at(selection.start)
11345                .and_then(|lang| lang.config().wrap_characters.as_ref())
11346                .is_some()
11347            {
11348                return true;
11349            }
11350        }
11351        false
11352    }
11353
11354    fn wrap_selections_in_tag(
11355        &mut self,
11356        _: &WrapSelectionsInTag,
11357        window: &mut Window,
11358        cx: &mut Context<Self>,
11359    ) {
11360        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11361
11362        let snapshot = self.buffer.read(cx).snapshot(cx);
11363
11364        let mut edits = Vec::new();
11365        let mut boundaries = Vec::new();
11366
11367        for selection in self
11368            .selections
11369            .all_adjusted(&self.display_snapshot(cx))
11370            .iter()
11371        {
11372            let Some(wrap_config) = snapshot
11373                .language_at(selection.start)
11374                .and_then(|lang| lang.config().wrap_characters.clone())
11375            else {
11376                continue;
11377            };
11378
11379            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11380            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11381
11382            let start_before = snapshot.anchor_before(selection.start);
11383            let end_after = snapshot.anchor_after(selection.end);
11384
11385            edits.push((start_before..start_before, open_tag));
11386            edits.push((end_after..end_after, close_tag));
11387
11388            boundaries.push((
11389                start_before,
11390                end_after,
11391                wrap_config.start_prefix.len(),
11392                wrap_config.end_suffix.len(),
11393            ));
11394        }
11395
11396        if edits.is_empty() {
11397            return;
11398        }
11399
11400        self.transact(window, cx, |this, window, cx| {
11401            let buffer = this.buffer.update(cx, |buffer, cx| {
11402                buffer.edit(edits, None, cx);
11403                buffer.snapshot(cx)
11404            });
11405
11406            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11407            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11408                boundaries.into_iter()
11409            {
11410                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11411                let close_offset = end_after
11412                    .to_offset(&buffer)
11413                    .saturating_sub_usize(end_suffix_len);
11414                new_selections.push(open_offset..open_offset);
11415                new_selections.push(close_offset..close_offset);
11416            }
11417
11418            this.change_selections(Default::default(), window, cx, |s| {
11419                s.select_ranges(new_selections);
11420            });
11421
11422            this.request_autoscroll(Autoscroll::fit(), cx);
11423        });
11424    }
11425
11426    pub fn toggle_read_only(
11427        &mut self,
11428        _: &workspace::ToggleReadOnlyFile,
11429        _: &mut Window,
11430        cx: &mut Context<Self>,
11431    ) {
11432        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11433            buffer.update(cx, |buffer, cx| {
11434                buffer.set_capability(
11435                    match buffer.capability() {
11436                        Capability::ReadWrite => Capability::Read,
11437                        Capability::Read => Capability::ReadWrite,
11438                        Capability::ReadOnly => Capability::ReadOnly,
11439                    },
11440                    cx,
11441                );
11442            })
11443        }
11444    }
11445
11446    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11447        let Some(project) = self.project.clone() else {
11448            return;
11449        };
11450        let task = self.reload(project, window, cx);
11451        self.detach_and_notify_err(task, window, cx);
11452    }
11453
11454    pub fn restore_file(
11455        &mut self,
11456        _: &::git::RestoreFile,
11457        window: &mut Window,
11458        cx: &mut Context<Self>,
11459    ) {
11460        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11461        let mut buffer_ids = HashSet::default();
11462        let snapshot = self.buffer().read(cx).snapshot(cx);
11463        for selection in self
11464            .selections
11465            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11466        {
11467            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11468        }
11469
11470        let buffer = self.buffer().read(cx);
11471        let ranges = buffer_ids
11472            .into_iter()
11473            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11474            .collect::<Vec<_>>();
11475
11476        self.restore_hunks_in_ranges(ranges, window, cx);
11477    }
11478
11479    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11480        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11481        let selections = self
11482            .selections
11483            .all(&self.display_snapshot(cx))
11484            .into_iter()
11485            .map(|s| s.range())
11486            .collect();
11487        self.restore_hunks_in_ranges(selections, window, cx);
11488    }
11489
11490    /// Restores the diff hunks in the editor's selections and moves the cursor
11491    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11492    /// not all diff hunks are expanded.
11493    pub fn restore_and_next(
11494        &mut self,
11495        _: &::git::RestoreAndNext,
11496        window: &mut Window,
11497        cx: &mut Context<Self>,
11498    ) {
11499        let selections = self
11500            .selections
11501            .all(&self.display_snapshot(cx))
11502            .into_iter()
11503            .map(|selection| selection.range())
11504            .collect();
11505
11506        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11507        self.restore_hunks_in_ranges(selections, window, cx);
11508
11509        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11510        let wrap_around = !all_diff_hunks_expanded;
11511        let snapshot = self.snapshot(window, cx);
11512        let position = self
11513            .selections
11514            .newest::<Point>(&snapshot.display_snapshot)
11515            .head();
11516
11517        self.go_to_hunk_before_or_after_position(
11518            &snapshot,
11519            position,
11520            Direction::Next,
11521            wrap_around,
11522            window,
11523            cx,
11524        );
11525    }
11526
11527    pub fn restore_hunks_in_ranges(
11528        &mut self,
11529        ranges: Vec<Range<Point>>,
11530        window: &mut Window,
11531        cx: &mut Context<Editor>,
11532    ) {
11533        if self.delegate_stage_and_restore {
11534            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11535            if !hunks.is_empty() {
11536                cx.emit(EditorEvent::RestoreRequested { hunks });
11537            }
11538            return;
11539        }
11540        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11541        self.transact(window, cx, |editor, window, cx| {
11542            editor.restore_diff_hunks(hunks, cx);
11543            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11544                selections.refresh()
11545            });
11546        });
11547    }
11548
11549    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11550        let mut revert_changes = HashMap::default();
11551        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11552        for (buffer_id, hunks) in &chunk_by {
11553            let hunks = hunks.collect::<Vec<_>>();
11554            for hunk in &hunks {
11555                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11556            }
11557            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11558        }
11559        if !revert_changes.is_empty() {
11560            self.buffer().update(cx, |multi_buffer, cx| {
11561                for (buffer_id, changes) in revert_changes {
11562                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11563                        buffer.update(cx, |buffer, cx| {
11564                            buffer.edit(
11565                                changes
11566                                    .into_iter()
11567                                    .map(|(range, text)| (range, text.to_string())),
11568                                None,
11569                                cx,
11570                            );
11571                        });
11572                    }
11573                }
11574            });
11575        }
11576    }
11577
11578    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11579        if let Some(status) = self
11580            .addons
11581            .iter()
11582            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11583        {
11584            return Some(status);
11585        }
11586        self.project
11587            .as_ref()?
11588            .read(cx)
11589            .status_for_buffer_id(buffer_id, cx)
11590    }
11591
11592    pub fn open_active_item_in_terminal(
11593        &mut self,
11594        _: &OpenInTerminal,
11595        window: &mut Window,
11596        cx: &mut Context<Self>,
11597    ) {
11598        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11599            let project_path = buffer.read(cx).project_path(cx)?;
11600            let project = self.project()?.read(cx);
11601            let entry = project.entry_for_path(&project_path, cx)?;
11602            let parent = match &entry.canonical_path {
11603                Some(canonical_path) => canonical_path.to_path_buf(),
11604                None => project.absolute_path(&project_path, cx)?,
11605            }
11606            .parent()?
11607            .to_path_buf();
11608            Some(parent)
11609        }) {
11610            window.dispatch_action(
11611                OpenTerminal {
11612                    working_directory,
11613                    local: false,
11614                }
11615                .boxed_clone(),
11616                cx,
11617            );
11618        }
11619    }
11620
11621    fn set_breakpoint_context_menu(
11622        &mut self,
11623        display_row: DisplayRow,
11624        position: Option<Anchor>,
11625        clicked_point: gpui::Point<Pixels>,
11626        window: &mut Window,
11627        cx: &mut Context<Self>,
11628    ) {
11629        let source = self
11630            .buffer
11631            .read(cx)
11632            .snapshot(cx)
11633            .anchor_before(Point::new(display_row.0, 0u32));
11634
11635        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11636
11637        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11638            self,
11639            source,
11640            clicked_point,
11641            context_menu,
11642            window,
11643            cx,
11644        );
11645    }
11646
11647    fn add_edit_breakpoint_block(
11648        &mut self,
11649        anchor: Anchor,
11650        breakpoint: &Breakpoint,
11651        edit_action: BreakpointPromptEditAction,
11652        window: &mut Window,
11653        cx: &mut Context<Self>,
11654    ) {
11655        let weak_editor = cx.weak_entity();
11656        let bp_prompt = cx.new(|cx| {
11657            BreakpointPromptEditor::new(
11658                weak_editor,
11659                anchor,
11660                breakpoint.clone(),
11661                edit_action,
11662                window,
11663                cx,
11664            )
11665        });
11666
11667        let height = bp_prompt.update(cx, |this, cx| {
11668            this.prompt
11669                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11670        });
11671        let cloned_prompt = bp_prompt.clone();
11672        let blocks = vec![BlockProperties {
11673            style: BlockStyle::Sticky,
11674            placement: BlockPlacement::Above(anchor),
11675            height: Some(height),
11676            render: Arc::new(move |cx| {
11677                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11678                cloned_prompt.clone().into_any_element()
11679            }),
11680            priority: 0,
11681        }];
11682
11683        let focus_handle = bp_prompt.focus_handle(cx);
11684        window.focus(&focus_handle, cx);
11685
11686        let block_ids = self.insert_blocks(blocks, None, cx);
11687        bp_prompt.update(cx, |prompt, _| {
11688            prompt.add_block_ids(block_ids);
11689        });
11690    }
11691
11692    pub(crate) fn breakpoint_at_row(
11693        &self,
11694        row: u32,
11695        window: &mut Window,
11696        cx: &mut Context<Self>,
11697    ) -> Option<(Anchor, Breakpoint)> {
11698        let snapshot = self.snapshot(window, cx);
11699        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11700
11701        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11702    }
11703
11704    pub(crate) fn breakpoint_at_anchor(
11705        &self,
11706        breakpoint_position: Anchor,
11707        snapshot: &EditorSnapshot,
11708        cx: &mut Context<Self>,
11709    ) -> Option<(Anchor, Breakpoint)> {
11710        let buffer = self
11711            .buffer
11712            .read(cx)
11713            .buffer_for_anchor(breakpoint_position, cx)?;
11714
11715        let enclosing_excerpt = breakpoint_position.excerpt_id;
11716        let buffer_snapshot = buffer.read(cx).snapshot();
11717
11718        let row = buffer_snapshot
11719            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11720            .row;
11721
11722        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11723        let anchor_end = snapshot
11724            .buffer_snapshot()
11725            .anchor_after(Point::new(row, line_len));
11726
11727        self.breakpoint_store
11728            .as_ref()?
11729            .read_with(cx, |breakpoint_store, cx| {
11730                breakpoint_store
11731                    .breakpoints(
11732                        &buffer,
11733                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11734                        &buffer_snapshot,
11735                        cx,
11736                    )
11737                    .next()
11738                    .and_then(|(bp, _)| {
11739                        let breakpoint_row = buffer_snapshot
11740                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11741                            .row;
11742
11743                        if breakpoint_row == row {
11744                            snapshot
11745                                .buffer_snapshot()
11746                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11747                                .map(|position| (position, bp.bp.clone()))
11748                        } else {
11749                            None
11750                        }
11751                    })
11752            })
11753    }
11754
11755    pub fn edit_log_breakpoint(
11756        &mut self,
11757        _: &EditLogBreakpoint,
11758        window: &mut Window,
11759        cx: &mut Context<Self>,
11760    ) {
11761        if self.breakpoint_store.is_none() {
11762            return;
11763        }
11764
11765        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11766            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11767                message: None,
11768                state: BreakpointState::Enabled,
11769                condition: None,
11770                hit_condition: None,
11771            });
11772
11773            self.add_edit_breakpoint_block(
11774                anchor,
11775                &breakpoint,
11776                BreakpointPromptEditAction::Log,
11777                window,
11778                cx,
11779            );
11780        }
11781    }
11782
11783    fn breakpoints_at_cursors(
11784        &self,
11785        window: &mut Window,
11786        cx: &mut Context<Self>,
11787    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11788        let snapshot = self.snapshot(window, cx);
11789        let cursors = self
11790            .selections
11791            .disjoint_anchors_arc()
11792            .iter()
11793            .map(|selection| {
11794                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11795
11796                let breakpoint_position = self
11797                    .breakpoint_at_row(cursor_position.row, window, cx)
11798                    .map(|bp| bp.0)
11799                    .unwrap_or_else(|| {
11800                        snapshot
11801                            .display_snapshot
11802                            .buffer_snapshot()
11803                            .anchor_after(Point::new(cursor_position.row, 0))
11804                    });
11805
11806                let breakpoint = self
11807                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11808                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11809
11810                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11811            })
11812            // 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.
11813            .collect::<HashMap<Anchor, _>>();
11814
11815        cursors.into_iter().collect()
11816    }
11817
11818    pub fn enable_breakpoint(
11819        &mut self,
11820        _: &crate::actions::EnableBreakpoint,
11821        window: &mut Window,
11822        cx: &mut Context<Self>,
11823    ) {
11824        if self.breakpoint_store.is_none() {
11825            return;
11826        }
11827
11828        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11829            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11830                continue;
11831            };
11832            self.edit_breakpoint_at_anchor(
11833                anchor,
11834                breakpoint,
11835                BreakpointEditAction::InvertState,
11836                cx,
11837            );
11838        }
11839    }
11840
11841    pub fn disable_breakpoint(
11842        &mut self,
11843        _: &crate::actions::DisableBreakpoint,
11844        window: &mut Window,
11845        cx: &mut Context<Self>,
11846    ) {
11847        if self.breakpoint_store.is_none() {
11848            return;
11849        }
11850
11851        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11852            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11853                continue;
11854            };
11855            self.edit_breakpoint_at_anchor(
11856                anchor,
11857                breakpoint,
11858                BreakpointEditAction::InvertState,
11859                cx,
11860            );
11861        }
11862    }
11863
11864    pub fn toggle_breakpoint(
11865        &mut self,
11866        _: &crate::actions::ToggleBreakpoint,
11867        window: &mut Window,
11868        cx: &mut Context<Self>,
11869    ) {
11870        if self.breakpoint_store.is_none() {
11871            return;
11872        }
11873
11874        let snapshot = self.snapshot(window, cx);
11875        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11876            if self.gutter_breakpoint_indicator.0.is_some() {
11877                let display_row = anchor
11878                    .to_point(snapshot.buffer_snapshot())
11879                    .to_display_point(&snapshot.display_snapshot)
11880                    .row();
11881                self.update_breakpoint_collision_on_toggle(
11882                    display_row,
11883                    &BreakpointEditAction::Toggle,
11884                );
11885            }
11886
11887            if let Some(breakpoint) = breakpoint {
11888                self.edit_breakpoint_at_anchor(
11889                    anchor,
11890                    breakpoint,
11891                    BreakpointEditAction::Toggle,
11892                    cx,
11893                );
11894            } else {
11895                self.edit_breakpoint_at_anchor(
11896                    anchor,
11897                    Breakpoint::new_standard(),
11898                    BreakpointEditAction::Toggle,
11899                    cx,
11900                );
11901            }
11902        }
11903    }
11904
11905    fn update_breakpoint_collision_on_toggle(
11906        &mut self,
11907        display_row: DisplayRow,
11908        edit_action: &BreakpointEditAction,
11909    ) {
11910        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
11911            if breakpoint_indicator.display_row == display_row
11912                && matches!(edit_action, BreakpointEditAction::Toggle)
11913            {
11914                breakpoint_indicator.collides_with_existing_breakpoint =
11915                    !breakpoint_indicator.collides_with_existing_breakpoint;
11916            }
11917        }
11918    }
11919
11920    pub fn edit_breakpoint_at_anchor(
11921        &mut self,
11922        breakpoint_position: Anchor,
11923        breakpoint: Breakpoint,
11924        edit_action: BreakpointEditAction,
11925        cx: &mut Context<Self>,
11926    ) {
11927        let Some(breakpoint_store) = &self.breakpoint_store else {
11928            return;
11929        };
11930
11931        let Some(buffer) = self
11932            .buffer
11933            .read(cx)
11934            .buffer_for_anchor(breakpoint_position, cx)
11935        else {
11936            return;
11937        };
11938
11939        breakpoint_store.update(cx, |breakpoint_store, cx| {
11940            breakpoint_store.toggle_breakpoint(
11941                buffer,
11942                BreakpointWithPosition {
11943                    position: breakpoint_position.text_anchor,
11944                    bp: breakpoint,
11945                },
11946                edit_action,
11947                cx,
11948            );
11949        });
11950
11951        cx.notify();
11952    }
11953
11954    #[cfg(any(test, feature = "test-support"))]
11955    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
11956        self.breakpoint_store.clone()
11957    }
11958
11959    pub fn prepare_restore_change(
11960        &self,
11961        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
11962        hunk: &MultiBufferDiffHunk,
11963        cx: &mut App,
11964    ) -> Option<()> {
11965        if hunk.is_created_file() {
11966            return None;
11967        }
11968        let buffer = self.buffer.read(cx);
11969        let diff = buffer.diff_for(hunk.buffer_id)?;
11970        let buffer = buffer.buffer(hunk.buffer_id)?;
11971        let buffer = buffer.read(cx);
11972        let original_text = diff
11973            .read(cx)
11974            .base_text(cx)
11975            .as_rope()
11976            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
11977        let buffer_snapshot = buffer.snapshot();
11978        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
11979        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
11980            probe
11981                .0
11982                .start
11983                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
11984                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
11985        }) {
11986            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
11987            Some(())
11988        } else {
11989            None
11990        }
11991    }
11992
11993    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
11994        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
11995    }
11996
11997    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
11998        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
11999    }
12000
12001    pub fn rotate_selections_forward(
12002        &mut self,
12003        _: &RotateSelectionsForward,
12004        window: &mut Window,
12005        cx: &mut Context<Self>,
12006    ) {
12007        self.rotate_selections(window, cx, false)
12008    }
12009
12010    pub fn rotate_selections_backward(
12011        &mut self,
12012        _: &RotateSelectionsBackward,
12013        window: &mut Window,
12014        cx: &mut Context<Self>,
12015    ) {
12016        self.rotate_selections(window, cx, true)
12017    }
12018
12019    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12020        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12021        let display_snapshot = self.display_snapshot(cx);
12022        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12023
12024        if selections.len() < 2 {
12025            return;
12026        }
12027
12028        let (edits, new_selections) = {
12029            let buffer = self.buffer.read(cx).read(cx);
12030            let has_selections = selections.iter().any(|s| !s.is_empty());
12031            if has_selections {
12032                let mut selected_texts: Vec<String> = selections
12033                    .iter()
12034                    .map(|selection| {
12035                        buffer
12036                            .text_for_range(selection.start..selection.end)
12037                            .collect()
12038                    })
12039                    .collect();
12040
12041                if reverse {
12042                    selected_texts.rotate_left(1);
12043                } else {
12044                    selected_texts.rotate_right(1);
12045                }
12046
12047                let mut offset_delta: i64 = 0;
12048                let mut new_selections = Vec::new();
12049                let edits: Vec<_> = selections
12050                    .iter()
12051                    .zip(selected_texts.iter())
12052                    .map(|(selection, new_text)| {
12053                        let old_len = (selection.end.0 - selection.start.0) as i64;
12054                        let new_len = new_text.len() as i64;
12055                        let adjusted_start =
12056                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12057                        let adjusted_end =
12058                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12059
12060                        new_selections.push(Selection {
12061                            id: selection.id,
12062                            start: adjusted_start,
12063                            end: adjusted_end,
12064                            reversed: selection.reversed,
12065                            goal: selection.goal,
12066                        });
12067
12068                        offset_delta += new_len - old_len;
12069                        (selection.start..selection.end, new_text.clone())
12070                    })
12071                    .collect();
12072                (edits, new_selections)
12073            } else {
12074                let mut all_rows: Vec<u32> = selections
12075                    .iter()
12076                    .map(|selection| buffer.offset_to_point(selection.start).row)
12077                    .collect();
12078                all_rows.sort_unstable();
12079                all_rows.dedup();
12080
12081                if all_rows.len() < 2 {
12082                    return;
12083                }
12084
12085                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12086                    .iter()
12087                    .map(|&row| {
12088                        let start = Point::new(row, 0);
12089                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12090                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12091                    })
12092                    .collect();
12093
12094                let mut line_texts: Vec<String> = line_ranges
12095                    .iter()
12096                    .map(|range| buffer.text_for_range(range.clone()).collect())
12097                    .collect();
12098
12099                if reverse {
12100                    line_texts.rotate_left(1);
12101                } else {
12102                    line_texts.rotate_right(1);
12103                }
12104
12105                let edits = line_ranges
12106                    .iter()
12107                    .zip(line_texts.iter())
12108                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12109                    .collect();
12110
12111                let num_rows = all_rows.len();
12112                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12113                    .iter()
12114                    .enumerate()
12115                    .map(|(i, &row)| (row, i))
12116                    .collect();
12117
12118                // Compute new line start offsets after rotation (handles CRLF)
12119                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12120                let first_line_start = line_ranges[0].start.0;
12121                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12122                for text in line_texts.iter().take(num_rows - 1) {
12123                    let prev_start = *new_line_starts.last().unwrap();
12124                    new_line_starts.push(prev_start + text.len() + newline_len);
12125                }
12126
12127                let new_selections = selections
12128                    .iter()
12129                    .map(|selection| {
12130                        let point = buffer.offset_to_point(selection.start);
12131                        let old_index = row_to_index[&point.row];
12132                        let new_index = if reverse {
12133                            (old_index + num_rows - 1) % num_rows
12134                        } else {
12135                            (old_index + 1) % num_rows
12136                        };
12137                        let new_offset =
12138                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12139                        Selection {
12140                            id: selection.id,
12141                            start: new_offset,
12142                            end: new_offset,
12143                            reversed: selection.reversed,
12144                            goal: selection.goal,
12145                        }
12146                    })
12147                    .collect();
12148
12149                (edits, new_selections)
12150            }
12151        };
12152
12153        self.transact(window, cx, |this, window, cx| {
12154            this.buffer.update(cx, |buffer, cx| {
12155                buffer.edit(edits, None, cx);
12156            });
12157            this.change_selections(Default::default(), window, cx, |s| {
12158                s.select(new_selections);
12159            });
12160        });
12161    }
12162
12163    fn manipulate_lines<M>(
12164        &mut self,
12165        window: &mut Window,
12166        cx: &mut Context<Self>,
12167        mut manipulate: M,
12168    ) where
12169        M: FnMut(&str) -> LineManipulationResult,
12170    {
12171        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12172
12173        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12174        let buffer = self.buffer.read(cx).snapshot(cx);
12175
12176        let mut edits = Vec::new();
12177
12178        let selections = self.selections.all::<Point>(&display_map);
12179        let mut selections = selections.iter().peekable();
12180        let mut contiguous_row_selections = Vec::new();
12181        let mut new_selections = Vec::new();
12182        let mut added_lines = 0;
12183        let mut removed_lines = 0;
12184
12185        while let Some(selection) = selections.next() {
12186            let (start_row, end_row) = consume_contiguous_rows(
12187                &mut contiguous_row_selections,
12188                selection,
12189                &display_map,
12190                &mut selections,
12191            );
12192
12193            let start_point = Point::new(start_row.0, 0);
12194            let end_point = Point::new(
12195                end_row.previous_row().0,
12196                buffer.line_len(end_row.previous_row()),
12197            );
12198            let text = buffer
12199                .text_for_range(start_point..end_point)
12200                .collect::<String>();
12201
12202            let LineManipulationResult {
12203                new_text,
12204                line_count_before,
12205                line_count_after,
12206            } = manipulate(&text);
12207
12208            edits.push((start_point..end_point, new_text));
12209
12210            // Selections must change based on added and removed line count
12211            let start_row =
12212                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12213            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12214            new_selections.push(Selection {
12215                id: selection.id,
12216                start: start_row,
12217                end: end_row,
12218                goal: SelectionGoal::None,
12219                reversed: selection.reversed,
12220            });
12221
12222            if line_count_after > line_count_before {
12223                added_lines += line_count_after - line_count_before;
12224            } else if line_count_before > line_count_after {
12225                removed_lines += line_count_before - line_count_after;
12226            }
12227        }
12228
12229        self.transact(window, cx, |this, window, cx| {
12230            let buffer = this.buffer.update(cx, |buffer, cx| {
12231                buffer.edit(edits, None, cx);
12232                buffer.snapshot(cx)
12233            });
12234
12235            // Recalculate offsets on newly edited buffer
12236            let new_selections = new_selections
12237                .iter()
12238                .map(|s| {
12239                    let start_point = Point::new(s.start.0, 0);
12240                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12241                    Selection {
12242                        id: s.id,
12243                        start: buffer.point_to_offset(start_point),
12244                        end: buffer.point_to_offset(end_point),
12245                        goal: s.goal,
12246                        reversed: s.reversed,
12247                    }
12248                })
12249                .collect();
12250
12251            this.change_selections(Default::default(), window, cx, |s| {
12252                s.select(new_selections);
12253            });
12254
12255            this.request_autoscroll(Autoscroll::fit(), cx);
12256        });
12257    }
12258
12259    fn manipulate_immutable_lines<Fn>(
12260        &mut self,
12261        window: &mut Window,
12262        cx: &mut Context<Self>,
12263        mut callback: Fn,
12264    ) where
12265        Fn: FnMut(&mut Vec<&str>),
12266    {
12267        self.manipulate_lines(window, cx, |text| {
12268            let mut lines: Vec<&str> = text.split('\n').collect();
12269            let line_count_before = lines.len();
12270
12271            callback(&mut lines);
12272
12273            LineManipulationResult {
12274                new_text: lines.join("\n"),
12275                line_count_before,
12276                line_count_after: lines.len(),
12277            }
12278        });
12279    }
12280
12281    fn manipulate_mutable_lines<Fn>(
12282        &mut self,
12283        window: &mut Window,
12284        cx: &mut Context<Self>,
12285        mut callback: Fn,
12286    ) where
12287        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12288    {
12289        self.manipulate_lines(window, cx, |text| {
12290            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12291            let line_count_before = lines.len();
12292
12293            callback(&mut lines);
12294
12295            LineManipulationResult {
12296                new_text: lines.join("\n"),
12297                line_count_before,
12298                line_count_after: lines.len(),
12299            }
12300        });
12301    }
12302
12303    pub fn convert_indentation_to_spaces(
12304        &mut self,
12305        _: &ConvertIndentationToSpaces,
12306        window: &mut Window,
12307        cx: &mut Context<Self>,
12308    ) {
12309        let settings = self.buffer.read(cx).language_settings(cx);
12310        let tab_size = settings.tab_size.get() as usize;
12311
12312        self.manipulate_mutable_lines(window, cx, |lines| {
12313            // Allocates a reasonably sized scratch buffer once for the whole loop
12314            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12315            // Avoids recomputing spaces that could be inserted many times
12316            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12317                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12318                .collect();
12319
12320            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12321                let mut chars = line.as_ref().chars();
12322                let mut col = 0;
12323                let mut changed = false;
12324
12325                for ch in chars.by_ref() {
12326                    match ch {
12327                        ' ' => {
12328                            reindented_line.push(' ');
12329                            col += 1;
12330                        }
12331                        '\t' => {
12332                            // \t are converted to spaces depending on the current column
12333                            let spaces_len = tab_size - (col % tab_size);
12334                            reindented_line.extend(&space_cache[spaces_len - 1]);
12335                            col += spaces_len;
12336                            changed = true;
12337                        }
12338                        _ => {
12339                            // If we dont append before break, the character is consumed
12340                            reindented_line.push(ch);
12341                            break;
12342                        }
12343                    }
12344                }
12345
12346                if !changed {
12347                    reindented_line.clear();
12348                    continue;
12349                }
12350                // Append the rest of the line and replace old reference with new one
12351                reindented_line.extend(chars);
12352                *line = Cow::Owned(reindented_line.clone());
12353                reindented_line.clear();
12354            }
12355        });
12356    }
12357
12358    pub fn convert_indentation_to_tabs(
12359        &mut self,
12360        _: &ConvertIndentationToTabs,
12361        window: &mut Window,
12362        cx: &mut Context<Self>,
12363    ) {
12364        let settings = self.buffer.read(cx).language_settings(cx);
12365        let tab_size = settings.tab_size.get() as usize;
12366
12367        self.manipulate_mutable_lines(window, cx, |lines| {
12368            // Allocates a reasonably sized buffer once for the whole loop
12369            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12370            // Avoids recomputing spaces that could be inserted many times
12371            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12372                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12373                .collect();
12374
12375            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12376                let mut chars = line.chars();
12377                let mut spaces_count = 0;
12378                let mut first_non_indent_char = None;
12379                let mut changed = false;
12380
12381                for ch in chars.by_ref() {
12382                    match ch {
12383                        ' ' => {
12384                            // Keep track of spaces. Append \t when we reach tab_size
12385                            spaces_count += 1;
12386                            changed = true;
12387                            if spaces_count == tab_size {
12388                                reindented_line.push('\t');
12389                                spaces_count = 0;
12390                            }
12391                        }
12392                        '\t' => {
12393                            reindented_line.push('\t');
12394                            spaces_count = 0;
12395                        }
12396                        _ => {
12397                            // Dont append it yet, we might have remaining spaces
12398                            first_non_indent_char = Some(ch);
12399                            break;
12400                        }
12401                    }
12402                }
12403
12404                if !changed {
12405                    reindented_line.clear();
12406                    continue;
12407                }
12408                // Remaining spaces that didn't make a full tab stop
12409                if spaces_count > 0 {
12410                    reindented_line.extend(&space_cache[spaces_count - 1]);
12411                }
12412                // If we consume an extra character that was not indentation, add it back
12413                if let Some(extra_char) = first_non_indent_char {
12414                    reindented_line.push(extra_char);
12415                }
12416                // Append the rest of the line and replace old reference with new one
12417                reindented_line.extend(chars);
12418                *line = Cow::Owned(reindented_line.clone());
12419                reindented_line.clear();
12420            }
12421        });
12422    }
12423
12424    pub fn convert_to_upper_case(
12425        &mut self,
12426        _: &ConvertToUpperCase,
12427        window: &mut Window,
12428        cx: &mut Context<Self>,
12429    ) {
12430        self.manipulate_text(window, cx, |text| text.to_uppercase())
12431    }
12432
12433    pub fn convert_to_lower_case(
12434        &mut self,
12435        _: &ConvertToLowerCase,
12436        window: &mut Window,
12437        cx: &mut Context<Self>,
12438    ) {
12439        self.manipulate_text(window, cx, |text| text.to_lowercase())
12440    }
12441
12442    pub fn convert_to_title_case(
12443        &mut self,
12444        _: &ConvertToTitleCase,
12445        window: &mut Window,
12446        cx: &mut Context<Self>,
12447    ) {
12448        self.manipulate_text(window, cx, |text| {
12449            text.split('\n')
12450                .map(|line| line.to_case(Case::Title))
12451                .join("\n")
12452        })
12453    }
12454
12455    pub fn convert_to_snake_case(
12456        &mut self,
12457        _: &ConvertToSnakeCase,
12458        window: &mut Window,
12459        cx: &mut Context<Self>,
12460    ) {
12461        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12462    }
12463
12464    pub fn convert_to_kebab_case(
12465        &mut self,
12466        _: &ConvertToKebabCase,
12467        window: &mut Window,
12468        cx: &mut Context<Self>,
12469    ) {
12470        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12471    }
12472
12473    pub fn convert_to_upper_camel_case(
12474        &mut self,
12475        _: &ConvertToUpperCamelCase,
12476        window: &mut Window,
12477        cx: &mut Context<Self>,
12478    ) {
12479        self.manipulate_text(window, cx, |text| {
12480            text.split('\n')
12481                .map(|line| line.to_case(Case::UpperCamel))
12482                .join("\n")
12483        })
12484    }
12485
12486    pub fn convert_to_lower_camel_case(
12487        &mut self,
12488        _: &ConvertToLowerCamelCase,
12489        window: &mut Window,
12490        cx: &mut Context<Self>,
12491    ) {
12492        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12493    }
12494
12495    pub fn convert_to_opposite_case(
12496        &mut self,
12497        _: &ConvertToOppositeCase,
12498        window: &mut Window,
12499        cx: &mut Context<Self>,
12500    ) {
12501        self.manipulate_text(window, cx, |text| {
12502            text.chars()
12503                .fold(String::with_capacity(text.len()), |mut t, c| {
12504                    if c.is_uppercase() {
12505                        t.extend(c.to_lowercase());
12506                    } else {
12507                        t.extend(c.to_uppercase());
12508                    }
12509                    t
12510                })
12511        })
12512    }
12513
12514    pub fn convert_to_sentence_case(
12515        &mut self,
12516        _: &ConvertToSentenceCase,
12517        window: &mut Window,
12518        cx: &mut Context<Self>,
12519    ) {
12520        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12521    }
12522
12523    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12524        self.manipulate_text(window, cx, |text| {
12525            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12526            if has_upper_case_characters {
12527                text.to_lowercase()
12528            } else {
12529                text.to_uppercase()
12530            }
12531        })
12532    }
12533
12534    pub fn convert_to_rot13(
12535        &mut self,
12536        _: &ConvertToRot13,
12537        window: &mut Window,
12538        cx: &mut Context<Self>,
12539    ) {
12540        self.manipulate_text(window, cx, |text| {
12541            text.chars()
12542                .map(|c| match c {
12543                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12544                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12545                    _ => c,
12546                })
12547                .collect()
12548        })
12549    }
12550
12551    pub fn convert_to_rot47(
12552        &mut self,
12553        _: &ConvertToRot47,
12554        window: &mut Window,
12555        cx: &mut Context<Self>,
12556    ) {
12557        self.manipulate_text(window, cx, |text| {
12558            text.chars()
12559                .map(|c| {
12560                    let code_point = c as u32;
12561                    if code_point >= 33 && code_point <= 126 {
12562                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12563                    }
12564                    c
12565                })
12566                .collect()
12567        })
12568    }
12569
12570    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12571    where
12572        Fn: FnMut(&str) -> String,
12573    {
12574        let buffer = self.buffer.read(cx).snapshot(cx);
12575
12576        let mut new_selections = Vec::new();
12577        let mut edits = Vec::new();
12578        let mut selection_adjustment = 0isize;
12579
12580        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12581            let selection_is_empty = selection.is_empty();
12582
12583            let (start, end) = if selection_is_empty {
12584                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12585                (word_range.start, word_range.end)
12586            } else {
12587                (
12588                    buffer.point_to_offset(selection.start),
12589                    buffer.point_to_offset(selection.end),
12590                )
12591            };
12592
12593            let text = buffer.text_for_range(start..end).collect::<String>();
12594            let old_length = text.len() as isize;
12595            let text = callback(&text);
12596
12597            new_selections.push(Selection {
12598                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12599                end: MultiBufferOffset(
12600                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12601                ),
12602                goal: SelectionGoal::None,
12603                id: selection.id,
12604                reversed: selection.reversed,
12605            });
12606
12607            selection_adjustment += old_length - text.len() as isize;
12608
12609            edits.push((start..end, text));
12610        }
12611
12612        self.transact(window, cx, |this, window, cx| {
12613            this.buffer.update(cx, |buffer, cx| {
12614                buffer.edit(edits, None, cx);
12615            });
12616
12617            this.change_selections(Default::default(), window, cx, |s| {
12618                s.select(new_selections);
12619            });
12620
12621            this.request_autoscroll(Autoscroll::fit(), cx);
12622        });
12623    }
12624
12625    pub fn move_selection_on_drop(
12626        &mut self,
12627        selection: &Selection<Anchor>,
12628        target: DisplayPoint,
12629        is_cut: bool,
12630        window: &mut Window,
12631        cx: &mut Context<Self>,
12632    ) {
12633        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12634        let buffer = display_map.buffer_snapshot();
12635        let mut edits = Vec::new();
12636        let insert_point = display_map
12637            .clip_point(target, Bias::Left)
12638            .to_point(&display_map);
12639        let text = buffer
12640            .text_for_range(selection.start..selection.end)
12641            .collect::<String>();
12642        if is_cut {
12643            edits.push(((selection.start..selection.end), String::new()));
12644        }
12645        let insert_anchor = buffer.anchor_before(insert_point);
12646        edits.push(((insert_anchor..insert_anchor), text));
12647        let last_edit_start = insert_anchor.bias_left(buffer);
12648        let last_edit_end = insert_anchor.bias_right(buffer);
12649        self.transact(window, cx, |this, window, cx| {
12650            this.buffer.update(cx, |buffer, cx| {
12651                buffer.edit(edits, None, cx);
12652            });
12653            this.change_selections(Default::default(), window, cx, |s| {
12654                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12655            });
12656        });
12657    }
12658
12659    pub fn clear_selection_drag_state(&mut self) {
12660        self.selection_drag_state = SelectionDragState::None;
12661    }
12662
12663    pub fn duplicate(
12664        &mut self,
12665        upwards: bool,
12666        whole_lines: bool,
12667        window: &mut Window,
12668        cx: &mut Context<Self>,
12669    ) {
12670        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12671
12672        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12673        let buffer = display_map.buffer_snapshot();
12674        let selections = self.selections.all::<Point>(&display_map);
12675
12676        let mut edits = Vec::new();
12677        let mut selections_iter = selections.iter().peekable();
12678        while let Some(selection) = selections_iter.next() {
12679            let mut rows = selection.spanned_rows(false, &display_map);
12680            // duplicate line-wise
12681            if whole_lines || selection.start == selection.end {
12682                // Avoid duplicating the same lines twice.
12683                while let Some(next_selection) = selections_iter.peek() {
12684                    let next_rows = next_selection.spanned_rows(false, &display_map);
12685                    if next_rows.start < rows.end {
12686                        rows.end = next_rows.end;
12687                        selections_iter.next().unwrap();
12688                    } else {
12689                        break;
12690                    }
12691                }
12692
12693                // Copy the text from the selected row region and splice it either at the start
12694                // or end of the region.
12695                let start = Point::new(rows.start.0, 0);
12696                let end = Point::new(
12697                    rows.end.previous_row().0,
12698                    buffer.line_len(rows.end.previous_row()),
12699                );
12700
12701                let mut text = buffer.text_for_range(start..end).collect::<String>();
12702
12703                let insert_location = if upwards {
12704                    // When duplicating upward, we need to insert before the current line.
12705                    // If we're on the last line and it doesn't end with a newline,
12706                    // we need to add a newline before the duplicated content.
12707                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12708                        && buffer.max_point().column > 0
12709                        && !text.ends_with('\n');
12710
12711                    if needs_leading_newline {
12712                        text.insert(0, '\n');
12713                        end
12714                    } else {
12715                        text.push('\n');
12716                        Point::new(rows.start.0, 0)
12717                    }
12718                } else {
12719                    text.push('\n');
12720                    start
12721                };
12722                edits.push((insert_location..insert_location, text));
12723            } else {
12724                // duplicate character-wise
12725                let start = selection.start;
12726                let end = selection.end;
12727                let text = buffer.text_for_range(start..end).collect::<String>();
12728                edits.push((selection.end..selection.end, text));
12729            }
12730        }
12731
12732        self.transact(window, cx, |this, window, cx| {
12733            this.buffer.update(cx, |buffer, cx| {
12734                buffer.edit(edits, None, cx);
12735            });
12736
12737            // When duplicating upward with whole lines, move the cursor to the duplicated line
12738            if upwards && whole_lines {
12739                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12740
12741                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12742                    let mut new_ranges = Vec::new();
12743                    let selections = s.all::<Point>(&display_map);
12744                    let mut selections_iter = selections.iter().peekable();
12745
12746                    while let Some(first_selection) = selections_iter.next() {
12747                        // Group contiguous selections together to find the total row span
12748                        let mut group_selections = vec![first_selection];
12749                        let mut rows = first_selection.spanned_rows(false, &display_map);
12750
12751                        while let Some(next_selection) = selections_iter.peek() {
12752                            let next_rows = next_selection.spanned_rows(false, &display_map);
12753                            if next_rows.start < rows.end {
12754                                rows.end = next_rows.end;
12755                                group_selections.push(selections_iter.next().unwrap());
12756                            } else {
12757                                break;
12758                            }
12759                        }
12760
12761                        let row_count = rows.end.0 - rows.start.0;
12762
12763                        // Move all selections in this group up by the total number of duplicated rows
12764                        for selection in group_selections {
12765                            let new_start = Point::new(
12766                                selection.start.row.saturating_sub(row_count),
12767                                selection.start.column,
12768                            );
12769
12770                            let new_end = Point::new(
12771                                selection.end.row.saturating_sub(row_count),
12772                                selection.end.column,
12773                            );
12774
12775                            new_ranges.push(new_start..new_end);
12776                        }
12777                    }
12778
12779                    s.select_ranges(new_ranges);
12780                });
12781            }
12782
12783            this.request_autoscroll(Autoscroll::fit(), cx);
12784        });
12785    }
12786
12787    pub fn duplicate_line_up(
12788        &mut self,
12789        _: &DuplicateLineUp,
12790        window: &mut Window,
12791        cx: &mut Context<Self>,
12792    ) {
12793        self.duplicate(true, true, window, cx);
12794    }
12795
12796    pub fn duplicate_line_down(
12797        &mut self,
12798        _: &DuplicateLineDown,
12799        window: &mut Window,
12800        cx: &mut Context<Self>,
12801    ) {
12802        self.duplicate(false, true, window, cx);
12803    }
12804
12805    pub fn duplicate_selection(
12806        &mut self,
12807        _: &DuplicateSelection,
12808        window: &mut Window,
12809        cx: &mut Context<Self>,
12810    ) {
12811        self.duplicate(false, false, window, cx);
12812    }
12813
12814    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12815        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12816        if self.mode.is_single_line() {
12817            cx.propagate();
12818            return;
12819        }
12820
12821        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12822        let buffer = self.buffer.read(cx).snapshot(cx);
12823
12824        let mut edits = Vec::new();
12825        let mut unfold_ranges = Vec::new();
12826        let mut refold_creases = Vec::new();
12827
12828        let selections = self.selections.all::<Point>(&display_map);
12829        let mut selections = selections.iter().peekable();
12830        let mut contiguous_row_selections = Vec::new();
12831        let mut new_selections = Vec::new();
12832
12833        while let Some(selection) = selections.next() {
12834            // Find all the selections that span a contiguous row range
12835            let (start_row, end_row) = consume_contiguous_rows(
12836                &mut contiguous_row_selections,
12837                selection,
12838                &display_map,
12839                &mut selections,
12840            );
12841
12842            // Move the text spanned by the row range to be before the line preceding the row range
12843            if start_row.0 > 0 {
12844                let range_to_move = Point::new(
12845                    start_row.previous_row().0,
12846                    buffer.line_len(start_row.previous_row()),
12847                )
12848                    ..Point::new(
12849                        end_row.previous_row().0,
12850                        buffer.line_len(end_row.previous_row()),
12851                    );
12852                let insertion_point = display_map
12853                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12854                    .0;
12855
12856                // Don't move lines across excerpts
12857                if buffer
12858                    .excerpt_containing(insertion_point..range_to_move.end)
12859                    .is_some()
12860                {
12861                    let text = buffer
12862                        .text_for_range(range_to_move.clone())
12863                        .flat_map(|s| s.chars())
12864                        .skip(1)
12865                        .chain(['\n'])
12866                        .collect::<String>();
12867
12868                    edits.push((
12869                        buffer.anchor_after(range_to_move.start)
12870                            ..buffer.anchor_before(range_to_move.end),
12871                        String::new(),
12872                    ));
12873                    let insertion_anchor = buffer.anchor_after(insertion_point);
12874                    edits.push((insertion_anchor..insertion_anchor, text));
12875
12876                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12877
12878                    // Move selections up
12879                    new_selections.extend(contiguous_row_selections.drain(..).map(
12880                        |mut selection| {
12881                            selection.start.row -= row_delta;
12882                            selection.end.row -= row_delta;
12883                            selection
12884                        },
12885                    ));
12886
12887                    // Move folds up
12888                    unfold_ranges.push(range_to_move.clone());
12889                    for fold in display_map.folds_in_range(
12890                        buffer.anchor_before(range_to_move.start)
12891                            ..buffer.anchor_after(range_to_move.end),
12892                    ) {
12893                        let mut start = fold.range.start.to_point(&buffer);
12894                        let mut end = fold.range.end.to_point(&buffer);
12895                        start.row -= row_delta;
12896                        end.row -= row_delta;
12897                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12898                    }
12899                }
12900            }
12901
12902            // If we didn't move line(s), preserve the existing selections
12903            new_selections.append(&mut contiguous_row_selections);
12904        }
12905
12906        self.transact(window, cx, |this, window, cx| {
12907            this.unfold_ranges(&unfold_ranges, true, true, cx);
12908            this.buffer.update(cx, |buffer, cx| {
12909                for (range, text) in edits {
12910                    buffer.edit([(range, text)], None, cx);
12911                }
12912            });
12913            this.fold_creases(refold_creases, true, window, cx);
12914            this.change_selections(Default::default(), window, cx, |s| {
12915                s.select(new_selections);
12916            })
12917        });
12918    }
12919
12920    pub fn move_line_down(
12921        &mut self,
12922        _: &MoveLineDown,
12923        window: &mut Window,
12924        cx: &mut Context<Self>,
12925    ) {
12926        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12927        if self.mode.is_single_line() {
12928            cx.propagate();
12929            return;
12930        }
12931
12932        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12933        let buffer = self.buffer.read(cx).snapshot(cx);
12934
12935        let mut edits = Vec::new();
12936        let mut unfold_ranges = Vec::new();
12937        let mut refold_creases = Vec::new();
12938
12939        let selections = self.selections.all::<Point>(&display_map);
12940        let mut selections = selections.iter().peekable();
12941        let mut contiguous_row_selections = Vec::new();
12942        let mut new_selections = Vec::new();
12943
12944        while let Some(selection) = selections.next() {
12945            // Find all the selections that span a contiguous row range
12946            let (start_row, end_row) = consume_contiguous_rows(
12947                &mut contiguous_row_selections,
12948                selection,
12949                &display_map,
12950                &mut selections,
12951            );
12952
12953            // Move the text spanned by the row range to be after the last line of the row range
12954            if end_row.0 <= buffer.max_point().row {
12955                let range_to_move =
12956                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
12957                let insertion_point = display_map
12958                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
12959                    .0;
12960
12961                // Don't move lines across excerpt boundaries
12962                if buffer
12963                    .excerpt_containing(range_to_move.start..insertion_point)
12964                    .is_some()
12965                {
12966                    let mut text = String::from("\n");
12967                    text.extend(buffer.text_for_range(range_to_move.clone()));
12968                    text.pop(); // Drop trailing newline
12969                    edits.push((
12970                        buffer.anchor_after(range_to_move.start)
12971                            ..buffer.anchor_before(range_to_move.end),
12972                        String::new(),
12973                    ));
12974                    let insertion_anchor = buffer.anchor_after(insertion_point);
12975                    edits.push((insertion_anchor..insertion_anchor, text));
12976
12977                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
12978
12979                    // Move selections down
12980                    new_selections.extend(contiguous_row_selections.drain(..).map(
12981                        |mut selection| {
12982                            selection.start.row += row_delta;
12983                            selection.end.row += row_delta;
12984                            selection
12985                        },
12986                    ));
12987
12988                    // Move folds down
12989                    unfold_ranges.push(range_to_move.clone());
12990                    for fold in display_map.folds_in_range(
12991                        buffer.anchor_before(range_to_move.start)
12992                            ..buffer.anchor_after(range_to_move.end),
12993                    ) {
12994                        let mut start = fold.range.start.to_point(&buffer);
12995                        let mut end = fold.range.end.to_point(&buffer);
12996                        start.row += row_delta;
12997                        end.row += row_delta;
12998                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12999                    }
13000                }
13001            }
13002
13003            // If we didn't move line(s), preserve the existing selections
13004            new_selections.append(&mut contiguous_row_selections);
13005        }
13006
13007        self.transact(window, cx, |this, window, cx| {
13008            this.unfold_ranges(&unfold_ranges, true, true, cx);
13009            this.buffer.update(cx, |buffer, cx| {
13010                for (range, text) in edits {
13011                    buffer.edit([(range, text)], None, cx);
13012                }
13013            });
13014            this.fold_creases(refold_creases, true, window, cx);
13015            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13016        });
13017    }
13018
13019    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13020        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13021        let text_layout_details = &self.text_layout_details(window, cx);
13022        self.transact(window, cx, |this, window, cx| {
13023            let edits = this.change_selections(Default::default(), window, cx, |s| {
13024                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13025                s.move_with(&mut |display_map, selection| {
13026                    if !selection.is_empty() {
13027                        return;
13028                    }
13029
13030                    let mut head = selection.head();
13031                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13032                    if head.column() == display_map.line_len(head.row()) {
13033                        transpose_offset = display_map
13034                            .buffer_snapshot()
13035                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13036                    }
13037
13038                    if transpose_offset == MultiBufferOffset(0) {
13039                        return;
13040                    }
13041
13042                    *head.column_mut() += 1;
13043                    head = display_map.clip_point(head, Bias::Right);
13044                    let goal = SelectionGoal::HorizontalPosition(
13045                        display_map
13046                            .x_for_display_point(head, text_layout_details)
13047                            .into(),
13048                    );
13049                    selection.collapse_to(head, goal);
13050
13051                    let transpose_start = display_map
13052                        .buffer_snapshot()
13053                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13054                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13055                        let transpose_end = display_map
13056                            .buffer_snapshot()
13057                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13058                        if let Some(ch) = display_map
13059                            .buffer_snapshot()
13060                            .chars_at(transpose_start)
13061                            .next()
13062                        {
13063                            edits.push((transpose_start..transpose_offset, String::new()));
13064                            edits.push((transpose_end..transpose_end, ch.to_string()));
13065                        }
13066                    }
13067                });
13068                edits
13069            });
13070            this.buffer
13071                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13072            let selections = this
13073                .selections
13074                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13075            this.change_selections(Default::default(), window, cx, |s| {
13076                s.select(selections);
13077            });
13078        });
13079    }
13080
13081    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13082        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13083        if self.mode.is_single_line() {
13084            cx.propagate();
13085            return;
13086        }
13087
13088        self.rewrap_impl(RewrapOptions::default(), cx)
13089    }
13090
13091    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13092        let buffer = self.buffer.read(cx).snapshot(cx);
13093        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13094
13095        #[derive(Clone, Debug, PartialEq)]
13096        enum CommentFormat {
13097            /// single line comment, with prefix for line
13098            Line(String),
13099            /// single line within a block comment, with prefix for line
13100            BlockLine(String),
13101            /// a single line of a block comment that includes the initial delimiter
13102            BlockCommentWithStart(BlockCommentConfig),
13103            /// a single line of a block comment that includes the ending delimiter
13104            BlockCommentWithEnd(BlockCommentConfig),
13105        }
13106
13107        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13108        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13109            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13110                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13111                .peekable();
13112
13113            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13114                row
13115            } else {
13116                return Vec::new();
13117            };
13118
13119            let language_settings = buffer.language_settings_at(selection.head(), cx);
13120            let language_scope = buffer.language_scope_at(selection.head());
13121
13122            let indent_and_prefix_for_row =
13123                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13124                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13125                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13126                        &language_scope
13127                    {
13128                        let indent_end = Point::new(row, indent.len);
13129                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13130                        let line_text_after_indent = buffer
13131                            .text_for_range(indent_end..line_end)
13132                            .collect::<String>();
13133
13134                        let is_within_comment_override = buffer
13135                            .language_scope_at(indent_end)
13136                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13137                        let comment_delimiters = if is_within_comment_override {
13138                            // we are within a comment syntax node, but we don't
13139                            // yet know what kind of comment: block, doc or line
13140                            match (
13141                                language_scope.documentation_comment(),
13142                                language_scope.block_comment(),
13143                            ) {
13144                                (Some(config), _) | (_, Some(config))
13145                                    if buffer.contains_str_at(indent_end, &config.start) =>
13146                                {
13147                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13148                                }
13149                                (Some(config), _) | (_, Some(config))
13150                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13151                                {
13152                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13153                                }
13154                                (Some(config), _) | (_, Some(config))
13155                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13156                                {
13157                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13158                                }
13159                                (_, _) => language_scope
13160                                    .line_comment_prefixes()
13161                                    .iter()
13162                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13163                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13164                            }
13165                        } else {
13166                            // we not in an overridden comment node, but we may
13167                            // be within a non-overridden line comment node
13168                            language_scope
13169                                .line_comment_prefixes()
13170                                .iter()
13171                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13172                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13173                        };
13174
13175                        let rewrap_prefix = language_scope
13176                            .rewrap_prefixes()
13177                            .iter()
13178                            .find_map(|prefix_regex| {
13179                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13180                                    if mat.start() == 0 {
13181                                        Some(mat.as_str().to_string())
13182                                    } else {
13183                                        None
13184                                    }
13185                                })
13186                            })
13187                            .flatten();
13188                        (comment_delimiters, rewrap_prefix)
13189                    } else {
13190                        (None, None)
13191                    };
13192                    (indent, comment_prefix, rewrap_prefix)
13193                };
13194
13195            let mut ranges = Vec::new();
13196            let from_empty_selection = selection.is_empty();
13197
13198            let mut current_range_start = first_row;
13199            let mut prev_row = first_row;
13200            let (
13201                mut current_range_indent,
13202                mut current_range_comment_delimiters,
13203                mut current_range_rewrap_prefix,
13204            ) = indent_and_prefix_for_row(first_row);
13205
13206            for row in non_blank_rows_iter.skip(1) {
13207                let has_paragraph_break = row > prev_row + 1;
13208
13209                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13210                    indent_and_prefix_for_row(row);
13211
13212                let has_indent_change = row_indent != current_range_indent;
13213                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13214
13215                let has_boundary_change = has_comment_change
13216                    || row_rewrap_prefix.is_some()
13217                    || (has_indent_change && current_range_comment_delimiters.is_some());
13218
13219                if has_paragraph_break || has_boundary_change {
13220                    ranges.push((
13221                        language_settings.clone(),
13222                        Point::new(current_range_start, 0)
13223                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13224                        current_range_indent,
13225                        current_range_comment_delimiters.clone(),
13226                        current_range_rewrap_prefix.clone(),
13227                        from_empty_selection,
13228                    ));
13229                    current_range_start = row;
13230                    current_range_indent = row_indent;
13231                    current_range_comment_delimiters = row_comment_delimiters;
13232                    current_range_rewrap_prefix = row_rewrap_prefix;
13233                }
13234                prev_row = row;
13235            }
13236
13237            ranges.push((
13238                language_settings.clone(),
13239                Point::new(current_range_start, 0)
13240                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13241                current_range_indent,
13242                current_range_comment_delimiters,
13243                current_range_rewrap_prefix,
13244                from_empty_selection,
13245            ));
13246
13247            ranges
13248        });
13249
13250        let mut edits = Vec::new();
13251        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13252
13253        for (
13254            language_settings,
13255            wrap_range,
13256            mut indent_size,
13257            comment_prefix,
13258            rewrap_prefix,
13259            from_empty_selection,
13260        ) in wrap_ranges
13261        {
13262            let mut start_row = wrap_range.start.row;
13263            let mut end_row = wrap_range.end.row;
13264
13265            // Skip selections that overlap with a range that has already been rewrapped.
13266            let selection_range = start_row..end_row;
13267            if rewrapped_row_ranges
13268                .iter()
13269                .any(|range| range.overlaps(&selection_range))
13270            {
13271                continue;
13272            }
13273
13274            let tab_size = language_settings.tab_size;
13275
13276            let (line_prefix, inside_comment) = match &comment_prefix {
13277                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13278                    (Some(prefix.as_str()), true)
13279                }
13280                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13281                    (Some(prefix.as_ref()), true)
13282                }
13283                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13284                    start: _,
13285                    end: _,
13286                    prefix,
13287                    tab_size,
13288                })) => {
13289                    indent_size.len += tab_size;
13290                    (Some(prefix.as_ref()), true)
13291                }
13292                None => (None, false),
13293            };
13294            let indent_prefix = indent_size.chars().collect::<String>();
13295            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13296
13297            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13298                RewrapBehavior::InComments => inside_comment,
13299                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13300                RewrapBehavior::Anywhere => true,
13301            };
13302
13303            let should_rewrap = options.override_language_settings
13304                || allow_rewrap_based_on_language
13305                || self.hard_wrap.is_some();
13306            if !should_rewrap {
13307                continue;
13308            }
13309
13310            if from_empty_selection {
13311                'expand_upwards: while start_row > 0 {
13312                    let prev_row = start_row - 1;
13313                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13314                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13315                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13316                    {
13317                        start_row = prev_row;
13318                    } else {
13319                        break 'expand_upwards;
13320                    }
13321                }
13322
13323                'expand_downwards: while end_row < buffer.max_point().row {
13324                    let next_row = end_row + 1;
13325                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13326                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13327                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13328                    {
13329                        end_row = next_row;
13330                    } else {
13331                        break 'expand_downwards;
13332                    }
13333                }
13334            }
13335
13336            let start = Point::new(start_row, 0);
13337            let start_offset = ToOffset::to_offset(&start, &buffer);
13338            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13339            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13340            let mut first_line_delimiter = None;
13341            let mut last_line_delimiter = None;
13342            let Some(lines_without_prefixes) = selection_text
13343                .lines()
13344                .enumerate()
13345                .map(|(ix, line)| {
13346                    let line_trimmed = line.trim_start();
13347                    if rewrap_prefix.is_some() && ix > 0 {
13348                        Ok(line_trimmed)
13349                    } else if let Some(
13350                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13351                            start,
13352                            prefix,
13353                            end,
13354                            tab_size,
13355                        })
13356                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13357                            start,
13358                            prefix,
13359                            end,
13360                            tab_size,
13361                        }),
13362                    ) = &comment_prefix
13363                    {
13364                        let line_trimmed = line_trimmed
13365                            .strip_prefix(start.as_ref())
13366                            .map(|s| {
13367                                let mut indent_size = indent_size;
13368                                indent_size.len -= tab_size;
13369                                let indent_prefix: String = indent_size.chars().collect();
13370                                first_line_delimiter = Some((indent_prefix, start));
13371                                s.trim_start()
13372                            })
13373                            .unwrap_or(line_trimmed);
13374                        let line_trimmed = line_trimmed
13375                            .strip_suffix(end.as_ref())
13376                            .map(|s| {
13377                                last_line_delimiter = Some(end);
13378                                s.trim_end()
13379                            })
13380                            .unwrap_or(line_trimmed);
13381                        let line_trimmed = line_trimmed
13382                            .strip_prefix(prefix.as_ref())
13383                            .unwrap_or(line_trimmed);
13384                        Ok(line_trimmed)
13385                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13386                        line_trimmed.strip_prefix(prefix).with_context(|| {
13387                            format!("line did not start with prefix {prefix:?}: {line:?}")
13388                        })
13389                    } else {
13390                        line_trimmed
13391                            .strip_prefix(&line_prefix.trim_start())
13392                            .with_context(|| {
13393                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13394                            })
13395                    }
13396                })
13397                .collect::<Result<Vec<_>, _>>()
13398                .log_err()
13399            else {
13400                continue;
13401            };
13402
13403            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13404                buffer
13405                    .language_settings_at(Point::new(start_row, 0), cx)
13406                    .preferred_line_length as usize
13407            });
13408
13409            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13410                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13411            } else {
13412                line_prefix.clone()
13413            };
13414
13415            let wrapped_text = {
13416                let mut wrapped_text = wrap_with_prefix(
13417                    line_prefix,
13418                    subsequent_lines_prefix,
13419                    lines_without_prefixes.join("\n"),
13420                    wrap_column,
13421                    tab_size,
13422                    options.preserve_existing_whitespace,
13423                );
13424
13425                if let Some((indent, delimiter)) = first_line_delimiter {
13426                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13427                }
13428                if let Some(last_line) = last_line_delimiter {
13429                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13430                }
13431
13432                wrapped_text
13433            };
13434
13435            // TODO: should always use char-based diff while still supporting cursor behavior that
13436            // matches vim.
13437            let mut diff_options = DiffOptions::default();
13438            if options.override_language_settings {
13439                diff_options.max_word_diff_len = 0;
13440                diff_options.max_word_diff_line_count = 0;
13441            } else {
13442                diff_options.max_word_diff_len = usize::MAX;
13443                diff_options.max_word_diff_line_count = usize::MAX;
13444            }
13445
13446            for (old_range, new_text) in
13447                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13448            {
13449                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13450                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13451                edits.push((edit_start..edit_end, new_text));
13452            }
13453
13454            rewrapped_row_ranges.push(start_row..=end_row);
13455        }
13456
13457        self.buffer
13458            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13459    }
13460
13461    pub fn cut_common(
13462        &mut self,
13463        cut_no_selection_line: bool,
13464        window: &mut Window,
13465        cx: &mut Context<Self>,
13466    ) -> ClipboardItem {
13467        let mut text = String::new();
13468        let buffer = self.buffer.read(cx).snapshot(cx);
13469        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13470        let mut clipboard_selections = Vec::with_capacity(selections.len());
13471        {
13472            let max_point = buffer.max_point();
13473            let mut is_first = true;
13474            let mut prev_selection_was_entire_line = false;
13475            for selection in &mut selections {
13476                let is_entire_line =
13477                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13478                if is_entire_line {
13479                    selection.start = Point::new(selection.start.row, 0);
13480                    if !selection.is_empty() && selection.end.column == 0 {
13481                        selection.end = cmp::min(max_point, selection.end);
13482                    } else {
13483                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13484                    }
13485                    selection.goal = SelectionGoal::None;
13486                }
13487                if is_first {
13488                    is_first = false;
13489                } else if !prev_selection_was_entire_line {
13490                    text += "\n";
13491                }
13492                prev_selection_was_entire_line = is_entire_line;
13493                let mut len = 0;
13494                for chunk in buffer.text_for_range(selection.start..selection.end) {
13495                    text.push_str(chunk);
13496                    len += chunk.len();
13497                }
13498
13499                clipboard_selections.push(ClipboardSelection::for_buffer(
13500                    len,
13501                    is_entire_line,
13502                    selection.range(),
13503                    &buffer,
13504                    self.project.as_ref(),
13505                    cx,
13506                ));
13507            }
13508        }
13509
13510        self.transact(window, cx, |this, window, cx| {
13511            this.change_selections(Default::default(), window, cx, |s| {
13512                s.select(selections);
13513            });
13514            this.insert("", window, cx);
13515        });
13516        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13517    }
13518
13519    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13520        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13521        let item = self.cut_common(true, window, cx);
13522        cx.write_to_clipboard(item);
13523    }
13524
13525    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13526        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13527        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13528            s.move_with(&mut |snapshot, sel| {
13529                if sel.is_empty() {
13530                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13531                }
13532                if sel.is_empty() {
13533                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13534                }
13535            });
13536        });
13537        let item = self.cut_common(false, window, cx);
13538        cx.set_global(KillRing(item))
13539    }
13540
13541    pub fn kill_ring_yank(
13542        &mut self,
13543        _: &KillRingYank,
13544        window: &mut Window,
13545        cx: &mut Context<Self>,
13546    ) {
13547        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13548        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13549            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13550                (kill_ring.text().to_string(), kill_ring.metadata_json())
13551            } else {
13552                return;
13553            }
13554        } else {
13555            return;
13556        };
13557        self.do_paste(&text, metadata, false, window, cx);
13558    }
13559
13560    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13561        self.do_copy(true, cx);
13562    }
13563
13564    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13565        self.do_copy(false, cx);
13566    }
13567
13568    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13569        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13570        let buffer = self.buffer.read(cx).read(cx);
13571        let mut text = String::new();
13572        let mut clipboard_selections = Vec::with_capacity(selections.len());
13573
13574        let max_point = buffer.max_point();
13575        let mut is_first = true;
13576        for selection in &selections {
13577            let mut start = selection.start;
13578            let mut end = selection.end;
13579            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13580            let mut add_trailing_newline = false;
13581            if is_entire_line {
13582                start = Point::new(start.row, 0);
13583                let next_line_start = Point::new(end.row + 1, 0);
13584                if next_line_start <= max_point {
13585                    end = next_line_start;
13586                } else {
13587                    // We're on the last line without a trailing newline.
13588                    // Copy to the end of the line and add a newline afterwards.
13589                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13590                    add_trailing_newline = true;
13591                }
13592            }
13593
13594            let mut trimmed_selections = Vec::new();
13595            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13596                let row = MultiBufferRow(start.row);
13597                let first_indent = buffer.indent_size_for_line(row);
13598                if first_indent.len == 0 || start.column > first_indent.len {
13599                    trimmed_selections.push(start..end);
13600                } else {
13601                    trimmed_selections.push(
13602                        Point::new(row.0, first_indent.len)
13603                            ..Point::new(row.0, buffer.line_len(row)),
13604                    );
13605                    for row in start.row + 1..=end.row {
13606                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13607                        if row == end.row {
13608                            line_len = end.column;
13609                        }
13610                        if line_len == 0 {
13611                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13612                            continue;
13613                        }
13614                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13615                        if row_indent_size.len >= first_indent.len {
13616                            trimmed_selections
13617                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13618                        } else {
13619                            trimmed_selections.clear();
13620                            trimmed_selections.push(start..end);
13621                            break;
13622                        }
13623                    }
13624                }
13625            } else {
13626                trimmed_selections.push(start..end);
13627            }
13628
13629            let is_multiline_trim = trimmed_selections.len() > 1;
13630            let mut selection_len: usize = 0;
13631            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13632
13633            for trimmed_range in trimmed_selections {
13634                if is_first {
13635                    is_first = false;
13636                } else if is_multiline_trim || !prev_selection_was_entire_line {
13637                    text.push('\n');
13638                    if is_multiline_trim {
13639                        selection_len += 1;
13640                    }
13641                }
13642                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13643                    text.push_str(chunk);
13644                    selection_len += chunk.len();
13645                }
13646                if add_trailing_newline {
13647                    text.push('\n');
13648                    selection_len += 1;
13649                }
13650            }
13651
13652            clipboard_selections.push(ClipboardSelection::for_buffer(
13653                selection_len,
13654                is_entire_line,
13655                start..end,
13656                &buffer,
13657                self.project.as_ref(),
13658                cx,
13659            ));
13660        }
13661
13662        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13663            text,
13664            clipboard_selections,
13665        ));
13666    }
13667
13668    pub fn do_paste(
13669        &mut self,
13670        text: &String,
13671        clipboard_selections: Option<Vec<ClipboardSelection>>,
13672        handle_entire_lines: bool,
13673        window: &mut Window,
13674        cx: &mut Context<Self>,
13675    ) {
13676        if self.read_only(cx) {
13677            return;
13678        }
13679
13680        let clipboard_text = Cow::Borrowed(text.as_str());
13681
13682        self.transact(window, cx, |this, window, cx| {
13683            let had_active_edit_prediction = this.has_active_edit_prediction();
13684            let display_map = this.display_snapshot(cx);
13685            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13686            let cursor_offset = this
13687                .selections
13688                .last::<MultiBufferOffset>(&display_map)
13689                .head();
13690
13691            if let Some(mut clipboard_selections) = clipboard_selections {
13692                let all_selections_were_entire_line =
13693                    clipboard_selections.iter().all(|s| s.is_entire_line);
13694                let first_selection_indent_column =
13695                    clipboard_selections.first().map(|s| s.first_line_indent);
13696                if clipboard_selections.len() != old_selections.len() {
13697                    clipboard_selections.drain(..);
13698                }
13699                let mut auto_indent_on_paste = true;
13700
13701                this.buffer.update(cx, |buffer, cx| {
13702                    let snapshot = buffer.read(cx);
13703                    auto_indent_on_paste = snapshot
13704                        .language_settings_at(cursor_offset, cx)
13705                        .auto_indent_on_paste;
13706
13707                    let mut start_offset = 0;
13708                    let mut edits = Vec::new();
13709                    let mut original_indent_columns = Vec::new();
13710                    for (ix, selection) in old_selections.iter().enumerate() {
13711                        let to_insert;
13712                        let entire_line;
13713                        let original_indent_column;
13714                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13715                            let end_offset = start_offset + clipboard_selection.len;
13716                            to_insert = &clipboard_text[start_offset..end_offset];
13717                            entire_line = clipboard_selection.is_entire_line;
13718                            start_offset = if entire_line {
13719                                end_offset
13720                            } else {
13721                                end_offset + 1
13722                            };
13723                            original_indent_column = Some(clipboard_selection.first_line_indent);
13724                        } else {
13725                            to_insert = &*clipboard_text;
13726                            entire_line = all_selections_were_entire_line;
13727                            original_indent_column = first_selection_indent_column
13728                        }
13729
13730                        let (range, to_insert) =
13731                            if selection.is_empty() && handle_entire_lines && entire_line {
13732                                // If the corresponding selection was empty when this slice of the
13733                                // clipboard text was written, then the entire line containing the
13734                                // selection was copied. If this selection is also currently empty,
13735                                // then paste the line before the current line of the buffer.
13736                                let column = selection.start.to_point(&snapshot).column as usize;
13737                                let line_start = selection.start - column;
13738                                (line_start..line_start, Cow::Borrowed(to_insert))
13739                            } else {
13740                                let language = snapshot.language_at(selection.head());
13741                                let range = selection.range();
13742                                if let Some(language) = language
13743                                    && language.name() == "Markdown"
13744                                {
13745                                    edit_for_markdown_paste(
13746                                        &snapshot,
13747                                        range,
13748                                        to_insert,
13749                                        url::Url::parse(to_insert).ok(),
13750                                    )
13751                                } else {
13752                                    (range, Cow::Borrowed(to_insert))
13753                                }
13754                            };
13755
13756                        edits.push((range, to_insert));
13757                        original_indent_columns.push(original_indent_column);
13758                    }
13759                    drop(snapshot);
13760
13761                    buffer.edit(
13762                        edits,
13763                        if auto_indent_on_paste {
13764                            Some(AutoindentMode::Block {
13765                                original_indent_columns,
13766                            })
13767                        } else {
13768                            None
13769                        },
13770                        cx,
13771                    );
13772                });
13773
13774                let selections = this
13775                    .selections
13776                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13777                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13778            } else {
13779                let url = url::Url::parse(&clipboard_text).ok();
13780
13781                let auto_indent_mode = if !clipboard_text.is_empty() {
13782                    Some(AutoindentMode::Block {
13783                        original_indent_columns: Vec::new(),
13784                    })
13785                } else {
13786                    None
13787                };
13788
13789                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13790                    let snapshot = buffer.snapshot(cx);
13791
13792                    let anchors = old_selections
13793                        .iter()
13794                        .map(|s| {
13795                            let anchor = snapshot.anchor_after(s.head());
13796                            s.map(|_| anchor)
13797                        })
13798                        .collect::<Vec<_>>();
13799
13800                    let mut edits = Vec::new();
13801
13802                    // When pasting text without metadata (e.g. copied from an
13803                    // external editor using multiple cursors) and the number of
13804                    // lines matches the number of selections, distribute one
13805                    // line per cursor instead of pasting the whole text at each.
13806                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
13807                    let distribute_lines =
13808                        old_selections.len() > 1 && lines.len() == old_selections.len();
13809
13810                    for (ix, selection) in old_selections.iter().enumerate() {
13811                        let language = snapshot.language_at(selection.head());
13812                        let range = selection.range();
13813
13814                        let text_for_cursor: &str = if distribute_lines {
13815                            lines[ix]
13816                        } else {
13817                            &clipboard_text
13818                        };
13819
13820                        let (edit_range, edit_text) = if let Some(language) = language
13821                            && language.name() == "Markdown"
13822                        {
13823                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
13824                        } else {
13825                            (range, Cow::Borrowed(text_for_cursor))
13826                        };
13827
13828                        edits.push((edit_range, edit_text));
13829                    }
13830
13831                    drop(snapshot);
13832                    buffer.edit(edits, auto_indent_mode, cx);
13833
13834                    anchors
13835                });
13836
13837                this.change_selections(Default::default(), window, cx, |s| {
13838                    s.select_anchors(selection_anchors);
13839                });
13840            }
13841
13842            //   🤔                 |    ..     | show_in_menu |
13843            // | ..                  |   true        true
13844            // | had_edit_prediction |   false       true
13845
13846            let trigger_in_words =
13847                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13848
13849            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13850        });
13851    }
13852
13853    pub fn diff_clipboard_with_selection(
13854        &mut self,
13855        _: &DiffClipboardWithSelection,
13856        window: &mut Window,
13857        cx: &mut Context<Self>,
13858    ) {
13859        let selections = self
13860            .selections
13861            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13862
13863        if selections.is_empty() {
13864            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13865            return;
13866        };
13867
13868        let clipboard_text = match cx.read_from_clipboard() {
13869            Some(item) => match item.entries().first() {
13870                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13871                _ => None,
13872            },
13873            None => None,
13874        };
13875
13876        let Some(clipboard_text) = clipboard_text else {
13877            log::warn!("Clipboard doesn't contain text.");
13878            return;
13879        };
13880
13881        window.dispatch_action(
13882            Box::new(DiffClipboardWithSelectionData {
13883                clipboard_text,
13884                editor: cx.entity(),
13885            }),
13886            cx,
13887        );
13888    }
13889
13890    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13891        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13892        if let Some(item) = cx.read_from_clipboard() {
13893            let entries = item.entries();
13894
13895            match entries.first() {
13896                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13897                // of all the pasted entries.
13898                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13899                    .do_paste(
13900                        clipboard_string.text(),
13901                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13902                        true,
13903                        window,
13904                        cx,
13905                    ),
13906                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13907            }
13908        }
13909    }
13910
13911    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13912        if self.read_only(cx) {
13913            return;
13914        }
13915
13916        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13917
13918        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13919            if let Some((selections, _)) =
13920                self.selection_history.transaction(transaction_id).cloned()
13921            {
13922                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13923                    s.select_anchors(selections.to_vec());
13924                });
13925            } else {
13926                log::error!(
13927                    "No entry in selection_history found for undo. \
13928                     This may correspond to a bug where undo does not update the selection. \
13929                     If this is occurring, please add details to \
13930                     https://github.com/zed-industries/zed/issues/22692"
13931                );
13932            }
13933            self.request_autoscroll(Autoscroll::fit(), cx);
13934            self.unmark_text(window, cx);
13935            self.refresh_edit_prediction(true, false, window, cx);
13936            cx.emit(EditorEvent::Edited { transaction_id });
13937            cx.emit(EditorEvent::TransactionUndone { transaction_id });
13938        }
13939    }
13940
13941    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
13942        if self.read_only(cx) {
13943            return;
13944        }
13945
13946        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13947
13948        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
13949            if let Some((_, Some(selections))) =
13950                self.selection_history.transaction(transaction_id).cloned()
13951            {
13952                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13953                    s.select_anchors(selections.to_vec());
13954                });
13955            } else {
13956                log::error!(
13957                    "No entry in selection_history found for redo. \
13958                     This may correspond to a bug where undo does not update the selection. \
13959                     If this is occurring, please add details to \
13960                     https://github.com/zed-industries/zed/issues/22692"
13961                );
13962            }
13963            self.request_autoscroll(Autoscroll::fit(), cx);
13964            self.unmark_text(window, cx);
13965            self.refresh_edit_prediction(true, false, window, cx);
13966            cx.emit(EditorEvent::Edited { transaction_id });
13967        }
13968    }
13969
13970    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
13971        self.buffer
13972            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
13973    }
13974
13975    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
13976        self.buffer
13977            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
13978    }
13979
13980    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
13981        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13982        self.change_selections(Default::default(), window, cx, |s| {
13983            s.move_with(&mut |map, selection| {
13984                let cursor = if selection.is_empty() {
13985                    movement::left(map, selection.start)
13986                } else {
13987                    selection.start
13988                };
13989                selection.collapse_to(cursor, SelectionGoal::None);
13990            });
13991        })
13992    }
13993
13994    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
13995        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13996        self.change_selections(Default::default(), window, cx, |s| {
13997            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
13998        })
13999    }
14000
14001    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14002        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14003        self.change_selections(Default::default(), window, cx, |s| {
14004            s.move_with(&mut |map, selection| {
14005                let cursor = if selection.is_empty() {
14006                    movement::right(map, selection.end)
14007                } else {
14008                    selection.end
14009                };
14010                selection.collapse_to(cursor, SelectionGoal::None)
14011            });
14012        })
14013    }
14014
14015    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14016        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14017        self.change_selections(Default::default(), window, cx, |s| {
14018            s.move_heads_with(&mut |map, head, _| {
14019                (movement::right(map, head), SelectionGoal::None)
14020            });
14021        });
14022    }
14023
14024    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14025        if self.take_rename(true, window, cx).is_some() {
14026            return;
14027        }
14028
14029        if self.mode.is_single_line() {
14030            cx.propagate();
14031            return;
14032        }
14033
14034        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14035
14036        let text_layout_details = &self.text_layout_details(window, cx);
14037        let selection_count = self.selections.count();
14038        let first_selection = self.selections.first_anchor();
14039
14040        self.change_selections(Default::default(), window, cx, |s| {
14041            s.move_with(&mut |map, selection| {
14042                if !selection.is_empty() {
14043                    selection.goal = SelectionGoal::None;
14044                }
14045                let (cursor, goal) = movement::up(
14046                    map,
14047                    selection.start,
14048                    selection.goal,
14049                    false,
14050                    text_layout_details,
14051                );
14052                selection.collapse_to(cursor, goal);
14053            });
14054        });
14055
14056        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14057        {
14058            cx.propagate();
14059        }
14060    }
14061
14062    pub fn move_up_by_lines(
14063        &mut self,
14064        action: &MoveUpByLines,
14065        window: &mut Window,
14066        cx: &mut Context<Self>,
14067    ) {
14068        if self.take_rename(true, window, cx).is_some() {
14069            return;
14070        }
14071
14072        if self.mode.is_single_line() {
14073            cx.propagate();
14074            return;
14075        }
14076
14077        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14078
14079        let text_layout_details = &self.text_layout_details(window, cx);
14080
14081        self.change_selections(Default::default(), window, cx, |s| {
14082            s.move_with(&mut |map, selection| {
14083                if !selection.is_empty() {
14084                    selection.goal = SelectionGoal::None;
14085                }
14086                let (cursor, goal) = movement::up_by_rows(
14087                    map,
14088                    selection.start,
14089                    action.lines,
14090                    selection.goal,
14091                    false,
14092                    text_layout_details,
14093                );
14094                selection.collapse_to(cursor, goal);
14095            });
14096        })
14097    }
14098
14099    pub fn move_down_by_lines(
14100        &mut self,
14101        action: &MoveDownByLines,
14102        window: &mut Window,
14103        cx: &mut Context<Self>,
14104    ) {
14105        if self.take_rename(true, window, cx).is_some() {
14106            return;
14107        }
14108
14109        if self.mode.is_single_line() {
14110            cx.propagate();
14111            return;
14112        }
14113
14114        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14115
14116        let text_layout_details = &self.text_layout_details(window, cx);
14117
14118        self.change_selections(Default::default(), window, cx, |s| {
14119            s.move_with(&mut |map, selection| {
14120                if !selection.is_empty() {
14121                    selection.goal = SelectionGoal::None;
14122                }
14123                let (cursor, goal) = movement::down_by_rows(
14124                    map,
14125                    selection.start,
14126                    action.lines,
14127                    selection.goal,
14128                    false,
14129                    text_layout_details,
14130                );
14131                selection.collapse_to(cursor, goal);
14132            });
14133        })
14134    }
14135
14136    pub fn select_down_by_lines(
14137        &mut self,
14138        action: &SelectDownByLines,
14139        window: &mut Window,
14140        cx: &mut Context<Self>,
14141    ) {
14142        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14143        let text_layout_details = &self.text_layout_details(window, cx);
14144        self.change_selections(Default::default(), window, cx, |s| {
14145            s.move_heads_with(&mut |map, head, goal| {
14146                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14147            })
14148        })
14149    }
14150
14151    pub fn select_up_by_lines(
14152        &mut self,
14153        action: &SelectUpByLines,
14154        window: &mut Window,
14155        cx: &mut Context<Self>,
14156    ) {
14157        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14158        let text_layout_details = &self.text_layout_details(window, cx);
14159        self.change_selections(Default::default(), window, cx, |s| {
14160            s.move_heads_with(&mut |map, head, goal| {
14161                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14162            })
14163        })
14164    }
14165
14166    pub fn select_page_up(
14167        &mut self,
14168        _: &SelectPageUp,
14169        window: &mut Window,
14170        cx: &mut Context<Self>,
14171    ) {
14172        let Some(row_count) = self.visible_row_count() else {
14173            return;
14174        };
14175
14176        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14177
14178        let text_layout_details = &self.text_layout_details(window, cx);
14179
14180        self.change_selections(Default::default(), window, cx, |s| {
14181            s.move_heads_with(&mut |map, head, goal| {
14182                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14183            })
14184        })
14185    }
14186
14187    pub fn move_page_up(
14188        &mut self,
14189        action: &MovePageUp,
14190        window: &mut Window,
14191        cx: &mut Context<Self>,
14192    ) {
14193        if self.take_rename(true, window, cx).is_some() {
14194            return;
14195        }
14196
14197        if self
14198            .context_menu
14199            .borrow_mut()
14200            .as_mut()
14201            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14202            .unwrap_or(false)
14203        {
14204            return;
14205        }
14206
14207        if matches!(self.mode, EditorMode::SingleLine) {
14208            cx.propagate();
14209            return;
14210        }
14211
14212        let Some(row_count) = self.visible_row_count() else {
14213            return;
14214        };
14215
14216        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14217
14218        let effects = if action.center_cursor {
14219            SelectionEffects::scroll(Autoscroll::center())
14220        } else {
14221            SelectionEffects::default()
14222        };
14223
14224        let text_layout_details = &self.text_layout_details(window, cx);
14225
14226        self.change_selections(effects, window, cx, |s| {
14227            s.move_with(&mut |map, selection| {
14228                if !selection.is_empty() {
14229                    selection.goal = SelectionGoal::None;
14230                }
14231                let (cursor, goal) = movement::up_by_rows(
14232                    map,
14233                    selection.end,
14234                    row_count,
14235                    selection.goal,
14236                    false,
14237                    text_layout_details,
14238                );
14239                selection.collapse_to(cursor, goal);
14240            });
14241        });
14242    }
14243
14244    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14245        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14246        let text_layout_details = &self.text_layout_details(window, cx);
14247        self.change_selections(Default::default(), window, cx, |s| {
14248            s.move_heads_with(&mut |map, head, goal| {
14249                movement::up(map, head, goal, false, text_layout_details)
14250            })
14251        })
14252    }
14253
14254    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14255        self.take_rename(true, window, cx);
14256
14257        if self.mode.is_single_line() {
14258            cx.propagate();
14259            return;
14260        }
14261
14262        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14263
14264        let text_layout_details = &self.text_layout_details(window, cx);
14265        let selection_count = self.selections.count();
14266        let first_selection = self.selections.first_anchor();
14267
14268        self.change_selections(Default::default(), window, cx, |s| {
14269            s.move_with(&mut |map, selection| {
14270                if !selection.is_empty() {
14271                    selection.goal = SelectionGoal::None;
14272                }
14273                let (cursor, goal) = movement::down(
14274                    map,
14275                    selection.end,
14276                    selection.goal,
14277                    false,
14278                    text_layout_details,
14279                );
14280                selection.collapse_to(cursor, goal);
14281            });
14282        });
14283
14284        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14285        {
14286            cx.propagate();
14287        }
14288    }
14289
14290    pub fn select_page_down(
14291        &mut self,
14292        _: &SelectPageDown,
14293        window: &mut Window,
14294        cx: &mut Context<Self>,
14295    ) {
14296        let Some(row_count) = self.visible_row_count() else {
14297            return;
14298        };
14299
14300        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14301
14302        let text_layout_details = &self.text_layout_details(window, cx);
14303
14304        self.change_selections(Default::default(), window, cx, |s| {
14305            s.move_heads_with(&mut |map, head, goal| {
14306                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14307            })
14308        })
14309    }
14310
14311    pub fn move_page_down(
14312        &mut self,
14313        action: &MovePageDown,
14314        window: &mut Window,
14315        cx: &mut Context<Self>,
14316    ) {
14317        if self.take_rename(true, window, cx).is_some() {
14318            return;
14319        }
14320
14321        if self
14322            .context_menu
14323            .borrow_mut()
14324            .as_mut()
14325            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14326            .unwrap_or(false)
14327        {
14328            return;
14329        }
14330
14331        if matches!(self.mode, EditorMode::SingleLine) {
14332            cx.propagate();
14333            return;
14334        }
14335
14336        let Some(row_count) = self.visible_row_count() else {
14337            return;
14338        };
14339
14340        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14341
14342        let effects = if action.center_cursor {
14343            SelectionEffects::scroll(Autoscroll::center())
14344        } else {
14345            SelectionEffects::default()
14346        };
14347
14348        let text_layout_details = &self.text_layout_details(window, cx);
14349        self.change_selections(effects, window, cx, |s| {
14350            s.move_with(&mut |map, selection| {
14351                if !selection.is_empty() {
14352                    selection.goal = SelectionGoal::None;
14353                }
14354                let (cursor, goal) = movement::down_by_rows(
14355                    map,
14356                    selection.end,
14357                    row_count,
14358                    selection.goal,
14359                    false,
14360                    text_layout_details,
14361                );
14362                selection.collapse_to(cursor, goal);
14363            });
14364        });
14365    }
14366
14367    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14368        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14369        let text_layout_details = &self.text_layout_details(window, cx);
14370        self.change_selections(Default::default(), window, cx, |s| {
14371            s.move_heads_with(&mut |map, head, goal| {
14372                movement::down(map, head, goal, false, text_layout_details)
14373            })
14374        });
14375    }
14376
14377    pub fn context_menu_first(
14378        &mut self,
14379        _: &ContextMenuFirst,
14380        window: &mut Window,
14381        cx: &mut Context<Self>,
14382    ) {
14383        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14384            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14385        }
14386    }
14387
14388    pub fn context_menu_prev(
14389        &mut self,
14390        _: &ContextMenuPrevious,
14391        window: &mut Window,
14392        cx: &mut Context<Self>,
14393    ) {
14394        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14395            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14396        }
14397    }
14398
14399    pub fn context_menu_next(
14400        &mut self,
14401        _: &ContextMenuNext,
14402        window: &mut Window,
14403        cx: &mut Context<Self>,
14404    ) {
14405        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14406            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14407        }
14408    }
14409
14410    pub fn context_menu_last(
14411        &mut self,
14412        _: &ContextMenuLast,
14413        window: &mut Window,
14414        cx: &mut Context<Self>,
14415    ) {
14416        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14417            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14418        }
14419    }
14420
14421    pub fn signature_help_prev(
14422        &mut self,
14423        _: &SignatureHelpPrevious,
14424        _: &mut Window,
14425        cx: &mut Context<Self>,
14426    ) {
14427        if let Some(popover) = self.signature_help_state.popover_mut() {
14428            if popover.current_signature == 0 {
14429                popover.current_signature = popover.signatures.len() - 1;
14430            } else {
14431                popover.current_signature -= 1;
14432            }
14433            cx.notify();
14434        }
14435    }
14436
14437    pub fn signature_help_next(
14438        &mut self,
14439        _: &SignatureHelpNext,
14440        _: &mut Window,
14441        cx: &mut Context<Self>,
14442    ) {
14443        if let Some(popover) = self.signature_help_state.popover_mut() {
14444            if popover.current_signature + 1 == popover.signatures.len() {
14445                popover.current_signature = 0;
14446            } else {
14447                popover.current_signature += 1;
14448            }
14449            cx.notify();
14450        }
14451    }
14452
14453    pub fn move_to_previous_word_start(
14454        &mut self,
14455        _: &MoveToPreviousWordStart,
14456        window: &mut Window,
14457        cx: &mut Context<Self>,
14458    ) {
14459        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14460        self.change_selections(Default::default(), window, cx, |s| {
14461            s.move_cursors_with(&mut |map, head, _| {
14462                (
14463                    movement::previous_word_start(map, head),
14464                    SelectionGoal::None,
14465                )
14466            });
14467        })
14468    }
14469
14470    pub fn move_to_previous_subword_start(
14471        &mut self,
14472        _: &MoveToPreviousSubwordStart,
14473        window: &mut Window,
14474        cx: &mut Context<Self>,
14475    ) {
14476        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14477        self.change_selections(Default::default(), window, cx, |s| {
14478            s.move_cursors_with(&mut |map, head, _| {
14479                (
14480                    movement::previous_subword_start(map, head),
14481                    SelectionGoal::None,
14482                )
14483            });
14484        })
14485    }
14486
14487    pub fn select_to_previous_word_start(
14488        &mut self,
14489        _: &SelectToPreviousWordStart,
14490        window: &mut Window,
14491        cx: &mut Context<Self>,
14492    ) {
14493        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14494        self.change_selections(Default::default(), window, cx, |s| {
14495            s.move_heads_with(&mut |map, head, _| {
14496                (
14497                    movement::previous_word_start(map, head),
14498                    SelectionGoal::None,
14499                )
14500            });
14501        })
14502    }
14503
14504    pub fn select_to_previous_subword_start(
14505        &mut self,
14506        _: &SelectToPreviousSubwordStart,
14507        window: &mut Window,
14508        cx: &mut Context<Self>,
14509    ) {
14510        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14511        self.change_selections(Default::default(), window, cx, |s| {
14512            s.move_heads_with(&mut |map, head, _| {
14513                (
14514                    movement::previous_subword_start(map, head),
14515                    SelectionGoal::None,
14516                )
14517            });
14518        })
14519    }
14520
14521    pub fn delete_to_previous_word_start(
14522        &mut self,
14523        action: &DeleteToPreviousWordStart,
14524        window: &mut Window,
14525        cx: &mut Context<Self>,
14526    ) {
14527        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14528        self.transact(window, cx, |this, window, cx| {
14529            this.select_autoclose_pair(window, cx);
14530            this.change_selections(Default::default(), window, cx, |s| {
14531                s.move_with(&mut |map, selection| {
14532                    if selection.is_empty() {
14533                        let mut cursor = if action.ignore_newlines {
14534                            movement::previous_word_start(map, selection.head())
14535                        } else {
14536                            movement::previous_word_start_or_newline(map, selection.head())
14537                        };
14538                        cursor = movement::adjust_greedy_deletion(
14539                            map,
14540                            selection.head(),
14541                            cursor,
14542                            action.ignore_brackets,
14543                        );
14544                        selection.set_head(cursor, SelectionGoal::None);
14545                    }
14546                });
14547            });
14548            this.insert("", window, cx);
14549        });
14550    }
14551
14552    pub fn delete_to_previous_subword_start(
14553        &mut self,
14554        action: &DeleteToPreviousSubwordStart,
14555        window: &mut Window,
14556        cx: &mut Context<Self>,
14557    ) {
14558        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14559        self.transact(window, cx, |this, window, cx| {
14560            this.select_autoclose_pair(window, cx);
14561            this.change_selections(Default::default(), window, cx, |s| {
14562                s.move_with(&mut |map, selection| {
14563                    if selection.is_empty() {
14564                        let mut cursor = if action.ignore_newlines {
14565                            movement::previous_subword_start(map, selection.head())
14566                        } else {
14567                            movement::previous_subword_start_or_newline(map, selection.head())
14568                        };
14569                        cursor = movement::adjust_greedy_deletion(
14570                            map,
14571                            selection.head(),
14572                            cursor,
14573                            action.ignore_brackets,
14574                        );
14575                        selection.set_head(cursor, SelectionGoal::None);
14576                    }
14577                });
14578            });
14579            this.insert("", window, cx);
14580        });
14581    }
14582
14583    pub fn move_to_next_word_end(
14584        &mut self,
14585        _: &MoveToNextWordEnd,
14586        window: &mut Window,
14587        cx: &mut Context<Self>,
14588    ) {
14589        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14590        self.change_selections(Default::default(), window, cx, |s| {
14591            s.move_cursors_with(&mut |map, head, _| {
14592                (movement::next_word_end(map, head), SelectionGoal::None)
14593            });
14594        })
14595    }
14596
14597    pub fn move_to_next_subword_end(
14598        &mut self,
14599        _: &MoveToNextSubwordEnd,
14600        window: &mut Window,
14601        cx: &mut Context<Self>,
14602    ) {
14603        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14604        self.change_selections(Default::default(), window, cx, |s| {
14605            s.move_cursors_with(&mut |map, head, _| {
14606                (movement::next_subword_end(map, head), SelectionGoal::None)
14607            });
14608        })
14609    }
14610
14611    pub fn select_to_next_word_end(
14612        &mut self,
14613        _: &SelectToNextWordEnd,
14614        window: &mut Window,
14615        cx: &mut Context<Self>,
14616    ) {
14617        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14618        self.change_selections(Default::default(), window, cx, |s| {
14619            s.move_heads_with(&mut |map, head, _| {
14620                (movement::next_word_end(map, head), SelectionGoal::None)
14621            });
14622        })
14623    }
14624
14625    pub fn select_to_next_subword_end(
14626        &mut self,
14627        _: &SelectToNextSubwordEnd,
14628        window: &mut Window,
14629        cx: &mut Context<Self>,
14630    ) {
14631        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14632        self.change_selections(Default::default(), window, cx, |s| {
14633            s.move_heads_with(&mut |map, head, _| {
14634                (movement::next_subword_end(map, head), SelectionGoal::None)
14635            });
14636        })
14637    }
14638
14639    pub fn delete_to_next_word_end(
14640        &mut self,
14641        action: &DeleteToNextWordEnd,
14642        window: &mut Window,
14643        cx: &mut Context<Self>,
14644    ) {
14645        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14646        self.transact(window, cx, |this, window, cx| {
14647            this.change_selections(Default::default(), window, cx, |s| {
14648                s.move_with(&mut |map, selection| {
14649                    if selection.is_empty() {
14650                        let mut cursor = if action.ignore_newlines {
14651                            movement::next_word_end(map, selection.head())
14652                        } else {
14653                            movement::next_word_end_or_newline(map, selection.head())
14654                        };
14655                        cursor = movement::adjust_greedy_deletion(
14656                            map,
14657                            selection.head(),
14658                            cursor,
14659                            action.ignore_brackets,
14660                        );
14661                        selection.set_head(cursor, SelectionGoal::None);
14662                    }
14663                });
14664            });
14665            this.insert("", window, cx);
14666        });
14667    }
14668
14669    pub fn delete_to_next_subword_end(
14670        &mut self,
14671        action: &DeleteToNextSubwordEnd,
14672        window: &mut Window,
14673        cx: &mut Context<Self>,
14674    ) {
14675        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14676        self.transact(window, cx, |this, window, cx| {
14677            this.change_selections(Default::default(), window, cx, |s| {
14678                s.move_with(&mut |map, selection| {
14679                    if selection.is_empty() {
14680                        let mut cursor = if action.ignore_newlines {
14681                            movement::next_subword_end(map, selection.head())
14682                        } else {
14683                            movement::next_subword_end_or_newline(map, selection.head())
14684                        };
14685                        cursor = movement::adjust_greedy_deletion(
14686                            map,
14687                            selection.head(),
14688                            cursor,
14689                            action.ignore_brackets,
14690                        );
14691                        selection.set_head(cursor, SelectionGoal::None);
14692                    }
14693                });
14694            });
14695            this.insert("", window, cx);
14696        });
14697    }
14698
14699    pub fn move_to_beginning_of_line(
14700        &mut self,
14701        action: &MoveToBeginningOfLine,
14702        window: &mut Window,
14703        cx: &mut Context<Self>,
14704    ) {
14705        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14706        self.change_selections(Default::default(), window, cx, |s| {
14707            s.move_cursors_with(&mut |map, head, _| {
14708                (
14709                    movement::indented_line_beginning(
14710                        map,
14711                        head,
14712                        action.stop_at_soft_wraps,
14713                        action.stop_at_indent,
14714                    ),
14715                    SelectionGoal::None,
14716                )
14717            });
14718        })
14719    }
14720
14721    pub fn select_to_beginning_of_line(
14722        &mut self,
14723        action: &SelectToBeginningOfLine,
14724        window: &mut Window,
14725        cx: &mut Context<Self>,
14726    ) {
14727        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14728        self.change_selections(Default::default(), window, cx, |s| {
14729            s.move_heads_with(&mut |map, head, _| {
14730                (
14731                    movement::indented_line_beginning(
14732                        map,
14733                        head,
14734                        action.stop_at_soft_wraps,
14735                        action.stop_at_indent,
14736                    ),
14737                    SelectionGoal::None,
14738                )
14739            });
14740        });
14741    }
14742
14743    pub fn delete_to_beginning_of_line(
14744        &mut self,
14745        action: &DeleteToBeginningOfLine,
14746        window: &mut Window,
14747        cx: &mut Context<Self>,
14748    ) {
14749        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14750        self.transact(window, cx, |this, window, cx| {
14751            this.change_selections(Default::default(), window, cx, |s| {
14752                s.move_with(&mut |_, selection| {
14753                    selection.reversed = true;
14754                });
14755            });
14756
14757            this.select_to_beginning_of_line(
14758                &SelectToBeginningOfLine {
14759                    stop_at_soft_wraps: false,
14760                    stop_at_indent: action.stop_at_indent,
14761                },
14762                window,
14763                cx,
14764            );
14765            this.backspace(&Backspace, window, cx);
14766        });
14767    }
14768
14769    pub fn move_to_end_of_line(
14770        &mut self,
14771        action: &MoveToEndOfLine,
14772        window: &mut Window,
14773        cx: &mut Context<Self>,
14774    ) {
14775        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14776        self.change_selections(Default::default(), window, cx, |s| {
14777            s.move_cursors_with(&mut |map, head, _| {
14778                (
14779                    movement::line_end(map, head, action.stop_at_soft_wraps),
14780                    SelectionGoal::None,
14781                )
14782            });
14783        })
14784    }
14785
14786    pub fn select_to_end_of_line(
14787        &mut self,
14788        action: &SelectToEndOfLine,
14789        window: &mut Window,
14790        cx: &mut Context<Self>,
14791    ) {
14792        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14793        self.change_selections(Default::default(), window, cx, |s| {
14794            s.move_heads_with(&mut |map, head, _| {
14795                (
14796                    movement::line_end(map, head, action.stop_at_soft_wraps),
14797                    SelectionGoal::None,
14798                )
14799            });
14800        })
14801    }
14802
14803    pub fn delete_to_end_of_line(
14804        &mut self,
14805        _: &DeleteToEndOfLine,
14806        window: &mut Window,
14807        cx: &mut Context<Self>,
14808    ) {
14809        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14810        self.transact(window, cx, |this, window, cx| {
14811            this.select_to_end_of_line(
14812                &SelectToEndOfLine {
14813                    stop_at_soft_wraps: false,
14814                },
14815                window,
14816                cx,
14817            );
14818            this.delete(&Delete, window, cx);
14819        });
14820    }
14821
14822    pub fn cut_to_end_of_line(
14823        &mut self,
14824        action: &CutToEndOfLine,
14825        window: &mut Window,
14826        cx: &mut Context<Self>,
14827    ) {
14828        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14829        self.transact(window, cx, |this, window, cx| {
14830            this.select_to_end_of_line(
14831                &SelectToEndOfLine {
14832                    stop_at_soft_wraps: false,
14833                },
14834                window,
14835                cx,
14836            );
14837            if !action.stop_at_newlines {
14838                this.change_selections(Default::default(), window, cx, |s| {
14839                    s.move_with(&mut |_, sel| {
14840                        if sel.is_empty() {
14841                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14842                        }
14843                    });
14844                });
14845            }
14846            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14847            let item = this.cut_common(false, window, cx);
14848            cx.write_to_clipboard(item);
14849        });
14850    }
14851
14852    pub fn move_to_start_of_paragraph(
14853        &mut self,
14854        _: &MoveToStartOfParagraph,
14855        window: &mut Window,
14856        cx: &mut Context<Self>,
14857    ) {
14858        if matches!(self.mode, EditorMode::SingleLine) {
14859            cx.propagate();
14860            return;
14861        }
14862        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14863        self.change_selections(Default::default(), window, cx, |s| {
14864            s.move_with(&mut |map, selection| {
14865                selection.collapse_to(
14866                    movement::start_of_paragraph(map, selection.head(), 1),
14867                    SelectionGoal::None,
14868                )
14869            });
14870        })
14871    }
14872
14873    pub fn move_to_end_of_paragraph(
14874        &mut self,
14875        _: &MoveToEndOfParagraph,
14876        window: &mut Window,
14877        cx: &mut Context<Self>,
14878    ) {
14879        if matches!(self.mode, EditorMode::SingleLine) {
14880            cx.propagate();
14881            return;
14882        }
14883        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14884        self.change_selections(Default::default(), window, cx, |s| {
14885            s.move_with(&mut |map, selection| {
14886                selection.collapse_to(
14887                    movement::end_of_paragraph(map, selection.head(), 1),
14888                    SelectionGoal::None,
14889                )
14890            });
14891        })
14892    }
14893
14894    pub fn select_to_start_of_paragraph(
14895        &mut self,
14896        _: &SelectToStartOfParagraph,
14897        window: &mut Window,
14898        cx: &mut Context<Self>,
14899    ) {
14900        if matches!(self.mode, EditorMode::SingleLine) {
14901            cx.propagate();
14902            return;
14903        }
14904        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14905        self.change_selections(Default::default(), window, cx, |s| {
14906            s.move_heads_with(&mut |map, head, _| {
14907                (
14908                    movement::start_of_paragraph(map, head, 1),
14909                    SelectionGoal::None,
14910                )
14911            });
14912        })
14913    }
14914
14915    pub fn select_to_end_of_paragraph(
14916        &mut self,
14917        _: &SelectToEndOfParagraph,
14918        window: &mut Window,
14919        cx: &mut Context<Self>,
14920    ) {
14921        if matches!(self.mode, EditorMode::SingleLine) {
14922            cx.propagate();
14923            return;
14924        }
14925        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14926        self.change_selections(Default::default(), window, cx, |s| {
14927            s.move_heads_with(&mut |map, head, _| {
14928                (
14929                    movement::end_of_paragraph(map, head, 1),
14930                    SelectionGoal::None,
14931                )
14932            });
14933        })
14934    }
14935
14936    pub fn move_to_start_of_excerpt(
14937        &mut self,
14938        _: &MoveToStartOfExcerpt,
14939        window: &mut Window,
14940        cx: &mut Context<Self>,
14941    ) {
14942        if matches!(self.mode, EditorMode::SingleLine) {
14943            cx.propagate();
14944            return;
14945        }
14946        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14947        self.change_selections(Default::default(), window, cx, |s| {
14948            s.move_with(&mut |map, selection| {
14949                selection.collapse_to(
14950                    movement::start_of_excerpt(
14951                        map,
14952                        selection.head(),
14953                        workspace::searchable::Direction::Prev,
14954                    ),
14955                    SelectionGoal::None,
14956                )
14957            });
14958        })
14959    }
14960
14961    pub fn move_to_start_of_next_excerpt(
14962        &mut self,
14963        _: &MoveToStartOfNextExcerpt,
14964        window: &mut Window,
14965        cx: &mut Context<Self>,
14966    ) {
14967        if matches!(self.mode, EditorMode::SingleLine) {
14968            cx.propagate();
14969            return;
14970        }
14971
14972        self.change_selections(Default::default(), window, cx, |s| {
14973            s.move_with(&mut |map, selection| {
14974                selection.collapse_to(
14975                    movement::start_of_excerpt(
14976                        map,
14977                        selection.head(),
14978                        workspace::searchable::Direction::Next,
14979                    ),
14980                    SelectionGoal::None,
14981                )
14982            });
14983        })
14984    }
14985
14986    pub fn move_to_end_of_excerpt(
14987        &mut self,
14988        _: &MoveToEndOfExcerpt,
14989        window: &mut Window,
14990        cx: &mut Context<Self>,
14991    ) {
14992        if matches!(self.mode, EditorMode::SingleLine) {
14993            cx.propagate();
14994            return;
14995        }
14996        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14997        self.change_selections(Default::default(), window, cx, |s| {
14998            s.move_with(&mut |map, selection| {
14999                selection.collapse_to(
15000                    movement::end_of_excerpt(
15001                        map,
15002                        selection.head(),
15003                        workspace::searchable::Direction::Next,
15004                    ),
15005                    SelectionGoal::None,
15006                )
15007            });
15008        })
15009    }
15010
15011    pub fn move_to_end_of_previous_excerpt(
15012        &mut self,
15013        _: &MoveToEndOfPreviousExcerpt,
15014        window: &mut Window,
15015        cx: &mut Context<Self>,
15016    ) {
15017        if matches!(self.mode, EditorMode::SingleLine) {
15018            cx.propagate();
15019            return;
15020        }
15021        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15022        self.change_selections(Default::default(), window, cx, |s| {
15023            s.move_with(&mut |map, selection| {
15024                selection.collapse_to(
15025                    movement::end_of_excerpt(
15026                        map,
15027                        selection.head(),
15028                        workspace::searchable::Direction::Prev,
15029                    ),
15030                    SelectionGoal::None,
15031                )
15032            });
15033        })
15034    }
15035
15036    pub fn select_to_start_of_excerpt(
15037        &mut self,
15038        _: &SelectToStartOfExcerpt,
15039        window: &mut Window,
15040        cx: &mut Context<Self>,
15041    ) {
15042        if matches!(self.mode, EditorMode::SingleLine) {
15043            cx.propagate();
15044            return;
15045        }
15046        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15047        self.change_selections(Default::default(), window, cx, |s| {
15048            s.move_heads_with(&mut |map, head, _| {
15049                (
15050                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15051                    SelectionGoal::None,
15052                )
15053            });
15054        })
15055    }
15056
15057    pub fn select_to_start_of_next_excerpt(
15058        &mut self,
15059        _: &SelectToStartOfNextExcerpt,
15060        window: &mut Window,
15061        cx: &mut Context<Self>,
15062    ) {
15063        if matches!(self.mode, EditorMode::SingleLine) {
15064            cx.propagate();
15065            return;
15066        }
15067        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15068        self.change_selections(Default::default(), window, cx, |s| {
15069            s.move_heads_with(&mut |map, head, _| {
15070                (
15071                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15072                    SelectionGoal::None,
15073                )
15074            });
15075        })
15076    }
15077
15078    pub fn select_to_end_of_excerpt(
15079        &mut self,
15080        _: &SelectToEndOfExcerpt,
15081        window: &mut Window,
15082        cx: &mut Context<Self>,
15083    ) {
15084        if matches!(self.mode, EditorMode::SingleLine) {
15085            cx.propagate();
15086            return;
15087        }
15088        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15089        self.change_selections(Default::default(), window, cx, |s| {
15090            s.move_heads_with(&mut |map, head, _| {
15091                (
15092                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15093                    SelectionGoal::None,
15094                )
15095            });
15096        })
15097    }
15098
15099    pub fn select_to_end_of_previous_excerpt(
15100        &mut self,
15101        _: &SelectToEndOfPreviousExcerpt,
15102        window: &mut Window,
15103        cx: &mut Context<Self>,
15104    ) {
15105        if matches!(self.mode, EditorMode::SingleLine) {
15106            cx.propagate();
15107            return;
15108        }
15109        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15110        self.change_selections(Default::default(), window, cx, |s| {
15111            s.move_heads_with(&mut |map, head, _| {
15112                (
15113                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15114                    SelectionGoal::None,
15115                )
15116            });
15117        })
15118    }
15119
15120    pub fn move_to_beginning(
15121        &mut self,
15122        _: &MoveToBeginning,
15123        window: &mut Window,
15124        cx: &mut Context<Self>,
15125    ) {
15126        if matches!(self.mode, EditorMode::SingleLine) {
15127            cx.propagate();
15128            return;
15129        }
15130        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15131        self.change_selections(Default::default(), window, cx, |s| {
15132            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15133        });
15134    }
15135
15136    pub fn select_to_beginning(
15137        &mut self,
15138        _: &SelectToBeginning,
15139        window: &mut Window,
15140        cx: &mut Context<Self>,
15141    ) {
15142        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15143        selection.set_head(Point::zero(), SelectionGoal::None);
15144        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15145        self.change_selections(Default::default(), window, cx, |s| {
15146            s.select(vec![selection]);
15147        });
15148    }
15149
15150    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15151        if matches!(self.mode, EditorMode::SingleLine) {
15152            cx.propagate();
15153            return;
15154        }
15155        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15156        let cursor = self.buffer.read(cx).read(cx).len();
15157        self.change_selections(Default::default(), window, cx, |s| {
15158            s.select_ranges(vec![cursor..cursor])
15159        });
15160    }
15161
15162    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15163        self.nav_history = nav_history;
15164    }
15165
15166    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15167        self.nav_history.as_ref()
15168    }
15169
15170    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15171        self.push_to_nav_history(
15172            self.selections.newest_anchor().head(),
15173            None,
15174            false,
15175            true,
15176            cx,
15177        );
15178    }
15179
15180    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15181        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15182        let buffer = self.buffer.read(cx).read(cx);
15183        let cursor_position = cursor_anchor.to_point(&buffer);
15184        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15185        let scroll_top_row = scroll_anchor.top_row(&buffer);
15186        drop(buffer);
15187
15188        NavigationData {
15189            cursor_anchor,
15190            cursor_position,
15191            scroll_anchor,
15192            scroll_top_row,
15193        }
15194    }
15195
15196    fn navigation_entry(
15197        &self,
15198        cursor_anchor: Anchor,
15199        cx: &mut Context<Self>,
15200    ) -> Option<NavigationEntry> {
15201        let Some(history) = self.nav_history.clone() else {
15202            return None;
15203        };
15204        let data = self.navigation_data(cursor_anchor, cx);
15205        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15206    }
15207
15208    fn push_to_nav_history(
15209        &mut self,
15210        cursor_anchor: Anchor,
15211        new_position: Option<Point>,
15212        is_deactivate: bool,
15213        always: bool,
15214        cx: &mut Context<Self>,
15215    ) {
15216        let data = self.navigation_data(cursor_anchor, cx);
15217        if let Some(nav_history) = self.nav_history.as_mut() {
15218            if let Some(new_position) = new_position {
15219                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15220                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15221                    return;
15222                }
15223            }
15224
15225            nav_history.push(Some(data), cx);
15226            cx.emit(EditorEvent::PushedToNavHistory {
15227                anchor: cursor_anchor,
15228                is_deactivate,
15229            })
15230        }
15231    }
15232
15233    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15234        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15235        let buffer = self.buffer.read(cx).snapshot(cx);
15236        let mut selection = self
15237            .selections
15238            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15239        selection.set_head(buffer.len(), SelectionGoal::None);
15240        self.change_selections(Default::default(), window, cx, |s| {
15241            s.select(vec![selection]);
15242        });
15243    }
15244
15245    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15246        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15247        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15248            s.select_ranges([Anchor::min()..Anchor::max()]);
15249        });
15250    }
15251
15252    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15253        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15254        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15255        let mut selections = self.selections.all::<Point>(&display_map);
15256        let max_point = display_map.buffer_snapshot().max_point();
15257        for selection in &mut selections {
15258            let rows = selection.spanned_rows(true, &display_map);
15259            selection.start = Point::new(rows.start.0, 0);
15260            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15261            selection.reversed = false;
15262        }
15263        self.change_selections(Default::default(), window, cx, |s| {
15264            s.select(selections);
15265        });
15266    }
15267
15268    pub fn split_selection_into_lines(
15269        &mut self,
15270        action: &SplitSelectionIntoLines,
15271        window: &mut Window,
15272        cx: &mut Context<Self>,
15273    ) {
15274        let selections = self
15275            .selections
15276            .all::<Point>(&self.display_snapshot(cx))
15277            .into_iter()
15278            .map(|selection| selection.start..selection.end)
15279            .collect::<Vec<_>>();
15280        self.unfold_ranges(&selections, true, false, cx);
15281
15282        let mut new_selection_ranges = Vec::new();
15283        {
15284            let buffer = self.buffer.read(cx).read(cx);
15285            for selection in selections {
15286                for row in selection.start.row..selection.end.row {
15287                    let line_start = Point::new(row, 0);
15288                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15289
15290                    if action.keep_selections {
15291                        // Keep the selection range for each line
15292                        let selection_start = if row == selection.start.row {
15293                            selection.start
15294                        } else {
15295                            line_start
15296                        };
15297                        new_selection_ranges.push(selection_start..line_end);
15298                    } else {
15299                        // Collapse to cursor at end of line
15300                        new_selection_ranges.push(line_end..line_end);
15301                    }
15302                }
15303
15304                let is_multiline_selection = selection.start.row != selection.end.row;
15305                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15306                // so this action feels more ergonomic when paired with other selection operations
15307                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15308                if !should_skip_last {
15309                    if action.keep_selections {
15310                        if is_multiline_selection {
15311                            let line_start = Point::new(selection.end.row, 0);
15312                            new_selection_ranges.push(line_start..selection.end);
15313                        } else {
15314                            new_selection_ranges.push(selection.start..selection.end);
15315                        }
15316                    } else {
15317                        new_selection_ranges.push(selection.end..selection.end);
15318                    }
15319                }
15320            }
15321        }
15322        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15323            s.select_ranges(new_selection_ranges);
15324        });
15325    }
15326
15327    pub fn add_selection_above(
15328        &mut self,
15329        action: &AddSelectionAbove,
15330        window: &mut Window,
15331        cx: &mut Context<Self>,
15332    ) {
15333        self.add_selection(true, action.skip_soft_wrap, window, cx);
15334    }
15335
15336    pub fn add_selection_below(
15337        &mut self,
15338        action: &AddSelectionBelow,
15339        window: &mut Window,
15340        cx: &mut Context<Self>,
15341    ) {
15342        self.add_selection(false, action.skip_soft_wrap, window, cx);
15343    }
15344
15345    fn add_selection(
15346        &mut self,
15347        above: bool,
15348        skip_soft_wrap: bool,
15349        window: &mut Window,
15350        cx: &mut Context<Self>,
15351    ) {
15352        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15353
15354        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15355        let all_selections = self.selections.all::<Point>(&display_map);
15356        let text_layout_details = self.text_layout_details(window, cx);
15357
15358        let (mut columnar_selections, new_selections_to_columnarize) = {
15359            if let Some(state) = self.add_selections_state.as_ref() {
15360                let columnar_selection_ids: HashSet<_> = state
15361                    .groups
15362                    .iter()
15363                    .flat_map(|group| group.stack.iter())
15364                    .copied()
15365                    .collect();
15366
15367                all_selections
15368                    .into_iter()
15369                    .partition(|s| columnar_selection_ids.contains(&s.id))
15370            } else {
15371                (Vec::new(), all_selections)
15372            }
15373        };
15374
15375        let mut state = self
15376            .add_selections_state
15377            .take()
15378            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15379
15380        for selection in new_selections_to_columnarize {
15381            let range = selection.display_range(&display_map).sorted();
15382            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15383            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15384            let positions = start_x.min(end_x)..start_x.max(end_x);
15385            let mut stack = Vec::new();
15386            for row in range.start.row().0..=range.end.row().0 {
15387                if let Some(selection) = self.selections.build_columnar_selection(
15388                    &display_map,
15389                    DisplayRow(row),
15390                    &positions,
15391                    selection.reversed,
15392                    &text_layout_details,
15393                ) {
15394                    stack.push(selection.id);
15395                    columnar_selections.push(selection);
15396                }
15397            }
15398            if !stack.is_empty() {
15399                if above {
15400                    stack.reverse();
15401                }
15402                state.groups.push(AddSelectionsGroup { above, stack });
15403            }
15404        }
15405
15406        let mut final_selections = Vec::new();
15407        let end_row = if above {
15408            DisplayRow(0)
15409        } else {
15410            display_map.max_point().row()
15411        };
15412
15413        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15414        // positions to place new selections, so we need to keep track of the
15415        // column range of the oldest selection in each group, because
15416        // intermediate selections may have been clamped to shorter lines.
15417        // selections may have been clamped to shorter lines.
15418        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15419            let mut map = HashMap::default();
15420            for group in state.groups.iter() {
15421                if let Some(oldest_id) = group.stack.first() {
15422                    if let Some(oldest_selection) =
15423                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15424                    {
15425                        let start_col = oldest_selection.start.column;
15426                        let end_col = oldest_selection.end.column;
15427                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15428                        for id in &group.stack {
15429                            map.insert(*id, goal_columns.clone());
15430                        }
15431                    }
15432                }
15433            }
15434            map
15435        } else {
15436            HashMap::default()
15437        };
15438
15439        let mut last_added_item_per_group = HashMap::default();
15440        for group in state.groups.iter_mut() {
15441            if let Some(last_id) = group.stack.last() {
15442                last_added_item_per_group.insert(*last_id, group);
15443            }
15444        }
15445
15446        for selection in columnar_selections {
15447            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15448                if above == group.above {
15449                    let range = selection.display_range(&display_map).sorted();
15450                    debug_assert_eq!(range.start.row(), range.end.row());
15451                    let row = range.start.row();
15452                    let positions =
15453                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15454                            Pixels::from(start)..Pixels::from(end)
15455                        } else {
15456                            let start_x =
15457                                display_map.x_for_display_point(range.start, &text_layout_details);
15458                            let end_x =
15459                                display_map.x_for_display_point(range.end, &text_layout_details);
15460                            start_x.min(end_x)..start_x.max(end_x)
15461                        };
15462
15463                    let maybe_new_selection = if skip_soft_wrap {
15464                        let goal_columns = goal_columns_by_selection_id
15465                            .remove(&selection.id)
15466                            .unwrap_or_else(|| {
15467                                let start_col = selection.start.column;
15468                                let end_col = selection.end.column;
15469                                start_col.min(end_col)..start_col.max(end_col)
15470                            });
15471                        self.selections.find_next_columnar_selection_by_buffer_row(
15472                            &display_map,
15473                            row,
15474                            end_row,
15475                            above,
15476                            &goal_columns,
15477                            selection.reversed,
15478                            &text_layout_details,
15479                        )
15480                    } else {
15481                        self.selections.find_next_columnar_selection_by_display_row(
15482                            &display_map,
15483                            row,
15484                            end_row,
15485                            above,
15486                            &positions,
15487                            selection.reversed,
15488                            &text_layout_details,
15489                        )
15490                    };
15491
15492                    if let Some(new_selection) = maybe_new_selection {
15493                        group.stack.push(new_selection.id);
15494                        if above {
15495                            final_selections.push(new_selection);
15496                            final_selections.push(selection);
15497                        } else {
15498                            final_selections.push(selection);
15499                            final_selections.push(new_selection);
15500                        }
15501                    } else {
15502                        final_selections.push(selection);
15503                    }
15504                } else {
15505                    group.stack.pop();
15506                }
15507            } else {
15508                final_selections.push(selection);
15509            }
15510        }
15511
15512        self.change_selections(Default::default(), window, cx, |s| {
15513            s.select(final_selections);
15514        });
15515
15516        let final_selection_ids: HashSet<_> = self
15517            .selections
15518            .all::<Point>(&display_map)
15519            .iter()
15520            .map(|s| s.id)
15521            .collect();
15522        state.groups.retain_mut(|group| {
15523            // selections might get merged above so we remove invalid items from stacks
15524            group.stack.retain(|id| final_selection_ids.contains(id));
15525
15526            // single selection in stack can be treated as initial state
15527            group.stack.len() > 1
15528        });
15529
15530        if !state.groups.is_empty() {
15531            self.add_selections_state = Some(state);
15532        }
15533    }
15534
15535    pub fn insert_snippet_at_selections(
15536        &mut self,
15537        action: &InsertSnippet,
15538        window: &mut Window,
15539        cx: &mut Context<Self>,
15540    ) {
15541        self.try_insert_snippet_at_selections(action, window, cx)
15542            .log_err();
15543    }
15544
15545    fn try_insert_snippet_at_selections(
15546        &mut self,
15547        action: &InsertSnippet,
15548        window: &mut Window,
15549        cx: &mut Context<Self>,
15550    ) -> Result<()> {
15551        let insertion_ranges = self
15552            .selections
15553            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15554            .into_iter()
15555            .map(|selection| selection.range())
15556            .collect_vec();
15557
15558        let snippet = if let Some(snippet_body) = &action.snippet {
15559            if action.language.is_none() && action.name.is_none() {
15560                Snippet::parse(snippet_body)?
15561            } else {
15562                bail!("`snippet` is mutually exclusive with `language` and `name`")
15563            }
15564        } else if let Some(name) = &action.name {
15565            let project = self.project().context("no project")?;
15566            let snippet_store = project.read(cx).snippets().read(cx);
15567            let snippet = snippet_store
15568                .snippets_for(action.language.clone(), cx)
15569                .into_iter()
15570                .find(|snippet| snippet.name == *name)
15571                .context("snippet not found")?;
15572            Snippet::parse(&snippet.body)?
15573        } else {
15574            // todo(andrew): open modal to select snippet
15575            bail!("`name` or `snippet` is required")
15576        };
15577
15578        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15579    }
15580
15581    fn select_match_ranges(
15582        &mut self,
15583        range: Range<MultiBufferOffset>,
15584        reversed: bool,
15585        replace_newest: bool,
15586        auto_scroll: Option<Autoscroll>,
15587        window: &mut Window,
15588        cx: &mut Context<Editor>,
15589    ) {
15590        self.unfold_ranges(
15591            std::slice::from_ref(&range),
15592            false,
15593            auto_scroll.is_some(),
15594            cx,
15595        );
15596        let effects = if let Some(scroll) = auto_scroll {
15597            SelectionEffects::scroll(scroll)
15598        } else {
15599            SelectionEffects::no_scroll()
15600        };
15601        self.change_selections(effects, window, cx, |s| {
15602            if replace_newest {
15603                s.delete(s.newest_anchor().id);
15604            }
15605            if reversed {
15606                s.insert_range(range.end..range.start);
15607            } else {
15608                s.insert_range(range);
15609            }
15610        });
15611    }
15612
15613    pub fn select_next_match_internal(
15614        &mut self,
15615        display_map: &DisplaySnapshot,
15616        replace_newest: bool,
15617        autoscroll: Option<Autoscroll>,
15618        window: &mut Window,
15619        cx: &mut Context<Self>,
15620    ) -> Result<()> {
15621        let buffer = display_map.buffer_snapshot();
15622        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15623        if let Some(mut select_next_state) = self.select_next_state.take() {
15624            let query = &select_next_state.query;
15625            if !select_next_state.done {
15626                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15627                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15628                let mut next_selected_range = None;
15629
15630                let bytes_after_last_selection =
15631                    buffer.bytes_in_range(last_selection.end..buffer.len());
15632                let bytes_before_first_selection =
15633                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15634                let query_matches = query
15635                    .stream_find_iter(bytes_after_last_selection)
15636                    .map(|result| (last_selection.end, result))
15637                    .chain(
15638                        query
15639                            .stream_find_iter(bytes_before_first_selection)
15640                            .map(|result| (MultiBufferOffset(0), result)),
15641                    );
15642
15643                for (start_offset, query_match) in query_matches {
15644                    let query_match = query_match.unwrap(); // can only fail due to I/O
15645                    let offset_range =
15646                        start_offset + query_match.start()..start_offset + query_match.end();
15647
15648                    if !select_next_state.wordwise
15649                        || (!buffer.is_inside_word(offset_range.start, None)
15650                            && !buffer.is_inside_word(offset_range.end, None))
15651                    {
15652                        let idx = selections
15653                            .partition_point(|selection| selection.end <= offset_range.start);
15654                        let overlaps = selections
15655                            .get(idx)
15656                            .map_or(false, |selection| selection.start < offset_range.end);
15657
15658                        if !overlaps {
15659                            next_selected_range = Some(offset_range);
15660                            break;
15661                        }
15662                    }
15663                }
15664
15665                if let Some(next_selected_range) = next_selected_range {
15666                    self.select_match_ranges(
15667                        next_selected_range,
15668                        last_selection.reversed,
15669                        replace_newest,
15670                        autoscroll,
15671                        window,
15672                        cx,
15673                    );
15674                } else {
15675                    select_next_state.done = true;
15676                }
15677            }
15678
15679            self.select_next_state = Some(select_next_state);
15680        } else {
15681            let mut only_carets = true;
15682            let mut same_text_selected = true;
15683            let mut selected_text = None;
15684
15685            let mut selections_iter = selections.iter().peekable();
15686            while let Some(selection) = selections_iter.next() {
15687                if selection.start != selection.end {
15688                    only_carets = false;
15689                }
15690
15691                if same_text_selected {
15692                    if selected_text.is_none() {
15693                        selected_text =
15694                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15695                    }
15696
15697                    if let Some(next_selection) = selections_iter.peek() {
15698                        if next_selection.len() == selection.len() {
15699                            let next_selected_text = buffer
15700                                .text_for_range(next_selection.range())
15701                                .collect::<String>();
15702                            if Some(next_selected_text) != selected_text {
15703                                same_text_selected = false;
15704                                selected_text = None;
15705                            }
15706                        } else {
15707                            same_text_selected = false;
15708                            selected_text = None;
15709                        }
15710                    }
15711                }
15712            }
15713
15714            if only_carets {
15715                for selection in &mut selections {
15716                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15717                    selection.start = word_range.start;
15718                    selection.end = word_range.end;
15719                    selection.goal = SelectionGoal::None;
15720                    selection.reversed = false;
15721                    self.select_match_ranges(
15722                        selection.start..selection.end,
15723                        selection.reversed,
15724                        replace_newest,
15725                        autoscroll,
15726                        window,
15727                        cx,
15728                    );
15729                }
15730
15731                if selections.len() == 1 {
15732                    let selection = selections
15733                        .last()
15734                        .expect("ensured that there's only one selection");
15735                    let query = buffer
15736                        .text_for_range(selection.start..selection.end)
15737                        .collect::<String>();
15738                    let is_empty = query.is_empty();
15739                    let select_state = SelectNextState {
15740                        query: self.build_query(&[query], cx)?,
15741                        wordwise: true,
15742                        done: is_empty,
15743                    };
15744                    self.select_next_state = Some(select_state);
15745                } else {
15746                    self.select_next_state = None;
15747                }
15748            } else if let Some(selected_text) = selected_text {
15749                self.select_next_state = Some(SelectNextState {
15750                    query: self.build_query(&[selected_text], cx)?,
15751                    wordwise: false,
15752                    done: false,
15753                });
15754                self.select_next_match_internal(
15755                    display_map,
15756                    replace_newest,
15757                    autoscroll,
15758                    window,
15759                    cx,
15760                )?;
15761            }
15762        }
15763        Ok(())
15764    }
15765
15766    pub fn select_all_matches(
15767        &mut self,
15768        _action: &SelectAllMatches,
15769        window: &mut Window,
15770        cx: &mut Context<Self>,
15771    ) -> Result<()> {
15772        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15773
15774        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15775
15776        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15777        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15778        else {
15779            return Ok(());
15780        };
15781
15782        let mut new_selections = Vec::new();
15783
15784        let reversed = self
15785            .selections
15786            .oldest::<MultiBufferOffset>(&display_map)
15787            .reversed;
15788        let buffer = display_map.buffer_snapshot();
15789        let query_matches = select_next_state
15790            .query
15791            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15792
15793        for query_match in query_matches.into_iter() {
15794            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15795            let offset_range = if reversed {
15796                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15797            } else {
15798                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15799            };
15800
15801            if !select_next_state.wordwise
15802                || (!buffer.is_inside_word(offset_range.start, None)
15803                    && !buffer.is_inside_word(offset_range.end, None))
15804            {
15805                new_selections.push(offset_range.start..offset_range.end);
15806            }
15807        }
15808
15809        select_next_state.done = true;
15810
15811        if new_selections.is_empty() {
15812            log::error!("bug: new_selections is empty in select_all_matches");
15813            return Ok(());
15814        }
15815
15816        self.unfold_ranges(&new_selections, false, false, cx);
15817        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15818            selections.select_ranges(new_selections)
15819        });
15820
15821        Ok(())
15822    }
15823
15824    pub fn select_next(
15825        &mut self,
15826        action: &SelectNext,
15827        window: &mut Window,
15828        cx: &mut Context<Self>,
15829    ) -> Result<()> {
15830        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15831        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15832        self.select_next_match_internal(
15833            &display_map,
15834            action.replace_newest,
15835            Some(Autoscroll::newest()),
15836            window,
15837            cx,
15838        )
15839    }
15840
15841    pub fn select_previous(
15842        &mut self,
15843        action: &SelectPrevious,
15844        window: &mut Window,
15845        cx: &mut Context<Self>,
15846    ) -> Result<()> {
15847        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15848        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15849        let buffer = display_map.buffer_snapshot();
15850        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15851        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15852            let query = &select_prev_state.query;
15853            if !select_prev_state.done {
15854                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15855                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15856                let mut next_selected_range = None;
15857                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15858                let bytes_before_last_selection =
15859                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15860                let bytes_after_first_selection =
15861                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15862                let query_matches = query
15863                    .stream_find_iter(bytes_before_last_selection)
15864                    .map(|result| (last_selection.start, result))
15865                    .chain(
15866                        query
15867                            .stream_find_iter(bytes_after_first_selection)
15868                            .map(|result| (buffer.len(), result)),
15869                    );
15870                for (end_offset, query_match) in query_matches {
15871                    let query_match = query_match.unwrap(); // can only fail due to I/O
15872                    let offset_range =
15873                        end_offset - query_match.end()..end_offset - query_match.start();
15874
15875                    if !select_prev_state.wordwise
15876                        || (!buffer.is_inside_word(offset_range.start, None)
15877                            && !buffer.is_inside_word(offset_range.end, None))
15878                    {
15879                        next_selected_range = Some(offset_range);
15880                        break;
15881                    }
15882                }
15883
15884                if let Some(next_selected_range) = next_selected_range {
15885                    self.select_match_ranges(
15886                        next_selected_range,
15887                        last_selection.reversed,
15888                        action.replace_newest,
15889                        Some(Autoscroll::newest()),
15890                        window,
15891                        cx,
15892                    );
15893                } else {
15894                    select_prev_state.done = true;
15895                }
15896            }
15897
15898            self.select_prev_state = Some(select_prev_state);
15899        } else {
15900            let mut only_carets = true;
15901            let mut same_text_selected = true;
15902            let mut selected_text = None;
15903
15904            let mut selections_iter = selections.iter().peekable();
15905            while let Some(selection) = selections_iter.next() {
15906                if selection.start != selection.end {
15907                    only_carets = false;
15908                }
15909
15910                if same_text_selected {
15911                    if selected_text.is_none() {
15912                        selected_text =
15913                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15914                    }
15915
15916                    if let Some(next_selection) = selections_iter.peek() {
15917                        if next_selection.len() == selection.len() {
15918                            let next_selected_text = buffer
15919                                .text_for_range(next_selection.range())
15920                                .collect::<String>();
15921                            if Some(next_selected_text) != selected_text {
15922                                same_text_selected = false;
15923                                selected_text = None;
15924                            }
15925                        } else {
15926                            same_text_selected = false;
15927                            selected_text = None;
15928                        }
15929                    }
15930                }
15931            }
15932
15933            if only_carets {
15934                for selection in &mut selections {
15935                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15936                    selection.start = word_range.start;
15937                    selection.end = word_range.end;
15938                    selection.goal = SelectionGoal::None;
15939                    selection.reversed = false;
15940                    self.select_match_ranges(
15941                        selection.start..selection.end,
15942                        selection.reversed,
15943                        action.replace_newest,
15944                        Some(Autoscroll::newest()),
15945                        window,
15946                        cx,
15947                    );
15948                }
15949                if selections.len() == 1 {
15950                    let selection = selections
15951                        .last()
15952                        .expect("ensured that there's only one selection");
15953                    let query = buffer
15954                        .text_for_range(selection.start..selection.end)
15955                        .collect::<String>();
15956                    let is_empty = query.is_empty();
15957                    let select_state = SelectNextState {
15958                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
15959                        wordwise: true,
15960                        done: is_empty,
15961                    };
15962                    self.select_prev_state = Some(select_state);
15963                } else {
15964                    self.select_prev_state = None;
15965                }
15966            } else if let Some(selected_text) = selected_text {
15967                self.select_prev_state = Some(SelectNextState {
15968                    query: self
15969                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
15970                    wordwise: false,
15971                    done: false,
15972                });
15973                self.select_previous(action, window, cx)?;
15974            }
15975        }
15976        Ok(())
15977    }
15978
15979    /// Builds an `AhoCorasick` automaton from the provided patterns, while
15980    /// setting the case sensitivity based on the global
15981    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
15982    /// editor's settings.
15983    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
15984    where
15985        I: IntoIterator<Item = P>,
15986        P: AsRef<[u8]>,
15987    {
15988        let case_sensitive = self
15989            .select_next_is_case_sensitive
15990            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
15991
15992        let mut builder = AhoCorasickBuilder::new();
15993        builder.ascii_case_insensitive(!case_sensitive);
15994        builder.build(patterns)
15995    }
15996
15997    pub fn find_next_match(
15998        &mut self,
15999        _: &FindNextMatch,
16000        window: &mut Window,
16001        cx: &mut Context<Self>,
16002    ) -> Result<()> {
16003        let selections = self.selections.disjoint_anchors_arc();
16004        match selections.first() {
16005            Some(first) if selections.len() >= 2 => {
16006                self.change_selections(Default::default(), window, cx, |s| {
16007                    s.select_ranges([first.range()]);
16008                });
16009            }
16010            _ => self.select_next(
16011                &SelectNext {
16012                    replace_newest: true,
16013                },
16014                window,
16015                cx,
16016            )?,
16017        }
16018        Ok(())
16019    }
16020
16021    pub fn find_previous_match(
16022        &mut self,
16023        _: &FindPreviousMatch,
16024        window: &mut Window,
16025        cx: &mut Context<Self>,
16026    ) -> Result<()> {
16027        let selections = self.selections.disjoint_anchors_arc();
16028        match selections.last() {
16029            Some(last) if selections.len() >= 2 => {
16030                self.change_selections(Default::default(), window, cx, |s| {
16031                    s.select_ranges([last.range()]);
16032                });
16033            }
16034            _ => self.select_previous(
16035                &SelectPrevious {
16036                    replace_newest: true,
16037                },
16038                window,
16039                cx,
16040            )?,
16041        }
16042        Ok(())
16043    }
16044
16045    pub fn toggle_comments(
16046        &mut self,
16047        action: &ToggleComments,
16048        window: &mut Window,
16049        cx: &mut Context<Self>,
16050    ) {
16051        if self.read_only(cx) {
16052            return;
16053        }
16054        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16055        let text_layout_details = &self.text_layout_details(window, cx);
16056        self.transact(window, cx, |this, window, cx| {
16057            let mut selections = this
16058                .selections
16059                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16060            let mut edits = Vec::new();
16061            let mut selection_edit_ranges = Vec::new();
16062            let mut last_toggled_row = None;
16063            let snapshot = this.buffer.read(cx).read(cx);
16064            let empty_str: Arc<str> = Arc::default();
16065            let mut suffixes_inserted = Vec::new();
16066            let ignore_indent = action.ignore_indent;
16067
16068            fn comment_prefix_range(
16069                snapshot: &MultiBufferSnapshot,
16070                row: MultiBufferRow,
16071                comment_prefix: &str,
16072                comment_prefix_whitespace: &str,
16073                ignore_indent: bool,
16074            ) -> Range<Point> {
16075                let indent_size = if ignore_indent {
16076                    0
16077                } else {
16078                    snapshot.indent_size_for_line(row).len
16079                };
16080
16081                let start = Point::new(row.0, indent_size);
16082
16083                let mut line_bytes = snapshot
16084                    .bytes_in_range(start..snapshot.max_point())
16085                    .flatten()
16086                    .copied();
16087
16088                // If this line currently begins with the line comment prefix, then record
16089                // the range containing the prefix.
16090                if line_bytes
16091                    .by_ref()
16092                    .take(comment_prefix.len())
16093                    .eq(comment_prefix.bytes())
16094                {
16095                    // Include any whitespace that matches the comment prefix.
16096                    let matching_whitespace_len = line_bytes
16097                        .zip(comment_prefix_whitespace.bytes())
16098                        .take_while(|(a, b)| a == b)
16099                        .count() as u32;
16100                    let end = Point::new(
16101                        start.row,
16102                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16103                    );
16104                    start..end
16105                } else {
16106                    start..start
16107                }
16108            }
16109
16110            fn comment_suffix_range(
16111                snapshot: &MultiBufferSnapshot,
16112                row: MultiBufferRow,
16113                comment_suffix: &str,
16114                comment_suffix_has_leading_space: bool,
16115            ) -> Range<Point> {
16116                let end = Point::new(row.0, snapshot.line_len(row));
16117                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16118
16119                let mut line_end_bytes = snapshot
16120                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16121                    .flatten()
16122                    .copied();
16123
16124                let leading_space_len = if suffix_start_column > 0
16125                    && line_end_bytes.next() == Some(b' ')
16126                    && comment_suffix_has_leading_space
16127                {
16128                    1
16129                } else {
16130                    0
16131                };
16132
16133                // If this line currently begins with the line comment prefix, then record
16134                // the range containing the prefix.
16135                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16136                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16137                    start..end
16138                } else {
16139                    end..end
16140                }
16141            }
16142
16143            // TODO: Handle selections that cross excerpts
16144            for selection in &mut selections {
16145                let start_column = snapshot
16146                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16147                    .len;
16148                let language = if let Some(language) =
16149                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16150                {
16151                    language
16152                } else {
16153                    continue;
16154                };
16155
16156                selection_edit_ranges.clear();
16157
16158                // If multiple selections contain a given row, avoid processing that
16159                // row more than once.
16160                let mut start_row = MultiBufferRow(selection.start.row);
16161                if last_toggled_row == Some(start_row) {
16162                    start_row = start_row.next_row();
16163                }
16164                let end_row =
16165                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16166                        MultiBufferRow(selection.end.row - 1)
16167                    } else {
16168                        MultiBufferRow(selection.end.row)
16169                    };
16170                last_toggled_row = Some(end_row);
16171
16172                if start_row > end_row {
16173                    continue;
16174                }
16175
16176                // If the language has line comments, toggle those.
16177                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16178
16179                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16180                if ignore_indent {
16181                    full_comment_prefixes = full_comment_prefixes
16182                        .into_iter()
16183                        .map(|s| Arc::from(s.trim_end()))
16184                        .collect();
16185                }
16186
16187                if !full_comment_prefixes.is_empty() {
16188                    let first_prefix = full_comment_prefixes
16189                        .first()
16190                        .expect("prefixes is non-empty");
16191                    let prefix_trimmed_lengths = full_comment_prefixes
16192                        .iter()
16193                        .map(|p| p.trim_end_matches(' ').len())
16194                        .collect::<SmallVec<[usize; 4]>>();
16195
16196                    let mut all_selection_lines_are_comments = true;
16197
16198                    for row in start_row.0..=end_row.0 {
16199                        let row = MultiBufferRow(row);
16200                        if start_row < end_row && snapshot.is_line_blank(row) {
16201                            continue;
16202                        }
16203
16204                        let prefix_range = full_comment_prefixes
16205                            .iter()
16206                            .zip(prefix_trimmed_lengths.iter().copied())
16207                            .map(|(prefix, trimmed_prefix_len)| {
16208                                comment_prefix_range(
16209                                    snapshot.deref(),
16210                                    row,
16211                                    &prefix[..trimmed_prefix_len],
16212                                    &prefix[trimmed_prefix_len..],
16213                                    ignore_indent,
16214                                )
16215                            })
16216                            .max_by_key(|range| range.end.column - range.start.column)
16217                            .expect("prefixes is non-empty");
16218
16219                        if prefix_range.is_empty() {
16220                            all_selection_lines_are_comments = false;
16221                        }
16222
16223                        selection_edit_ranges.push(prefix_range);
16224                    }
16225
16226                    if all_selection_lines_are_comments {
16227                        edits.extend(
16228                            selection_edit_ranges
16229                                .iter()
16230                                .cloned()
16231                                .map(|range| (range, empty_str.clone())),
16232                        );
16233                    } else {
16234                        let min_column = selection_edit_ranges
16235                            .iter()
16236                            .map(|range| range.start.column)
16237                            .min()
16238                            .unwrap_or(0);
16239                        edits.extend(selection_edit_ranges.iter().map(|range| {
16240                            let position = Point::new(range.start.row, min_column);
16241                            (position..position, first_prefix.clone())
16242                        }));
16243                    }
16244                } else if let Some(BlockCommentConfig {
16245                    start: full_comment_prefix,
16246                    end: comment_suffix,
16247                    ..
16248                }) = language.block_comment()
16249                {
16250                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16251                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16252                    let prefix_range = comment_prefix_range(
16253                        snapshot.deref(),
16254                        start_row,
16255                        comment_prefix,
16256                        comment_prefix_whitespace,
16257                        ignore_indent,
16258                    );
16259                    let suffix_range = comment_suffix_range(
16260                        snapshot.deref(),
16261                        end_row,
16262                        comment_suffix.trim_start_matches(' '),
16263                        comment_suffix.starts_with(' '),
16264                    );
16265
16266                    if prefix_range.is_empty() || suffix_range.is_empty() {
16267                        edits.push((
16268                            prefix_range.start..prefix_range.start,
16269                            full_comment_prefix.clone(),
16270                        ));
16271                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16272                        suffixes_inserted.push((end_row, comment_suffix.len()));
16273                    } else {
16274                        edits.push((prefix_range, empty_str.clone()));
16275                        edits.push((suffix_range, empty_str.clone()));
16276                    }
16277                } else {
16278                    continue;
16279                }
16280            }
16281
16282            drop(snapshot);
16283            this.buffer.update(cx, |buffer, cx| {
16284                buffer.edit(edits, None, cx);
16285            });
16286
16287            // Adjust selections so that they end before any comment suffixes that
16288            // were inserted.
16289            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16290            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16291            let snapshot = this.buffer.read(cx).read(cx);
16292            for selection in &mut selections {
16293                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16294                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16295                        Ordering::Less => {
16296                            suffixes_inserted.next();
16297                            continue;
16298                        }
16299                        Ordering::Greater => break,
16300                        Ordering::Equal => {
16301                            if selection.end.column == snapshot.line_len(row) {
16302                                if selection.is_empty() {
16303                                    selection.start.column -= suffix_len as u32;
16304                                }
16305                                selection.end.column -= suffix_len as u32;
16306                            }
16307                            break;
16308                        }
16309                    }
16310                }
16311            }
16312
16313            drop(snapshot);
16314            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16315
16316            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16317            let selections_on_single_row = selections.windows(2).all(|selections| {
16318                selections[0].start.row == selections[1].start.row
16319                    && selections[0].end.row == selections[1].end.row
16320                    && selections[0].start.row == selections[0].end.row
16321            });
16322            let selections_selecting = selections
16323                .iter()
16324                .any(|selection| selection.start != selection.end);
16325            let advance_downwards = action.advance_downwards
16326                && selections_on_single_row
16327                && !selections_selecting
16328                && !matches!(this.mode, EditorMode::SingleLine);
16329
16330            if advance_downwards {
16331                let snapshot = this.buffer.read(cx).snapshot(cx);
16332
16333                this.change_selections(Default::default(), window, cx, |s| {
16334                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16335                        let mut point = display_point.to_point(display_snapshot);
16336                        point.row += 1;
16337                        point = snapshot.clip_point(point, Bias::Left);
16338                        let display_point = point.to_display_point(display_snapshot);
16339                        let goal = SelectionGoal::HorizontalPosition(
16340                            display_snapshot
16341                                .x_for_display_point(display_point, text_layout_details)
16342                                .into(),
16343                        );
16344                        (display_point, goal)
16345                    })
16346                });
16347            }
16348        });
16349    }
16350
16351    pub fn select_enclosing_symbol(
16352        &mut self,
16353        _: &SelectEnclosingSymbol,
16354        window: &mut Window,
16355        cx: &mut Context<Self>,
16356    ) {
16357        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16358
16359        let buffer = self.buffer.read(cx).snapshot(cx);
16360        let old_selections = self
16361            .selections
16362            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16363            .into_boxed_slice();
16364
16365        fn update_selection(
16366            selection: &Selection<MultiBufferOffset>,
16367            buffer_snap: &MultiBufferSnapshot,
16368        ) -> Option<Selection<MultiBufferOffset>> {
16369            let cursor = selection.head();
16370            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16371            for symbol in symbols.iter().rev() {
16372                let start = symbol.range.start.to_offset(buffer_snap);
16373                let end = symbol.range.end.to_offset(buffer_snap);
16374                let new_range = start..end;
16375                if start < selection.start || end > selection.end {
16376                    return Some(Selection {
16377                        id: selection.id,
16378                        start: new_range.start,
16379                        end: new_range.end,
16380                        goal: SelectionGoal::None,
16381                        reversed: selection.reversed,
16382                    });
16383                }
16384            }
16385            None
16386        }
16387
16388        let mut selected_larger_symbol = false;
16389        let new_selections = old_selections
16390            .iter()
16391            .map(|selection| match update_selection(selection, &buffer) {
16392                Some(new_selection) => {
16393                    if new_selection.range() != selection.range() {
16394                        selected_larger_symbol = true;
16395                    }
16396                    new_selection
16397                }
16398                None => selection.clone(),
16399            })
16400            .collect::<Vec<_>>();
16401
16402        if selected_larger_symbol {
16403            self.change_selections(Default::default(), window, cx, |s| {
16404                s.select(new_selections);
16405            });
16406        }
16407    }
16408
16409    pub fn select_larger_syntax_node(
16410        &mut self,
16411        _: &SelectLargerSyntaxNode,
16412        window: &mut Window,
16413        cx: &mut Context<Self>,
16414    ) {
16415        let Some(visible_row_count) = self.visible_row_count() else {
16416            return;
16417        };
16418        let old_selections: Box<[_]> = self
16419            .selections
16420            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16421            .into();
16422        if old_selections.is_empty() {
16423            return;
16424        }
16425
16426        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16427
16428        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16429        let buffer = self.buffer.read(cx).snapshot(cx);
16430
16431        let mut selected_larger_node = false;
16432        let mut new_selections = old_selections
16433            .iter()
16434            .map(|selection| {
16435                let old_range = selection.start..selection.end;
16436
16437                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16438                    // manually select word at selection
16439                    if ["string_content", "inline"].contains(&node.kind()) {
16440                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16441                        // ignore if word is already selected
16442                        if !word_range.is_empty() && old_range != word_range {
16443                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16444                            // only select word if start and end point belongs to same word
16445                            if word_range == last_word_range {
16446                                selected_larger_node = true;
16447                                return Selection {
16448                                    id: selection.id,
16449                                    start: word_range.start,
16450                                    end: word_range.end,
16451                                    goal: SelectionGoal::None,
16452                                    reversed: selection.reversed,
16453                                };
16454                            }
16455                        }
16456                    }
16457                }
16458
16459                let mut new_range = old_range.clone();
16460                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16461                    new_range = range;
16462                    if !node.is_named() {
16463                        continue;
16464                    }
16465                    if !display_map.intersects_fold(new_range.start)
16466                        && !display_map.intersects_fold(new_range.end)
16467                    {
16468                        break;
16469                    }
16470                }
16471
16472                selected_larger_node |= new_range != old_range;
16473                Selection {
16474                    id: selection.id,
16475                    start: new_range.start,
16476                    end: new_range.end,
16477                    goal: SelectionGoal::None,
16478                    reversed: selection.reversed,
16479                }
16480            })
16481            .collect::<Vec<_>>();
16482
16483        if !selected_larger_node {
16484            return; // don't put this call in the history
16485        }
16486
16487        // scroll based on transformation done to the last selection created by the user
16488        let (last_old, last_new) = old_selections
16489            .last()
16490            .zip(new_selections.last().cloned())
16491            .expect("old_selections isn't empty");
16492
16493        // revert selection
16494        let is_selection_reversed = {
16495            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16496            new_selections.last_mut().expect("checked above").reversed =
16497                should_newest_selection_be_reversed;
16498            should_newest_selection_be_reversed
16499        };
16500
16501        if selected_larger_node {
16502            self.select_syntax_node_history.disable_clearing = true;
16503            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16504                s.select(new_selections.clone());
16505            });
16506            self.select_syntax_node_history.disable_clearing = false;
16507        }
16508
16509        let start_row = last_new.start.to_display_point(&display_map).row().0;
16510        let end_row = last_new.end.to_display_point(&display_map).row().0;
16511        let selection_height = end_row - start_row + 1;
16512        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16513
16514        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16515        let scroll_behavior = if fits_on_the_screen {
16516            self.request_autoscroll(Autoscroll::fit(), cx);
16517            SelectSyntaxNodeScrollBehavior::FitSelection
16518        } else if is_selection_reversed {
16519            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16520            SelectSyntaxNodeScrollBehavior::CursorTop
16521        } else {
16522            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16523            SelectSyntaxNodeScrollBehavior::CursorBottom
16524        };
16525
16526        let old_selections: Box<[Selection<Anchor>]> = old_selections
16527            .iter()
16528            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16529            .collect();
16530        self.select_syntax_node_history.push((
16531            old_selections,
16532            scroll_behavior,
16533            is_selection_reversed,
16534        ));
16535    }
16536
16537    pub fn select_smaller_syntax_node(
16538        &mut self,
16539        _: &SelectSmallerSyntaxNode,
16540        window: &mut Window,
16541        cx: &mut Context<Self>,
16542    ) {
16543        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16544
16545        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16546            self.select_syntax_node_history.pop()
16547        {
16548            if let Some(selection) = selections.last_mut() {
16549                selection.reversed = is_selection_reversed;
16550            }
16551
16552            let snapshot = self.buffer.read(cx).snapshot(cx);
16553            let selections: Vec<Selection<MultiBufferOffset>> = selections
16554                .iter()
16555                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16556                .collect();
16557
16558            self.select_syntax_node_history.disable_clearing = true;
16559            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16560                s.select(selections);
16561            });
16562            self.select_syntax_node_history.disable_clearing = false;
16563
16564            match scroll_behavior {
16565                SelectSyntaxNodeScrollBehavior::CursorTop => {
16566                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16567                }
16568                SelectSyntaxNodeScrollBehavior::FitSelection => {
16569                    self.request_autoscroll(Autoscroll::fit(), cx);
16570                }
16571                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16572                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16573                }
16574            }
16575        }
16576    }
16577
16578    pub fn unwrap_syntax_node(
16579        &mut self,
16580        _: &UnwrapSyntaxNode,
16581        window: &mut Window,
16582        cx: &mut Context<Self>,
16583    ) {
16584        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16585
16586        let buffer = self.buffer.read(cx).snapshot(cx);
16587        let selections = self
16588            .selections
16589            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16590            .into_iter()
16591            // subtracting the offset requires sorting
16592            .sorted_by_key(|i| i.start);
16593
16594        let full_edits = selections
16595            .into_iter()
16596            .filter_map(|selection| {
16597                let child = if selection.is_empty()
16598                    && let Some((_, ancestor_range)) =
16599                        buffer.syntax_ancestor(selection.start..selection.end)
16600                {
16601                    ancestor_range
16602                } else {
16603                    selection.range()
16604                };
16605
16606                let mut parent = child.clone();
16607                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16608                    parent = ancestor_range;
16609                    if parent.start < child.start || parent.end > child.end {
16610                        break;
16611                    }
16612                }
16613
16614                if parent == child {
16615                    return None;
16616                }
16617                let text = buffer.text_for_range(child).collect::<String>();
16618                Some((selection.id, parent, text))
16619            })
16620            .collect::<Vec<_>>();
16621        if full_edits.is_empty() {
16622            return;
16623        }
16624
16625        self.transact(window, cx, |this, window, cx| {
16626            this.buffer.update(cx, |buffer, cx| {
16627                buffer.edit(
16628                    full_edits
16629                        .iter()
16630                        .map(|(_, p, t)| (p.clone(), t.clone()))
16631                        .collect::<Vec<_>>(),
16632                    None,
16633                    cx,
16634                );
16635            });
16636            this.change_selections(Default::default(), window, cx, |s| {
16637                let mut offset = 0;
16638                let mut selections = vec![];
16639                for (id, parent, text) in full_edits {
16640                    let start = parent.start - offset;
16641                    offset += (parent.end - parent.start) - text.len();
16642                    selections.push(Selection {
16643                        id,
16644                        start,
16645                        end: start + text.len(),
16646                        reversed: false,
16647                        goal: Default::default(),
16648                    });
16649                }
16650                s.select(selections);
16651            });
16652        });
16653    }
16654
16655    pub fn select_next_syntax_node(
16656        &mut self,
16657        _: &SelectNextSyntaxNode,
16658        window: &mut Window,
16659        cx: &mut Context<Self>,
16660    ) {
16661        let old_selections: Box<[_]> = self
16662            .selections
16663            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16664            .into();
16665        if old_selections.is_empty() {
16666            return;
16667        }
16668
16669        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16670
16671        let buffer = self.buffer.read(cx).snapshot(cx);
16672        let mut selected_sibling = false;
16673
16674        let new_selections = old_selections
16675            .iter()
16676            .map(|selection| {
16677                let old_range = selection.start..selection.end;
16678
16679                let old_range =
16680                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16681                let excerpt = buffer.excerpt_containing(old_range.clone());
16682
16683                if let Some(mut excerpt) = excerpt
16684                    && let Some(node) = excerpt
16685                        .buffer()
16686                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16687                {
16688                    let new_range = excerpt.map_range_from_buffer(
16689                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16690                    );
16691                    selected_sibling = true;
16692                    Selection {
16693                        id: selection.id,
16694                        start: new_range.start,
16695                        end: new_range.end,
16696                        goal: SelectionGoal::None,
16697                        reversed: selection.reversed,
16698                    }
16699                } else {
16700                    selection.clone()
16701                }
16702            })
16703            .collect::<Vec<_>>();
16704
16705        if selected_sibling {
16706            self.change_selections(
16707                SelectionEffects::scroll(Autoscroll::fit()),
16708                window,
16709                cx,
16710                |s| {
16711                    s.select(new_selections);
16712                },
16713            );
16714        }
16715    }
16716
16717    pub fn select_prev_syntax_node(
16718        &mut self,
16719        _: &SelectPreviousSyntaxNode,
16720        window: &mut Window,
16721        cx: &mut Context<Self>,
16722    ) {
16723        let old_selections: Box<[_]> = self
16724            .selections
16725            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16726            .into();
16727        if old_selections.is_empty() {
16728            return;
16729        }
16730
16731        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16732
16733        let buffer = self.buffer.read(cx).snapshot(cx);
16734        let mut selected_sibling = false;
16735
16736        let new_selections = old_selections
16737            .iter()
16738            .map(|selection| {
16739                let old_range = selection.start..selection.end;
16740                let old_range =
16741                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16742                let excerpt = buffer.excerpt_containing(old_range.clone());
16743
16744                if let Some(mut excerpt) = excerpt
16745                    && let Some(node) = excerpt
16746                        .buffer()
16747                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16748                {
16749                    let new_range = excerpt.map_range_from_buffer(
16750                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16751                    );
16752                    selected_sibling = true;
16753                    Selection {
16754                        id: selection.id,
16755                        start: new_range.start,
16756                        end: new_range.end,
16757                        goal: SelectionGoal::None,
16758                        reversed: selection.reversed,
16759                    }
16760                } else {
16761                    selection.clone()
16762                }
16763            })
16764            .collect::<Vec<_>>();
16765
16766        if selected_sibling {
16767            self.change_selections(
16768                SelectionEffects::scroll(Autoscroll::fit()),
16769                window,
16770                cx,
16771                |s| {
16772                    s.select(new_selections);
16773                },
16774            );
16775        }
16776    }
16777
16778    pub fn move_to_start_of_larger_syntax_node(
16779        &mut self,
16780        _: &MoveToStartOfLargerSyntaxNode,
16781        window: &mut Window,
16782        cx: &mut Context<Self>,
16783    ) {
16784        self.move_cursors_to_syntax_nodes(window, cx, false);
16785    }
16786
16787    pub fn move_to_end_of_larger_syntax_node(
16788        &mut self,
16789        _: &MoveToEndOfLargerSyntaxNode,
16790        window: &mut Window,
16791        cx: &mut Context<Self>,
16792    ) {
16793        self.move_cursors_to_syntax_nodes(window, cx, true);
16794    }
16795
16796    fn find_syntax_node_boundary(
16797        &self,
16798        selection_pos: MultiBufferOffset,
16799        move_to_end: bool,
16800        display_map: &DisplaySnapshot,
16801        buffer: &MultiBufferSnapshot,
16802    ) -> MultiBufferOffset {
16803        let old_range = selection_pos..selection_pos;
16804        let mut new_pos = selection_pos;
16805        let mut search_range = old_range;
16806        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16807            search_range = range.clone();
16808            if !node.is_named()
16809                || display_map.intersects_fold(range.start)
16810                || display_map.intersects_fold(range.end)
16811                // If cursor is already at the end of the syntax node, continue searching
16812                || (move_to_end && range.end == selection_pos)
16813                // If cursor is already at the start of the syntax node, continue searching
16814                || (!move_to_end && range.start == selection_pos)
16815            {
16816                continue;
16817            }
16818
16819            // If we found a string_content node, find the largest parent that is still string_content
16820            // Enables us to skip to the end of strings without taking multiple steps inside the string
16821            let (_, final_range) = if node.kind() == "string_content" {
16822                let mut current_node = node;
16823                let mut current_range = range;
16824                while let Some((parent, parent_range)) =
16825                    buffer.syntax_ancestor(current_range.clone())
16826                {
16827                    if parent.kind() == "string_content" {
16828                        current_node = parent;
16829                        current_range = parent_range;
16830                    } else {
16831                        break;
16832                    }
16833                }
16834
16835                (current_node, current_range)
16836            } else {
16837                (node, range)
16838            };
16839
16840            new_pos = if move_to_end {
16841                final_range.end
16842            } else {
16843                final_range.start
16844            };
16845
16846            break;
16847        }
16848
16849        new_pos
16850    }
16851
16852    fn move_cursors_to_syntax_nodes(
16853        &mut self,
16854        window: &mut Window,
16855        cx: &mut Context<Self>,
16856        move_to_end: bool,
16857    ) -> bool {
16858        let old_selections: Box<[_]> = self
16859            .selections
16860            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16861            .into();
16862        if old_selections.is_empty() {
16863            return false;
16864        }
16865
16866        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16867
16868        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16869        let buffer = self.buffer.read(cx).snapshot(cx);
16870
16871        let mut any_cursor_moved = false;
16872        let new_selections = old_selections
16873            .iter()
16874            .map(|selection| {
16875                if !selection.is_empty() {
16876                    return selection.clone();
16877                }
16878
16879                let selection_pos = selection.head();
16880                let new_pos = self.find_syntax_node_boundary(
16881                    selection_pos,
16882                    move_to_end,
16883                    &display_map,
16884                    &buffer,
16885                );
16886
16887                any_cursor_moved |= new_pos != selection_pos;
16888
16889                Selection {
16890                    id: selection.id,
16891                    start: new_pos,
16892                    end: new_pos,
16893                    goal: SelectionGoal::None,
16894                    reversed: false,
16895                }
16896            })
16897            .collect::<Vec<_>>();
16898
16899        self.change_selections(Default::default(), window, cx, |s| {
16900            s.select(new_selections);
16901        });
16902        self.request_autoscroll(Autoscroll::newest(), cx);
16903
16904        any_cursor_moved
16905    }
16906
16907    pub fn select_to_start_of_larger_syntax_node(
16908        &mut self,
16909        _: &SelectToStartOfLargerSyntaxNode,
16910        window: &mut Window,
16911        cx: &mut Context<Self>,
16912    ) {
16913        self.select_to_syntax_nodes(window, cx, false);
16914    }
16915
16916    pub fn select_to_end_of_larger_syntax_node(
16917        &mut self,
16918        _: &SelectToEndOfLargerSyntaxNode,
16919        window: &mut Window,
16920        cx: &mut Context<Self>,
16921    ) {
16922        self.select_to_syntax_nodes(window, cx, true);
16923    }
16924
16925    fn select_to_syntax_nodes(
16926        &mut self,
16927        window: &mut Window,
16928        cx: &mut Context<Self>,
16929        move_to_end: bool,
16930    ) {
16931        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16932
16933        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16934        let buffer = self.buffer.read(cx).snapshot(cx);
16935        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
16936
16937        let new_selections = old_selections
16938            .iter()
16939            .map(|selection| {
16940                let new_pos = self.find_syntax_node_boundary(
16941                    selection.head(),
16942                    move_to_end,
16943                    &display_map,
16944                    &buffer,
16945                );
16946
16947                let mut new_selection = selection.clone();
16948                new_selection.set_head(new_pos, SelectionGoal::None);
16949                new_selection
16950            })
16951            .collect::<Vec<_>>();
16952
16953        self.change_selections(Default::default(), window, cx, |s| {
16954            s.select(new_selections);
16955        });
16956    }
16957
16958    pub fn move_to_enclosing_bracket(
16959        &mut self,
16960        _: &MoveToEnclosingBracket,
16961        window: &mut Window,
16962        cx: &mut Context<Self>,
16963    ) {
16964        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16965        self.change_selections(Default::default(), window, cx, |s| {
16966            s.move_offsets_with(&mut |snapshot, selection| {
16967                let Some(enclosing_bracket_ranges) =
16968                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
16969                else {
16970                    return;
16971                };
16972
16973                let mut best_length = usize::MAX;
16974                let mut best_inside = false;
16975                let mut best_in_bracket_range = false;
16976                let mut best_destination = None;
16977                for (open, close) in enclosing_bracket_ranges {
16978                    let close = close.to_inclusive();
16979                    let length = *close.end() - open.start;
16980                    let inside = selection.start >= open.end && selection.end <= *close.start();
16981                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
16982                        || close.contains(&selection.head());
16983
16984                    // If best is next to a bracket and current isn't, skip
16985                    if !in_bracket_range && best_in_bracket_range {
16986                        continue;
16987                    }
16988
16989                    // Prefer smaller lengths unless best is inside and current isn't
16990                    if length > best_length && (best_inside || !inside) {
16991                        continue;
16992                    }
16993
16994                    best_length = length;
16995                    best_inside = inside;
16996                    best_in_bracket_range = in_bracket_range;
16997                    best_destination = Some(
16998                        if close.contains(&selection.start) && close.contains(&selection.end) {
16999                            if inside { open.end } else { open.start }
17000                        } else if inside {
17001                            *close.start()
17002                        } else {
17003                            *close.end()
17004                        },
17005                    );
17006                }
17007
17008                if let Some(destination) = best_destination {
17009                    selection.collapse_to(destination, SelectionGoal::None);
17010                }
17011            })
17012        });
17013    }
17014
17015    pub fn undo_selection(
17016        &mut self,
17017        _: &UndoSelection,
17018        window: &mut Window,
17019        cx: &mut Context<Self>,
17020    ) {
17021        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17022        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17023            self.selection_history.mode = SelectionHistoryMode::Undoing;
17024            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17025                this.end_selection(window, cx);
17026                this.change_selections(
17027                    SelectionEffects::scroll(Autoscroll::newest()),
17028                    window,
17029                    cx,
17030                    |s| s.select_anchors(entry.selections.to_vec()),
17031                );
17032            });
17033            self.selection_history.mode = SelectionHistoryMode::Normal;
17034
17035            self.select_next_state = entry.select_next_state;
17036            self.select_prev_state = entry.select_prev_state;
17037            self.add_selections_state = entry.add_selections_state;
17038        }
17039    }
17040
17041    pub fn redo_selection(
17042        &mut self,
17043        _: &RedoSelection,
17044        window: &mut Window,
17045        cx: &mut Context<Self>,
17046    ) {
17047        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17048        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17049            self.selection_history.mode = SelectionHistoryMode::Redoing;
17050            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17051                this.end_selection(window, cx);
17052                this.change_selections(
17053                    SelectionEffects::scroll(Autoscroll::newest()),
17054                    window,
17055                    cx,
17056                    |s| s.select_anchors(entry.selections.to_vec()),
17057                );
17058            });
17059            self.selection_history.mode = SelectionHistoryMode::Normal;
17060
17061            self.select_next_state = entry.select_next_state;
17062            self.select_prev_state = entry.select_prev_state;
17063            self.add_selections_state = entry.add_selections_state;
17064        }
17065    }
17066
17067    pub fn expand_excerpts(
17068        &mut self,
17069        action: &ExpandExcerpts,
17070        _: &mut Window,
17071        cx: &mut Context<Self>,
17072    ) {
17073        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17074    }
17075
17076    pub fn expand_excerpts_down(
17077        &mut self,
17078        action: &ExpandExcerptsDown,
17079        _: &mut Window,
17080        cx: &mut Context<Self>,
17081    ) {
17082        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17083    }
17084
17085    pub fn expand_excerpts_up(
17086        &mut self,
17087        action: &ExpandExcerptsUp,
17088        _: &mut Window,
17089        cx: &mut Context<Self>,
17090    ) {
17091        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17092    }
17093
17094    pub fn expand_excerpts_for_direction(
17095        &mut self,
17096        lines: u32,
17097        direction: ExpandExcerptDirection,
17098        cx: &mut Context<Self>,
17099    ) {
17100        let selections = self.selections.disjoint_anchors_arc();
17101
17102        let lines = if lines == 0 {
17103            EditorSettings::get_global(cx).expand_excerpt_lines
17104        } else {
17105            lines
17106        };
17107
17108        let snapshot = self.buffer.read(cx).snapshot(cx);
17109        let excerpt_ids = selections
17110            .iter()
17111            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17112            .unique()
17113            .sorted()
17114            .collect::<Vec<_>>();
17115
17116        if self.delegate_expand_excerpts {
17117            cx.emit(EditorEvent::ExpandExcerptsRequested {
17118                excerpt_ids,
17119                lines,
17120                direction,
17121            });
17122            return;
17123        }
17124
17125        self.buffer.update(cx, |buffer, cx| {
17126            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17127        })
17128    }
17129
17130    pub fn expand_excerpt(
17131        &mut self,
17132        excerpt: ExcerptId,
17133        direction: ExpandExcerptDirection,
17134        window: &mut Window,
17135        cx: &mut Context<Self>,
17136    ) {
17137        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17138
17139        if self.delegate_expand_excerpts {
17140            cx.emit(EditorEvent::ExpandExcerptsRequested {
17141                excerpt_ids: vec![excerpt],
17142                lines: lines_to_expand,
17143                direction,
17144            });
17145            return;
17146        }
17147
17148        let current_scroll_position = self.scroll_position(cx);
17149        let mut scroll = None;
17150
17151        if direction == ExpandExcerptDirection::Down {
17152            let multi_buffer = self.buffer.read(cx);
17153            let snapshot = multi_buffer.snapshot(cx);
17154            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17155                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17156                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17157            {
17158                let buffer_snapshot = buffer.read(cx).snapshot();
17159                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17160                let last_row = buffer_snapshot.max_point().row;
17161                let lines_below = last_row.saturating_sub(excerpt_end_row);
17162                if lines_below >= lines_to_expand {
17163                    scroll = Some(
17164                        current_scroll_position
17165                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17166                    );
17167                }
17168            }
17169        }
17170        if direction == ExpandExcerptDirection::Up
17171            && self
17172                .buffer
17173                .read(cx)
17174                .snapshot(cx)
17175                .excerpt_before(excerpt)
17176                .is_none()
17177        {
17178            scroll = Some(current_scroll_position);
17179        }
17180
17181        self.buffer.update(cx, |buffer, cx| {
17182            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17183        });
17184
17185        if let Some(new_scroll_position) = scroll {
17186            self.set_scroll_position(new_scroll_position, window, cx);
17187        }
17188    }
17189
17190    pub fn go_to_singleton_buffer_point(
17191        &mut self,
17192        point: Point,
17193        window: &mut Window,
17194        cx: &mut Context<Self>,
17195    ) {
17196        self.go_to_singleton_buffer_range(point..point, window, cx);
17197    }
17198
17199    pub fn go_to_singleton_buffer_range(
17200        &mut self,
17201        range: Range<Point>,
17202        window: &mut Window,
17203        cx: &mut Context<Self>,
17204    ) {
17205        let multibuffer = self.buffer().read(cx);
17206        let Some(buffer) = multibuffer.as_singleton() else {
17207            return;
17208        };
17209        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17210            return;
17211        };
17212        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17213            return;
17214        };
17215        self.change_selections(
17216            SelectionEffects::default().nav_history(true),
17217            window,
17218            cx,
17219            |s| s.select_anchor_ranges([start..end]),
17220        );
17221    }
17222
17223    pub fn go_to_diagnostic(
17224        &mut self,
17225        action: &GoToDiagnostic,
17226        window: &mut Window,
17227        cx: &mut Context<Self>,
17228    ) {
17229        if !self.diagnostics_enabled() {
17230            return;
17231        }
17232        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17233        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17234    }
17235
17236    pub fn go_to_prev_diagnostic(
17237        &mut self,
17238        action: &GoToPreviousDiagnostic,
17239        window: &mut Window,
17240        cx: &mut Context<Self>,
17241    ) {
17242        if !self.diagnostics_enabled() {
17243            return;
17244        }
17245        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17246        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17247    }
17248
17249    pub fn go_to_diagnostic_impl(
17250        &mut self,
17251        direction: Direction,
17252        severity: GoToDiagnosticSeverityFilter,
17253        window: &mut Window,
17254        cx: &mut Context<Self>,
17255    ) {
17256        let buffer = self.buffer.read(cx).snapshot(cx);
17257        let selection = self
17258            .selections
17259            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17260
17261        let mut active_group_id = None;
17262        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17263            && active_group.active_range.start.to_offset(&buffer) == selection.start
17264        {
17265            active_group_id = Some(active_group.group_id);
17266        }
17267
17268        fn filtered<'a>(
17269            severity: GoToDiagnosticSeverityFilter,
17270            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17271        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17272            diagnostics
17273                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17274                .filter(|entry| entry.range.start != entry.range.end)
17275                .filter(|entry| !entry.diagnostic.is_unnecessary)
17276        }
17277
17278        let before = filtered(
17279            severity,
17280            buffer
17281                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17282                .filter(|entry| entry.range.start <= selection.start),
17283        );
17284        let after = filtered(
17285            severity,
17286            buffer
17287                .diagnostics_in_range(selection.start..buffer.len())
17288                .filter(|entry| entry.range.start >= selection.start),
17289        );
17290
17291        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17292        if direction == Direction::Prev {
17293            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17294            {
17295                for diagnostic in prev_diagnostics.into_iter().rev() {
17296                    if diagnostic.range.start != selection.start
17297                        || active_group_id
17298                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17299                    {
17300                        found = Some(diagnostic);
17301                        break 'outer;
17302                    }
17303                }
17304            }
17305        } else {
17306            for diagnostic in after.chain(before) {
17307                if diagnostic.range.start != selection.start
17308                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17309                {
17310                    found = Some(diagnostic);
17311                    break;
17312                }
17313            }
17314        }
17315        let Some(next_diagnostic) = found else {
17316            return;
17317        };
17318
17319        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17320        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17321            return;
17322        };
17323        let snapshot = self.snapshot(window, cx);
17324        if snapshot.intersects_fold(next_diagnostic.range.start) {
17325            self.unfold_ranges(
17326                std::slice::from_ref(&next_diagnostic.range),
17327                true,
17328                false,
17329                cx,
17330            );
17331        }
17332        self.change_selections(Default::default(), window, cx, |s| {
17333            s.select_ranges(vec![
17334                next_diagnostic.range.start..next_diagnostic.range.start,
17335            ])
17336        });
17337        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17338        self.refresh_edit_prediction(false, true, window, cx);
17339    }
17340
17341    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17342        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17343        let snapshot = self.snapshot(window, cx);
17344        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17345        self.go_to_hunk_before_or_after_position(
17346            &snapshot,
17347            selection.head(),
17348            Direction::Next,
17349            true,
17350            window,
17351            cx,
17352        );
17353    }
17354
17355    pub fn go_to_hunk_before_or_after_position(
17356        &mut self,
17357        snapshot: &EditorSnapshot,
17358        position: Point,
17359        direction: Direction,
17360        wrap_around: bool,
17361        window: &mut Window,
17362        cx: &mut Context<Editor>,
17363    ) {
17364        let row = if direction == Direction::Next {
17365            self.hunk_after_position(snapshot, position, wrap_around)
17366                .map(|hunk| hunk.row_range.start)
17367        } else {
17368            self.hunk_before_position(snapshot, position, wrap_around)
17369        };
17370
17371        if let Some(row) = row {
17372            let destination = Point::new(row.0, 0);
17373            let autoscroll = Autoscroll::center();
17374
17375            self.unfold_ranges(&[destination..destination], false, false, cx);
17376            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17377                s.select_ranges([destination..destination]);
17378            });
17379        }
17380    }
17381
17382    fn hunk_after_position(
17383        &mut self,
17384        snapshot: &EditorSnapshot,
17385        position: Point,
17386        wrap_around: bool,
17387    ) -> Option<MultiBufferDiffHunk> {
17388        let result = snapshot
17389            .buffer_snapshot()
17390            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17391            .find(|hunk| hunk.row_range.start.0 > position.row);
17392
17393        if wrap_around {
17394            result.or_else(|| {
17395                snapshot
17396                    .buffer_snapshot()
17397                    .diff_hunks_in_range(Point::zero()..position)
17398                    .find(|hunk| hunk.row_range.end.0 < position.row)
17399            })
17400        } else {
17401            result
17402        }
17403    }
17404
17405    fn go_to_prev_hunk(
17406        &mut self,
17407        _: &GoToPreviousHunk,
17408        window: &mut Window,
17409        cx: &mut Context<Self>,
17410    ) {
17411        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17412        let snapshot = self.snapshot(window, cx);
17413        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17414        self.go_to_hunk_before_or_after_position(
17415            &snapshot,
17416            selection.head(),
17417            Direction::Prev,
17418            true,
17419            window,
17420            cx,
17421        );
17422    }
17423
17424    fn hunk_before_position(
17425        &mut self,
17426        snapshot: &EditorSnapshot,
17427        position: Point,
17428        wrap_around: bool,
17429    ) -> Option<MultiBufferRow> {
17430        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17431
17432        if wrap_around {
17433            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17434        } else {
17435            result
17436        }
17437    }
17438
17439    fn go_to_next_change(
17440        &mut self,
17441        _: &GoToNextChange,
17442        window: &mut Window,
17443        cx: &mut Context<Self>,
17444    ) {
17445        if let Some(selections) = self
17446            .change_list
17447            .next_change(1, Direction::Next)
17448            .map(|s| s.to_vec())
17449        {
17450            self.change_selections(Default::default(), window, cx, |s| {
17451                let map = s.display_snapshot();
17452                s.select_display_ranges(selections.iter().map(|a| {
17453                    let point = a.to_display_point(&map);
17454                    point..point
17455                }))
17456            })
17457        }
17458    }
17459
17460    fn go_to_previous_change(
17461        &mut self,
17462        _: &GoToPreviousChange,
17463        window: &mut Window,
17464        cx: &mut Context<Self>,
17465    ) {
17466        if let Some(selections) = self
17467            .change_list
17468            .next_change(1, Direction::Prev)
17469            .map(|s| s.to_vec())
17470        {
17471            self.change_selections(Default::default(), window, cx, |s| {
17472                let map = s.display_snapshot();
17473                s.select_display_ranges(selections.iter().map(|a| {
17474                    let point = a.to_display_point(&map);
17475                    point..point
17476                }))
17477            })
17478        }
17479    }
17480
17481    pub fn go_to_next_document_highlight(
17482        &mut self,
17483        _: &GoToNextDocumentHighlight,
17484        window: &mut Window,
17485        cx: &mut Context<Self>,
17486    ) {
17487        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17488    }
17489
17490    pub fn go_to_prev_document_highlight(
17491        &mut self,
17492        _: &GoToPreviousDocumentHighlight,
17493        window: &mut Window,
17494        cx: &mut Context<Self>,
17495    ) {
17496        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17497    }
17498
17499    pub fn go_to_document_highlight_before_or_after_position(
17500        &mut self,
17501        direction: Direction,
17502        window: &mut Window,
17503        cx: &mut Context<Editor>,
17504    ) {
17505        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17506        let snapshot = self.snapshot(window, cx);
17507        let buffer = &snapshot.buffer_snapshot();
17508        let position = self
17509            .selections
17510            .newest::<Point>(&snapshot.display_snapshot)
17511            .head();
17512        let anchor_position = buffer.anchor_after(position);
17513
17514        // Get all document highlights (both read and write)
17515        let mut all_highlights = Vec::new();
17516
17517        if let Some((_, read_highlights)) = self
17518            .background_highlights
17519            .get(&HighlightKey::DocumentHighlightRead)
17520        {
17521            all_highlights.extend(read_highlights.iter());
17522        }
17523
17524        if let Some((_, write_highlights)) = self
17525            .background_highlights
17526            .get(&HighlightKey::DocumentHighlightWrite)
17527        {
17528            all_highlights.extend(write_highlights.iter());
17529        }
17530
17531        if all_highlights.is_empty() {
17532            return;
17533        }
17534
17535        // Sort highlights by position
17536        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17537
17538        let target_highlight = match direction {
17539            Direction::Next => {
17540                // Find the first highlight after the current position
17541                all_highlights
17542                    .iter()
17543                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17544            }
17545            Direction::Prev => {
17546                // Find the last highlight before the current position
17547                all_highlights
17548                    .iter()
17549                    .rev()
17550                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17551            }
17552        };
17553
17554        if let Some(highlight) = target_highlight {
17555            let destination = highlight.start.to_point(buffer);
17556            let autoscroll = Autoscroll::center();
17557
17558            self.unfold_ranges(&[destination..destination], false, false, cx);
17559            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17560                s.select_ranges([destination..destination]);
17561            });
17562        }
17563    }
17564
17565    fn go_to_line<T: 'static>(
17566        &mut self,
17567        position: Anchor,
17568        highlight_color: Option<Hsla>,
17569        window: &mut Window,
17570        cx: &mut Context<Self>,
17571    ) {
17572        let snapshot = self.snapshot(window, cx).display_snapshot;
17573        let position = position.to_point(&snapshot.buffer_snapshot());
17574        let start = snapshot
17575            .buffer_snapshot()
17576            .clip_point(Point::new(position.row, 0), Bias::Left);
17577        let end = start + Point::new(1, 0);
17578        let start = snapshot.buffer_snapshot().anchor_before(start);
17579        let end = snapshot.buffer_snapshot().anchor_before(end);
17580
17581        self.highlight_rows::<T>(
17582            start..end,
17583            highlight_color
17584                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17585            Default::default(),
17586            cx,
17587        );
17588
17589        if self.buffer.read(cx).is_singleton() {
17590            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17591        }
17592    }
17593
17594    pub fn go_to_definition(
17595        &mut self,
17596        _: &GoToDefinition,
17597        window: &mut Window,
17598        cx: &mut Context<Self>,
17599    ) -> Task<Result<Navigated>> {
17600        let definition =
17601            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17602        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17603        cx.spawn_in(window, async move |editor, cx| {
17604            if definition.await? == Navigated::Yes {
17605                return Ok(Navigated::Yes);
17606            }
17607            match fallback_strategy {
17608                GoToDefinitionFallback::None => Ok(Navigated::No),
17609                GoToDefinitionFallback::FindAllReferences => {
17610                    match editor.update_in(cx, |editor, window, cx| {
17611                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17612                    })? {
17613                        Some(references) => references.await,
17614                        None => Ok(Navigated::No),
17615                    }
17616                }
17617            }
17618        })
17619    }
17620
17621    pub fn go_to_declaration(
17622        &mut self,
17623        _: &GoToDeclaration,
17624        window: &mut Window,
17625        cx: &mut Context<Self>,
17626    ) -> Task<Result<Navigated>> {
17627        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17628    }
17629
17630    pub fn go_to_declaration_split(
17631        &mut self,
17632        _: &GoToDeclaration,
17633        window: &mut Window,
17634        cx: &mut Context<Self>,
17635    ) -> Task<Result<Navigated>> {
17636        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17637    }
17638
17639    pub fn go_to_implementation(
17640        &mut self,
17641        _: &GoToImplementation,
17642        window: &mut Window,
17643        cx: &mut Context<Self>,
17644    ) -> Task<Result<Navigated>> {
17645        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17646    }
17647
17648    pub fn go_to_implementation_split(
17649        &mut self,
17650        _: &GoToImplementationSplit,
17651        window: &mut Window,
17652        cx: &mut Context<Self>,
17653    ) -> Task<Result<Navigated>> {
17654        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17655    }
17656
17657    pub fn go_to_type_definition(
17658        &mut self,
17659        _: &GoToTypeDefinition,
17660        window: &mut Window,
17661        cx: &mut Context<Self>,
17662    ) -> Task<Result<Navigated>> {
17663        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17664    }
17665
17666    pub fn go_to_definition_split(
17667        &mut self,
17668        _: &GoToDefinitionSplit,
17669        window: &mut Window,
17670        cx: &mut Context<Self>,
17671    ) -> Task<Result<Navigated>> {
17672        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17673    }
17674
17675    pub fn go_to_type_definition_split(
17676        &mut self,
17677        _: &GoToTypeDefinitionSplit,
17678        window: &mut Window,
17679        cx: &mut Context<Self>,
17680    ) -> Task<Result<Navigated>> {
17681        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17682    }
17683
17684    fn go_to_definition_of_kind(
17685        &mut self,
17686        kind: GotoDefinitionKind,
17687        split: bool,
17688        window: &mut Window,
17689        cx: &mut Context<Self>,
17690    ) -> Task<Result<Navigated>> {
17691        let Some(provider) = self.semantics_provider.clone() else {
17692            return Task::ready(Ok(Navigated::No));
17693        };
17694        let head = self
17695            .selections
17696            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17697            .head();
17698        let buffer = self.buffer.read(cx);
17699        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17700            return Task::ready(Ok(Navigated::No));
17701        };
17702        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17703            return Task::ready(Ok(Navigated::No));
17704        };
17705
17706        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17707
17708        cx.spawn_in(window, async move |editor, cx| {
17709            let Some(definitions) = definitions.await? else {
17710                return Ok(Navigated::No);
17711            };
17712            let navigated = editor
17713                .update_in(cx, |editor, window, cx| {
17714                    editor.navigate_to_hover_links(
17715                        Some(kind),
17716                        definitions
17717                            .into_iter()
17718                            .filter(|location| {
17719                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
17720                            })
17721                            .map(HoverLink::Text)
17722                            .collect::<Vec<_>>(),
17723                        nav_entry,
17724                        split,
17725                        window,
17726                        cx,
17727                    )
17728                })?
17729                .await?;
17730            anyhow::Ok(navigated)
17731        })
17732    }
17733
17734    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
17735        let selection = self.selections.newest_anchor();
17736        let head = selection.head();
17737        let tail = selection.tail();
17738
17739        let Some((buffer, start_position)) =
17740            self.buffer.read(cx).text_anchor_for_position(head, cx)
17741        else {
17742            return;
17743        };
17744
17745        let end_position = if head != tail {
17746            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
17747                return;
17748            };
17749            Some(pos)
17750        } else {
17751            None
17752        };
17753
17754        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
17755            let url = if let Some(end_pos) = end_position {
17756                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
17757            } else {
17758                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
17759            };
17760
17761            if let Some(url) = url {
17762                cx.update(|window, cx| {
17763                    if parse_zed_link(&url, cx).is_some() {
17764                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17765                    } else {
17766                        cx.open_url(&url);
17767                    }
17768                })?;
17769            }
17770
17771            anyhow::Ok(())
17772        });
17773
17774        url_finder.detach();
17775    }
17776
17777    pub fn open_selected_filename(
17778        &mut self,
17779        _: &OpenSelectedFilename,
17780        window: &mut Window,
17781        cx: &mut Context<Self>,
17782    ) {
17783        let Some(workspace) = self.workspace() else {
17784            return;
17785        };
17786
17787        let position = self.selections.newest_anchor().head();
17788
17789        let Some((buffer, buffer_position)) =
17790            self.buffer.read(cx).text_anchor_for_position(position, cx)
17791        else {
17792            return;
17793        };
17794
17795        let project = self.project.clone();
17796
17797        cx.spawn_in(window, async move |_, cx| {
17798            let result = find_file(&buffer, project, buffer_position, cx).await;
17799
17800            if let Some((_, path)) = result {
17801                workspace
17802                    .update_in(cx, |workspace, window, cx| {
17803                        workspace.open_resolved_path(path, window, cx)
17804                    })?
17805                    .await?;
17806            }
17807            anyhow::Ok(())
17808        })
17809        .detach();
17810    }
17811
17812    pub(crate) fn navigate_to_hover_links(
17813        &mut self,
17814        kind: Option<GotoDefinitionKind>,
17815        definitions: Vec<HoverLink>,
17816        origin: Option<NavigationEntry>,
17817        split: bool,
17818        window: &mut Window,
17819        cx: &mut Context<Editor>,
17820    ) -> Task<Result<Navigated>> {
17821        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
17822        let mut first_url_or_file = None;
17823        let definitions: Vec<_> = definitions
17824            .into_iter()
17825            .filter_map(|def| match def {
17826                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
17827                HoverLink::InlayHint(lsp_location, server_id) => {
17828                    let computation =
17829                        self.compute_target_location(lsp_location, server_id, window, cx);
17830                    Some(cx.background_spawn(computation))
17831                }
17832                HoverLink::Url(url) => {
17833                    first_url_or_file = Some(Either::Left(url));
17834                    None
17835                }
17836                HoverLink::File(path) => {
17837                    first_url_or_file = Some(Either::Right(path));
17838                    None
17839                }
17840            })
17841            .collect();
17842
17843        let workspace = self.workspace();
17844
17845        cx.spawn_in(window, async move |editor, cx| {
17846            let locations: Vec<Location> = future::join_all(definitions)
17847                .await
17848                .into_iter()
17849                .filter_map(|location| location.transpose())
17850                .collect::<Result<_>>()
17851                .context("location tasks")?;
17852            let mut locations = cx.update(|_, cx| {
17853                locations
17854                    .into_iter()
17855                    .map(|location| {
17856                        let buffer = location.buffer.read(cx);
17857                        (location.buffer, location.range.to_point(buffer))
17858                    })
17859                    .into_group_map()
17860            })?;
17861            let mut num_locations = 0;
17862            for ranges in locations.values_mut() {
17863                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
17864                ranges.dedup();
17865                num_locations += ranges.len();
17866            }
17867
17868            if num_locations > 1 {
17869                let tab_kind = match kind {
17870                    Some(GotoDefinitionKind::Implementation) => "Implementations",
17871                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
17872                    Some(GotoDefinitionKind::Declaration) => "Declarations",
17873                    Some(GotoDefinitionKind::Type) => "Types",
17874                };
17875                let title = editor
17876                    .update_in(cx, |_, _, cx| {
17877                        let target = locations
17878                            .iter()
17879                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
17880                            .map(|(buffer, location)| {
17881                                buffer
17882                                    .read(cx)
17883                                    .text_for_range(location.clone())
17884                                    .collect::<String>()
17885                            })
17886                            .filter(|text| !text.contains('\n'))
17887                            .unique()
17888                            .take(3)
17889                            .join(", ");
17890                        if target.is_empty() {
17891                            tab_kind.to_owned()
17892                        } else {
17893                            format!("{tab_kind} for {target}")
17894                        }
17895                    })
17896                    .context("buffer title")?;
17897
17898                let Some(workspace) = workspace else {
17899                    return Ok(Navigated::No);
17900                };
17901
17902                let opened = workspace
17903                    .update_in(cx, |workspace, window, cx| {
17904                        let allow_preview = PreviewTabsSettings::get_global(cx)
17905                            .enable_preview_multibuffer_from_code_navigation;
17906                        if let Some((target_editor, target_pane)) =
17907                            Self::open_locations_in_multibuffer(
17908                                workspace,
17909                                locations,
17910                                title,
17911                                split,
17912                                allow_preview,
17913                                MultibufferSelectionMode::First,
17914                                window,
17915                                cx,
17916                            )
17917                        {
17918                            // We create our own nav history instead of using
17919                            // `target_editor.nav_history` because `nav_history`
17920                            // seems to be populated asynchronously when an item
17921                            // is added to a pane
17922                            let mut nav_history = target_pane
17923                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
17924                            target_editor.update(cx, |editor, cx| {
17925                                let nav_data = editor
17926                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
17927                                let target =
17928                                    Some(nav_history.navigation_entry(Some(
17929                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
17930                                    )));
17931                                nav_history.push_tag(origin, target);
17932                            })
17933                        }
17934                    })
17935                    .is_ok();
17936
17937                anyhow::Ok(Navigated::from_bool(opened))
17938            } else if num_locations == 0 {
17939                // If there is one url or file, open it directly
17940                match first_url_or_file {
17941                    Some(Either::Left(url)) => {
17942                        cx.update(|window, cx| {
17943                            if parse_zed_link(&url, cx).is_some() {
17944                                window
17945                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
17946                            } else {
17947                                cx.open_url(&url);
17948                            }
17949                        })?;
17950                        Ok(Navigated::Yes)
17951                    }
17952                    Some(Either::Right(path)) => {
17953                        // TODO(andrew): respect preview tab settings
17954                        //               `enable_keep_preview_on_code_navigation` and
17955                        //               `enable_preview_file_from_code_navigation`
17956                        let Some(workspace) = workspace else {
17957                            return Ok(Navigated::No);
17958                        };
17959                        workspace
17960                            .update_in(cx, |workspace, window, cx| {
17961                                workspace.open_resolved_path(path, window, cx)
17962                            })?
17963                            .await?;
17964                        Ok(Navigated::Yes)
17965                    }
17966                    None => Ok(Navigated::No),
17967                }
17968            } else {
17969                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
17970                let target_range = target_ranges.first().unwrap().clone();
17971
17972                editor.update_in(cx, |editor, window, cx| {
17973                    let range = editor.range_for_match(&target_range);
17974                    let range = collapse_multiline_range(range);
17975
17976                    if !split
17977                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
17978                    {
17979                        editor.go_to_singleton_buffer_range(range, window, cx);
17980
17981                        let target =
17982                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
17983                        if let Some(mut nav_history) = editor.nav_history.clone() {
17984                            nav_history.push_tag(origin, target);
17985                        }
17986                    } else {
17987                        let Some(workspace) = workspace else {
17988                            return Navigated::No;
17989                        };
17990                        let pane = workspace.read(cx).active_pane().clone();
17991                        window.defer(cx, move |window, cx| {
17992                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
17993                                workspace.update(cx, |workspace, cx| {
17994                                    let pane = if split {
17995                                        workspace.adjacent_pane(window, cx)
17996                                    } else {
17997                                        workspace.active_pane().clone()
17998                                    };
17999
18000                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18001                                    let keep_old_preview = preview_tabs_settings
18002                                        .enable_keep_preview_on_code_navigation;
18003                                    let allow_new_preview = preview_tabs_settings
18004                                        .enable_preview_file_from_code_navigation;
18005
18006                                    let editor = workspace.open_project_item(
18007                                        pane.clone(),
18008                                        target_buffer.clone(),
18009                                        true,
18010                                        true,
18011                                        keep_old_preview,
18012                                        allow_new_preview,
18013                                        window,
18014                                        cx,
18015                                    );
18016                                    (editor, pane)
18017                                });
18018                            // We create our own nav history instead of using
18019                            // `target_editor.nav_history` because `nav_history`
18020                            // seems to be populated asynchronously when an item
18021                            // is added to a pane
18022                            let mut nav_history = target_pane
18023                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18024                            target_editor.update(cx, |target_editor, cx| {
18025                                // When selecting a definition in a different buffer, disable the nav history
18026                                // to avoid creating a history entry at the previous cursor location.
18027                                pane.update(cx, |pane, _| pane.disable_history());
18028                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18029
18030                                let nav_data = target_editor.navigation_data(
18031                                    target_editor.selections.newest_anchor().head(),
18032                                    cx,
18033                                );
18034                                let target =
18035                                    Some(nav_history.navigation_entry(Some(
18036                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18037                                    )));
18038                                nav_history.push_tag(origin, target);
18039                                pane.update(cx, |pane, _| pane.enable_history());
18040                            });
18041                        });
18042                    }
18043                    Navigated::Yes
18044                })
18045            }
18046        })
18047    }
18048
18049    fn compute_target_location(
18050        &self,
18051        lsp_location: lsp::Location,
18052        server_id: LanguageServerId,
18053        window: &mut Window,
18054        cx: &mut Context<Self>,
18055    ) -> Task<anyhow::Result<Option<Location>>> {
18056        let Some(project) = self.project.clone() else {
18057            return Task::ready(Ok(None));
18058        };
18059
18060        cx.spawn_in(window, async move |editor, cx| {
18061            let location_task = editor.update(cx, |_, cx| {
18062                project.update(cx, |project, cx| {
18063                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18064                })
18065            })?;
18066            let location = Some({
18067                let target_buffer_handle = location_task.await.context("open local buffer")?;
18068                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18069                    let target_start = target_buffer
18070                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18071                    let target_end = target_buffer
18072                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18073                    target_buffer.anchor_after(target_start)
18074                        ..target_buffer.anchor_before(target_end)
18075                });
18076                Location {
18077                    buffer: target_buffer_handle,
18078                    range,
18079                }
18080            });
18081            Ok(location)
18082        })
18083    }
18084
18085    fn go_to_next_reference(
18086        &mut self,
18087        _: &GoToNextReference,
18088        window: &mut Window,
18089        cx: &mut Context<Self>,
18090    ) {
18091        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18092        if let Some(task) = task {
18093            task.detach();
18094        };
18095    }
18096
18097    fn go_to_prev_reference(
18098        &mut self,
18099        _: &GoToPreviousReference,
18100        window: &mut Window,
18101        cx: &mut Context<Self>,
18102    ) {
18103        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18104        if let Some(task) = task {
18105            task.detach();
18106        };
18107    }
18108
18109    pub fn go_to_reference_before_or_after_position(
18110        &mut self,
18111        direction: Direction,
18112        count: usize,
18113        window: &mut Window,
18114        cx: &mut Context<Self>,
18115    ) -> Option<Task<Result<()>>> {
18116        let selection = self.selections.newest_anchor();
18117        let head = selection.head();
18118
18119        let multi_buffer = self.buffer.read(cx);
18120
18121        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18122        let workspace = self.workspace()?;
18123        let project = workspace.read(cx).project().clone();
18124        let references =
18125            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18126        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18127            let Some(locations) = references.await? else {
18128                return Ok(());
18129            };
18130
18131            if locations.is_empty() {
18132                // totally normal - the cursor may be on something which is not
18133                // a symbol (e.g. a keyword)
18134                log::info!("no references found under cursor");
18135                return Ok(());
18136            }
18137
18138            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18139
18140            let (locations, current_location_index) =
18141                multi_buffer.update(cx, |multi_buffer, cx| {
18142                    let mut locations = locations
18143                        .into_iter()
18144                        .filter_map(|loc| {
18145                            let start = multi_buffer.buffer_anchor_to_anchor(
18146                                &loc.buffer,
18147                                loc.range.start,
18148                                cx,
18149                            )?;
18150                            let end = multi_buffer.buffer_anchor_to_anchor(
18151                                &loc.buffer,
18152                                loc.range.end,
18153                                cx,
18154                            )?;
18155                            Some(start..end)
18156                        })
18157                        .collect::<Vec<_>>();
18158
18159                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18160                    // There is an O(n) implementation, but given this list will be
18161                    // small (usually <100 items), the extra O(log(n)) factor isn't
18162                    // worth the (surprisingly large amount of) extra complexity.
18163                    locations
18164                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18165
18166                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18167
18168                    let current_location_index = locations.iter().position(|loc| {
18169                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18170                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18171                    });
18172
18173                    (locations, current_location_index)
18174                });
18175
18176            let Some(current_location_index) = current_location_index else {
18177                // This indicates something has gone wrong, because we already
18178                // handle the "no references" case above
18179                log::error!(
18180                    "failed to find current reference under cursor. Total references: {}",
18181                    locations.len()
18182                );
18183                return Ok(());
18184            };
18185
18186            let destination_location_index = match direction {
18187                Direction::Next => (current_location_index + count) % locations.len(),
18188                Direction::Prev => {
18189                    (current_location_index + locations.len() - count % locations.len())
18190                        % locations.len()
18191                }
18192            };
18193
18194            // TODO(cameron): is this needed?
18195            // the thinking is to avoid "jumping to the current location" (avoid
18196            // polluting "jumplist" in vim terms)
18197            if current_location_index == destination_location_index {
18198                return Ok(());
18199            }
18200
18201            let Range { start, end } = locations[destination_location_index];
18202
18203            editor.update_in(cx, |editor, window, cx| {
18204                let effects = SelectionEffects::default();
18205
18206                editor.unfold_ranges(&[start..end], false, false, cx);
18207                editor.change_selections(effects, window, cx, |s| {
18208                    s.select_ranges([start..start]);
18209                });
18210            })?;
18211
18212            Ok(())
18213        }))
18214    }
18215
18216    pub fn find_all_references(
18217        &mut self,
18218        action: &FindAllReferences,
18219        window: &mut Window,
18220        cx: &mut Context<Self>,
18221    ) -> Option<Task<Result<Navigated>>> {
18222        let always_open_multibuffer = action.always_open_multibuffer;
18223        let selection = self.selections.newest_anchor();
18224        let multi_buffer = self.buffer.read(cx);
18225        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18226        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18227        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18228        let head = selection_offset.head();
18229
18230        let head_anchor = multi_buffer_snapshot.anchor_at(
18231            head,
18232            if head < selection_offset.tail() {
18233                Bias::Right
18234            } else {
18235                Bias::Left
18236            },
18237        );
18238
18239        match self
18240            .find_all_references_task_sources
18241            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18242        {
18243            Ok(_) => {
18244                log::info!(
18245                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18246                );
18247                return None;
18248            }
18249            Err(i) => {
18250                self.find_all_references_task_sources.insert(i, head_anchor);
18251            }
18252        }
18253
18254        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18255        let workspace = self.workspace()?;
18256        let project = workspace.read(cx).project().clone();
18257        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18258        Some(cx.spawn_in(window, async move |editor, cx| {
18259            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18260                if let Ok(i) = editor
18261                    .find_all_references_task_sources
18262                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18263                {
18264                    editor.find_all_references_task_sources.remove(i);
18265                }
18266            });
18267
18268            let Some(locations) = references.await? else {
18269                return anyhow::Ok(Navigated::No);
18270            };
18271            let mut locations = cx.update(|_, cx| {
18272                locations
18273                    .into_iter()
18274                    .map(|location| {
18275                        let buffer = location.buffer.read(cx);
18276                        (location.buffer, location.range.to_point(buffer))
18277                    })
18278                    // if special-casing the single-match case, remove ranges
18279                    // that intersect current selection
18280                    .filter(|(location_buffer, location)| {
18281                        if always_open_multibuffer || &buffer != location_buffer {
18282                            return true;
18283                        }
18284
18285                        !location.contains_inclusive(&selection_point.range())
18286                    })
18287                    .into_group_map()
18288            })?;
18289            if locations.is_empty() {
18290                return anyhow::Ok(Navigated::No);
18291            }
18292            for ranges in locations.values_mut() {
18293                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18294                ranges.dedup();
18295            }
18296            let mut num_locations = 0;
18297            for ranges in locations.values_mut() {
18298                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18299                ranges.dedup();
18300                num_locations += ranges.len();
18301            }
18302
18303            if num_locations == 1 && !always_open_multibuffer {
18304                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18305                let target_range = target_ranges.first().unwrap().clone();
18306
18307                return editor.update_in(cx, |editor, window, cx| {
18308                    let range = target_range.to_point(target_buffer.read(cx));
18309                    let range = editor.range_for_match(&range);
18310                    let range = range.start..range.start;
18311
18312                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18313                        editor.go_to_singleton_buffer_range(range, window, cx);
18314                    } else {
18315                        let pane = workspace.read(cx).active_pane().clone();
18316                        window.defer(cx, move |window, cx| {
18317                            let target_editor: Entity<Self> =
18318                                workspace.update(cx, |workspace, cx| {
18319                                    let pane = workspace.active_pane().clone();
18320
18321                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18322                                    let keep_old_preview = preview_tabs_settings
18323                                        .enable_keep_preview_on_code_navigation;
18324                                    let allow_new_preview = preview_tabs_settings
18325                                        .enable_preview_file_from_code_navigation;
18326
18327                                    workspace.open_project_item(
18328                                        pane,
18329                                        target_buffer.clone(),
18330                                        true,
18331                                        true,
18332                                        keep_old_preview,
18333                                        allow_new_preview,
18334                                        window,
18335                                        cx,
18336                                    )
18337                                });
18338                            target_editor.update(cx, |target_editor, cx| {
18339                                // When selecting a definition in a different buffer, disable the nav history
18340                                // to avoid creating a history entry at the previous cursor location.
18341                                pane.update(cx, |pane, _| pane.disable_history());
18342                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18343                                pane.update(cx, |pane, _| pane.enable_history());
18344                            });
18345                        });
18346                    }
18347                    Navigated::No
18348                });
18349            }
18350
18351            workspace.update_in(cx, |workspace, window, cx| {
18352                let target = locations
18353                    .iter()
18354                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18355                    .map(|(buffer, location)| {
18356                        buffer
18357                            .read(cx)
18358                            .text_for_range(location.clone())
18359                            .collect::<String>()
18360                    })
18361                    .filter(|text| !text.contains('\n'))
18362                    .unique()
18363                    .take(3)
18364                    .join(", ");
18365                let title = if target.is_empty() {
18366                    "References".to_owned()
18367                } else {
18368                    format!("References to {target}")
18369                };
18370                let allow_preview = PreviewTabsSettings::get_global(cx)
18371                    .enable_preview_multibuffer_from_code_navigation;
18372                Self::open_locations_in_multibuffer(
18373                    workspace,
18374                    locations,
18375                    title,
18376                    false,
18377                    allow_preview,
18378                    MultibufferSelectionMode::First,
18379                    window,
18380                    cx,
18381                );
18382                Navigated::Yes
18383            })
18384        }))
18385    }
18386
18387    /// Opens a multibuffer with the given project locations in it.
18388    pub fn open_locations_in_multibuffer(
18389        workspace: &mut Workspace,
18390        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18391        title: String,
18392        split: bool,
18393        allow_preview: bool,
18394        multibuffer_selection_mode: MultibufferSelectionMode,
18395        window: &mut Window,
18396        cx: &mut Context<Workspace>,
18397    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18398        if locations.is_empty() {
18399            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18400            return None;
18401        }
18402
18403        let capability = workspace.project().read(cx).capability();
18404        let mut ranges = <Vec<Range<Anchor>>>::new();
18405
18406        // a key to find existing multibuffer editors with the same set of locations
18407        // to prevent us from opening more and more multibuffer tabs for searches and the like
18408        let mut key = (title.clone(), vec![]);
18409        let excerpt_buffer = cx.new(|cx| {
18410            let key = &mut key.1;
18411            let mut multibuffer = MultiBuffer::new(capability);
18412            for (buffer, mut ranges_for_buffer) in locations {
18413                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18414                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18415                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18416                    PathKey::for_buffer(&buffer, cx),
18417                    buffer.clone(),
18418                    ranges_for_buffer,
18419                    multibuffer_context_lines(cx),
18420                    cx,
18421                );
18422                ranges.extend(new_ranges)
18423            }
18424
18425            multibuffer.with_title(title)
18426        });
18427        let existing = workspace.active_pane().update(cx, |pane, cx| {
18428            pane.items()
18429                .filter_map(|item| item.downcast::<Editor>())
18430                .find(|editor| {
18431                    editor
18432                        .read(cx)
18433                        .lookup_key
18434                        .as_ref()
18435                        .and_then(|it| {
18436                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18437                        })
18438                        .is_some_and(|it| *it == key)
18439                })
18440        });
18441        let was_existing = existing.is_some();
18442        let editor = existing.unwrap_or_else(|| {
18443            cx.new(|cx| {
18444                let mut editor = Editor::for_multibuffer(
18445                    excerpt_buffer,
18446                    Some(workspace.project().clone()),
18447                    window,
18448                    cx,
18449                );
18450                editor.lookup_key = Some(Box::new(key));
18451                editor
18452            })
18453        });
18454        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18455            MultibufferSelectionMode::First => {
18456                if let Some(first_range) = ranges.first() {
18457                    editor.change_selections(
18458                        SelectionEffects::no_scroll(),
18459                        window,
18460                        cx,
18461                        |selections| {
18462                            selections.clear_disjoint();
18463                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18464                        },
18465                    );
18466                }
18467                editor.highlight_background(
18468                    HighlightKey::Editor,
18469                    &ranges,
18470                    |_, theme| theme.colors().editor_highlighted_line_background,
18471                    cx,
18472                );
18473            }
18474            MultibufferSelectionMode::All => {
18475                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18476                    selections.clear_disjoint();
18477                    selections.select_anchor_ranges(ranges);
18478                });
18479            }
18480        });
18481
18482        let item = Box::new(editor.clone());
18483
18484        let pane = if split {
18485            workspace.adjacent_pane(window, cx)
18486        } else {
18487            workspace.active_pane().clone()
18488        };
18489        let activate_pane = split;
18490
18491        let mut destination_index = None;
18492        pane.update(cx, |pane, cx| {
18493            if allow_preview && !was_existing {
18494                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18495            }
18496            if was_existing && !allow_preview {
18497                pane.unpreview_item_if_preview(item.item_id());
18498            }
18499            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18500        });
18501
18502        Some((editor, pane))
18503    }
18504
18505    pub fn rename(
18506        &mut self,
18507        _: &Rename,
18508        window: &mut Window,
18509        cx: &mut Context<Self>,
18510    ) -> Option<Task<Result<()>>> {
18511        use language::ToOffset as _;
18512
18513        let provider = self.semantics_provider.clone()?;
18514        let selection = self.selections.newest_anchor().clone();
18515        let (cursor_buffer, cursor_buffer_position) = self
18516            .buffer
18517            .read(cx)
18518            .text_anchor_for_position(selection.head(), cx)?;
18519        let (tail_buffer, cursor_buffer_position_end) = self
18520            .buffer
18521            .read(cx)
18522            .text_anchor_for_position(selection.tail(), cx)?;
18523        if tail_buffer != cursor_buffer {
18524            return None;
18525        }
18526
18527        let snapshot = cursor_buffer.read(cx).snapshot();
18528        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18529        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18530        let prepare_rename = provider
18531            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18532            .unwrap_or_else(|| Task::ready(Ok(None)));
18533        drop(snapshot);
18534
18535        Some(cx.spawn_in(window, async move |this, cx| {
18536            let rename_range = if let Some(range) = prepare_rename.await? {
18537                Some(range)
18538            } else {
18539                this.update(cx, |this, cx| {
18540                    let buffer = this.buffer.read(cx).snapshot(cx);
18541                    let mut buffer_highlights = this
18542                        .document_highlights_for_position(selection.head(), &buffer)
18543                        .filter(|highlight| {
18544                            highlight.start.excerpt_id == selection.head().excerpt_id
18545                                && highlight.end.excerpt_id == selection.head().excerpt_id
18546                        });
18547                    buffer_highlights
18548                        .next()
18549                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18550                })?
18551            };
18552            if let Some(rename_range) = rename_range {
18553                this.update_in(cx, |this, window, cx| {
18554                    let snapshot = cursor_buffer.read(cx).snapshot();
18555                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18556                    let cursor_offset_in_rename_range =
18557                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18558                    let cursor_offset_in_rename_range_end =
18559                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18560
18561                    this.take_rename(false, window, cx);
18562                    let buffer = this.buffer.read(cx).read(cx);
18563                    let cursor_offset = selection.head().to_offset(&buffer);
18564                    let rename_start =
18565                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18566                    let rename_end = rename_start + rename_buffer_range.len();
18567                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18568                    let mut old_highlight_id = None;
18569                    let old_name: Arc<str> = buffer
18570                        .chunks(rename_start..rename_end, true)
18571                        .map(|chunk| {
18572                            if old_highlight_id.is_none() {
18573                                old_highlight_id = chunk.syntax_highlight_id;
18574                            }
18575                            chunk.text
18576                        })
18577                        .collect::<String>()
18578                        .into();
18579
18580                    drop(buffer);
18581
18582                    // Position the selection in the rename editor so that it matches the current selection.
18583                    this.show_local_selections = false;
18584                    let rename_editor = cx.new(|cx| {
18585                        let mut editor = Editor::single_line(window, cx);
18586                        editor.buffer.update(cx, |buffer, cx| {
18587                            buffer.edit(
18588                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18589                                None,
18590                                cx,
18591                            )
18592                        });
18593                        let cursor_offset_in_rename_range =
18594                            MultiBufferOffset(cursor_offset_in_rename_range);
18595                        let cursor_offset_in_rename_range_end =
18596                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18597                        let rename_selection_range = match cursor_offset_in_rename_range
18598                            .cmp(&cursor_offset_in_rename_range_end)
18599                        {
18600                            Ordering::Equal => {
18601                                editor.select_all(&SelectAll, window, cx);
18602                                return editor;
18603                            }
18604                            Ordering::Less => {
18605                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18606                            }
18607                            Ordering::Greater => {
18608                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18609                            }
18610                        };
18611                        if rename_selection_range.end.0 > old_name.len() {
18612                            editor.select_all(&SelectAll, window, cx);
18613                        } else {
18614                            editor.change_selections(Default::default(), window, cx, |s| {
18615                                s.select_ranges([rename_selection_range]);
18616                            });
18617                        }
18618                        editor
18619                    });
18620                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18621                        if e == &EditorEvent::Focused {
18622                            cx.emit(EditorEvent::FocusedIn)
18623                        }
18624                    })
18625                    .detach();
18626
18627                    let write_highlights =
18628                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18629                    let read_highlights =
18630                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18631                    let ranges = write_highlights
18632                        .iter()
18633                        .flat_map(|(_, ranges)| ranges.iter())
18634                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18635                        .cloned()
18636                        .collect();
18637
18638                    this.highlight_text(
18639                        HighlightKey::Rename,
18640                        ranges,
18641                        HighlightStyle {
18642                            fade_out: Some(0.6),
18643                            ..Default::default()
18644                        },
18645                        cx,
18646                    );
18647                    let rename_focus_handle = rename_editor.focus_handle(cx);
18648                    window.focus(&rename_focus_handle, cx);
18649                    let block_id = this.insert_blocks(
18650                        [BlockProperties {
18651                            style: BlockStyle::Flex,
18652                            placement: BlockPlacement::Below(range.start),
18653                            height: Some(1),
18654                            render: Arc::new({
18655                                let rename_editor = rename_editor.clone();
18656                                move |cx: &mut BlockContext| {
18657                                    let mut text_style = cx.editor_style.text.clone();
18658                                    if let Some(highlight_style) = old_highlight_id
18659                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18660                                    {
18661                                        text_style = text_style.highlight(highlight_style);
18662                                    }
18663                                    div()
18664                                        .block_mouse_except_scroll()
18665                                        .pl(cx.anchor_x)
18666                                        .child(EditorElement::new(
18667                                            &rename_editor,
18668                                            EditorStyle {
18669                                                background: cx.theme().system().transparent,
18670                                                local_player: cx.editor_style.local_player,
18671                                                text: text_style,
18672                                                scrollbar_width: cx.editor_style.scrollbar_width,
18673                                                syntax: cx.editor_style.syntax.clone(),
18674                                                status: cx.editor_style.status.clone(),
18675                                                inlay_hints_style: HighlightStyle {
18676                                                    font_weight: Some(FontWeight::BOLD),
18677                                                    ..make_inlay_hints_style(cx.app)
18678                                                },
18679                                                edit_prediction_styles: make_suggestion_styles(
18680                                                    cx.app,
18681                                                ),
18682                                                ..EditorStyle::default()
18683                                            },
18684                                        ))
18685                                        .into_any_element()
18686                                }
18687                            }),
18688                            priority: 0,
18689                        }],
18690                        Some(Autoscroll::fit()),
18691                        cx,
18692                    )[0];
18693                    this.pending_rename = Some(RenameState {
18694                        range,
18695                        old_name,
18696                        editor: rename_editor,
18697                        block_id,
18698                    });
18699                })?;
18700            }
18701
18702            Ok(())
18703        }))
18704    }
18705
18706    pub fn confirm_rename(
18707        &mut self,
18708        _: &ConfirmRename,
18709        window: &mut Window,
18710        cx: &mut Context<Self>,
18711    ) -> Option<Task<Result<()>>> {
18712        let rename = self.take_rename(false, window, cx)?;
18713        let workspace = self.workspace()?.downgrade();
18714        let (buffer, start) = self
18715            .buffer
18716            .read(cx)
18717            .text_anchor_for_position(rename.range.start, cx)?;
18718        let (end_buffer, _) = self
18719            .buffer
18720            .read(cx)
18721            .text_anchor_for_position(rename.range.end, cx)?;
18722        if buffer != end_buffer {
18723            return None;
18724        }
18725
18726        let old_name = rename.old_name;
18727        let new_name = rename.editor.read(cx).text(cx);
18728
18729        let rename = self.semantics_provider.as_ref()?.perform_rename(
18730            &buffer,
18731            start,
18732            new_name.clone(),
18733            cx,
18734        )?;
18735
18736        Some(cx.spawn_in(window, async move |editor, cx| {
18737            let project_transaction = rename.await?;
18738            Self::open_project_transaction(
18739                &editor,
18740                workspace,
18741                project_transaction,
18742                format!("Rename: {}{}", old_name, new_name),
18743                cx,
18744            )
18745            .await?;
18746
18747            editor.update(cx, |editor, cx| {
18748                editor.refresh_document_highlights(cx);
18749            })?;
18750            Ok(())
18751        }))
18752    }
18753
18754    fn take_rename(
18755        &mut self,
18756        moving_cursor: bool,
18757        window: &mut Window,
18758        cx: &mut Context<Self>,
18759    ) -> Option<RenameState> {
18760        let rename = self.pending_rename.take()?;
18761        if rename.editor.focus_handle(cx).is_focused(window) {
18762            window.focus(&self.focus_handle, cx);
18763        }
18764
18765        self.remove_blocks(
18766            [rename.block_id].into_iter().collect(),
18767            Some(Autoscroll::fit()),
18768            cx,
18769        );
18770        self.clear_highlights(HighlightKey::Rename, cx);
18771        self.show_local_selections = true;
18772
18773        if moving_cursor {
18774            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
18775                editor
18776                    .selections
18777                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
18778                    .head()
18779            });
18780
18781            // Update the selection to match the position of the selection inside
18782            // the rename editor.
18783            let snapshot = self.buffer.read(cx).read(cx);
18784            let rename_range = rename.range.to_offset(&snapshot);
18785            let cursor_in_editor = snapshot
18786                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
18787                .min(rename_range.end);
18788            drop(snapshot);
18789
18790            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
18791                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
18792            });
18793        } else {
18794            self.refresh_document_highlights(cx);
18795        }
18796
18797        Some(rename)
18798    }
18799
18800    pub fn pending_rename(&self) -> Option<&RenameState> {
18801        self.pending_rename.as_ref()
18802    }
18803
18804    fn format(
18805        &mut self,
18806        _: &Format,
18807        window: &mut Window,
18808        cx: &mut Context<Self>,
18809    ) -> Option<Task<Result<()>>> {
18810        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18811
18812        let project = match &self.project {
18813            Some(project) => project.clone(),
18814            None => return None,
18815        };
18816
18817        Some(self.perform_format(
18818            project,
18819            FormatTrigger::Manual,
18820            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
18821            window,
18822            cx,
18823        ))
18824    }
18825
18826    fn format_selections(
18827        &mut self,
18828        _: &FormatSelections,
18829        window: &mut Window,
18830        cx: &mut Context<Self>,
18831    ) -> Option<Task<Result<()>>> {
18832        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18833
18834        let project = match &self.project {
18835            Some(project) => project.clone(),
18836            None => return None,
18837        };
18838
18839        let ranges = self
18840            .selections
18841            .all_adjusted(&self.display_snapshot(cx))
18842            .into_iter()
18843            .map(|selection| selection.range())
18844            .collect_vec();
18845
18846        Some(self.perform_format(
18847            project,
18848            FormatTrigger::Manual,
18849            FormatTarget::Ranges(ranges),
18850            window,
18851            cx,
18852        ))
18853    }
18854
18855    fn perform_format(
18856        &mut self,
18857        project: Entity<Project>,
18858        trigger: FormatTrigger,
18859        target: FormatTarget,
18860        window: &mut Window,
18861        cx: &mut Context<Self>,
18862    ) -> Task<Result<()>> {
18863        let buffer = self.buffer.clone();
18864        let (buffers, target) = match target {
18865            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
18866            FormatTarget::Ranges(selection_ranges) => {
18867                let multi_buffer = buffer.read(cx);
18868                let snapshot = multi_buffer.read(cx);
18869                let mut buffers = HashSet::default();
18870                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
18871                    BTreeMap::new();
18872                for selection_range in selection_ranges {
18873                    for (buffer, buffer_range, _) in
18874                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
18875                    {
18876                        let buffer_id = buffer.remote_id();
18877                        let start = buffer.anchor_before(buffer_range.start);
18878                        let end = buffer.anchor_after(buffer_range.end);
18879                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
18880                        buffer_id_to_ranges
18881                            .entry(buffer_id)
18882                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
18883                            .or_insert_with(|| vec![start..end]);
18884                    }
18885                }
18886                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
18887            }
18888        };
18889
18890        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
18891        let selections_prev = transaction_id_prev
18892            .and_then(|transaction_id_prev| {
18893                // default to selections as they were after the last edit, if we have them,
18894                // instead of how they are now.
18895                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
18896                // will take you back to where you made the last edit, instead of staying where you scrolled
18897                self.selection_history
18898                    .transaction(transaction_id_prev)
18899                    .map(|t| t.0.clone())
18900            })
18901            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
18902
18903        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
18904        let format = project.update(cx, |project, cx| {
18905            project.format(buffers, target, true, trigger, cx)
18906        });
18907
18908        cx.spawn_in(window, async move |editor, cx| {
18909            let transaction = futures::select_biased! {
18910                transaction = format.log_err().fuse() => transaction,
18911                () = timeout => {
18912                    log::warn!("timed out waiting for formatting");
18913                    None
18914                }
18915            };
18916
18917            buffer.update(cx, |buffer, cx| {
18918                if let Some(transaction) = transaction
18919                    && !buffer.is_singleton()
18920                {
18921                    buffer.push_transaction(&transaction.0, cx);
18922                }
18923                cx.notify();
18924            });
18925
18926            if let Some(transaction_id_now) =
18927                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
18928            {
18929                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
18930                if has_new_transaction {
18931                    editor
18932                        .update(cx, |editor, _| {
18933                            editor
18934                                .selection_history
18935                                .insert_transaction(transaction_id_now, selections_prev);
18936                        })
18937                        .ok();
18938                }
18939            }
18940
18941            Ok(())
18942        })
18943    }
18944
18945    fn organize_imports(
18946        &mut self,
18947        _: &OrganizeImports,
18948        window: &mut Window,
18949        cx: &mut Context<Self>,
18950    ) -> Option<Task<Result<()>>> {
18951        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18952        let project = match &self.project {
18953            Some(project) => project.clone(),
18954            None => return None,
18955        };
18956        Some(self.perform_code_action_kind(
18957            project,
18958            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
18959            window,
18960            cx,
18961        ))
18962    }
18963
18964    fn perform_code_action_kind(
18965        &mut self,
18966        project: Entity<Project>,
18967        kind: CodeActionKind,
18968        window: &mut Window,
18969        cx: &mut Context<Self>,
18970    ) -> Task<Result<()>> {
18971        let buffer = self.buffer.clone();
18972        let buffers = buffer.read(cx).all_buffers();
18973        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
18974        let apply_action = project.update(cx, |project, cx| {
18975            project.apply_code_action_kind(buffers, kind, true, cx)
18976        });
18977        cx.spawn_in(window, async move |_, cx| {
18978            let transaction = futures::select_biased! {
18979                () = timeout => {
18980                    log::warn!("timed out waiting for executing code action");
18981                    None
18982                }
18983                transaction = apply_action.log_err().fuse() => transaction,
18984            };
18985            buffer.update(cx, |buffer, cx| {
18986                // check if we need this
18987                if let Some(transaction) = transaction
18988                    && !buffer.is_singleton()
18989                {
18990                    buffer.push_transaction(&transaction.0, cx);
18991                }
18992                cx.notify();
18993            });
18994            Ok(())
18995        })
18996    }
18997
18998    pub fn restart_language_server(
18999        &mut self,
19000        _: &RestartLanguageServer,
19001        _: &mut Window,
19002        cx: &mut Context<Self>,
19003    ) {
19004        if let Some(project) = self.project.clone() {
19005            self.buffer.update(cx, |multi_buffer, cx| {
19006                project.update(cx, |project, cx| {
19007                    project.restart_language_servers_for_buffers(
19008                        multi_buffer.all_buffers().into_iter().collect(),
19009                        HashSet::default(),
19010                        cx,
19011                    );
19012                });
19013            })
19014        }
19015    }
19016
19017    pub fn stop_language_server(
19018        &mut self,
19019        _: &StopLanguageServer,
19020        _: &mut Window,
19021        cx: &mut Context<Self>,
19022    ) {
19023        if let Some(project) = self.project.clone() {
19024            self.buffer.update(cx, |multi_buffer, cx| {
19025                project.update(cx, |project, cx| {
19026                    project.stop_language_servers_for_buffers(
19027                        multi_buffer.all_buffers().into_iter().collect(),
19028                        HashSet::default(),
19029                        cx,
19030                    );
19031                });
19032            });
19033        }
19034    }
19035
19036    fn cancel_language_server_work(
19037        workspace: &mut Workspace,
19038        _: &actions::CancelLanguageServerWork,
19039        _: &mut Window,
19040        cx: &mut Context<Workspace>,
19041    ) {
19042        let project = workspace.project();
19043        let buffers = workspace
19044            .active_item(cx)
19045            .and_then(|item| item.act_as::<Editor>(cx))
19046            .map_or(HashSet::default(), |editor| {
19047                editor.read(cx).buffer.read(cx).all_buffers()
19048            });
19049        project.update(cx, |project, cx| {
19050            project.cancel_language_server_work_for_buffers(buffers, cx);
19051        });
19052    }
19053
19054    fn show_character_palette(
19055        &mut self,
19056        _: &ShowCharacterPalette,
19057        window: &mut Window,
19058        _: &mut Context<Self>,
19059    ) {
19060        window.show_character_palette();
19061    }
19062
19063    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19064        if !self.diagnostics_enabled() {
19065            return;
19066        }
19067
19068        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19069            let buffer = self.buffer.read(cx).snapshot(cx);
19070            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19071            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19072            let is_valid = buffer
19073                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19074                .any(|entry| {
19075                    entry.diagnostic.is_primary
19076                        && !entry.range.is_empty()
19077                        && entry.range.start == primary_range_start
19078                        && entry.diagnostic.message == active_diagnostics.active_message
19079                });
19080
19081            if !is_valid {
19082                self.dismiss_diagnostics(cx);
19083            }
19084        }
19085    }
19086
19087    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19088        match &self.active_diagnostics {
19089            ActiveDiagnostic::Group(group) => Some(group),
19090            _ => None,
19091        }
19092    }
19093
19094    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19095        if !self.diagnostics_enabled() {
19096            return;
19097        }
19098        self.dismiss_diagnostics(cx);
19099        self.active_diagnostics = ActiveDiagnostic::All;
19100    }
19101
19102    fn activate_diagnostics(
19103        &mut self,
19104        buffer_id: BufferId,
19105        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19106        window: &mut Window,
19107        cx: &mut Context<Self>,
19108    ) {
19109        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19110            return;
19111        }
19112        self.dismiss_diagnostics(cx);
19113        let snapshot = self.snapshot(window, cx);
19114        let buffer = self.buffer.read(cx).snapshot(cx);
19115        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19116            return;
19117        };
19118
19119        let diagnostic_group = buffer
19120            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19121            .collect::<Vec<_>>();
19122
19123        let language_registry = self
19124            .project()
19125            .map(|project| project.read(cx).languages().clone());
19126
19127        let blocks = renderer.render_group(
19128            diagnostic_group,
19129            buffer_id,
19130            snapshot,
19131            cx.weak_entity(),
19132            language_registry,
19133            cx,
19134        );
19135
19136        let blocks = self.display_map.update(cx, |display_map, cx| {
19137            display_map.insert_blocks(blocks, cx).into_iter().collect()
19138        });
19139        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19140            active_range: buffer.anchor_before(diagnostic.range.start)
19141                ..buffer.anchor_after(diagnostic.range.end),
19142            active_message: diagnostic.diagnostic.message.clone(),
19143            group_id: diagnostic.diagnostic.group_id,
19144            blocks,
19145        });
19146        cx.notify();
19147    }
19148
19149    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19150        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19151            return;
19152        };
19153
19154        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19155        if let ActiveDiagnostic::Group(group) = prev {
19156            self.display_map.update(cx, |display_map, cx| {
19157                display_map.remove_blocks(group.blocks, cx);
19158            });
19159            cx.notify();
19160        }
19161    }
19162
19163    /// Disable inline diagnostics rendering for this editor.
19164    pub fn disable_inline_diagnostics(&mut self) {
19165        self.inline_diagnostics_enabled = false;
19166        self.inline_diagnostics_update = Task::ready(());
19167        self.inline_diagnostics.clear();
19168    }
19169
19170    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19171        self.diagnostics_enabled = false;
19172        self.dismiss_diagnostics(cx);
19173        self.inline_diagnostics_update = Task::ready(());
19174        self.inline_diagnostics.clear();
19175    }
19176
19177    pub fn disable_word_completions(&mut self) {
19178        self.word_completions_enabled = false;
19179    }
19180
19181    pub fn diagnostics_enabled(&self) -> bool {
19182        self.diagnostics_enabled && self.lsp_data_enabled()
19183    }
19184
19185    pub fn inline_diagnostics_enabled(&self) -> bool {
19186        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19187    }
19188
19189    pub fn show_inline_diagnostics(&self) -> bool {
19190        self.show_inline_diagnostics
19191    }
19192
19193    pub fn toggle_inline_diagnostics(
19194        &mut self,
19195        _: &ToggleInlineDiagnostics,
19196        window: &mut Window,
19197        cx: &mut Context<Editor>,
19198    ) {
19199        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19200        self.refresh_inline_diagnostics(false, window, cx);
19201    }
19202
19203    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19204        self.diagnostics_max_severity = severity;
19205        self.display_map.update(cx, |display_map, _| {
19206            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19207        });
19208    }
19209
19210    pub fn toggle_diagnostics(
19211        &mut self,
19212        _: &ToggleDiagnostics,
19213        window: &mut Window,
19214        cx: &mut Context<Editor>,
19215    ) {
19216        if !self.diagnostics_enabled() {
19217            return;
19218        }
19219
19220        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19221            EditorSettings::get_global(cx)
19222                .diagnostics_max_severity
19223                .filter(|severity| severity != &DiagnosticSeverity::Off)
19224                .unwrap_or(DiagnosticSeverity::Hint)
19225        } else {
19226            DiagnosticSeverity::Off
19227        };
19228        self.set_max_diagnostics_severity(new_severity, cx);
19229        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19230            self.active_diagnostics = ActiveDiagnostic::None;
19231            self.inline_diagnostics_update = Task::ready(());
19232            self.inline_diagnostics.clear();
19233        } else {
19234            self.refresh_inline_diagnostics(false, window, cx);
19235        }
19236
19237        cx.notify();
19238    }
19239
19240    pub fn toggle_minimap(
19241        &mut self,
19242        _: &ToggleMinimap,
19243        window: &mut Window,
19244        cx: &mut Context<Editor>,
19245    ) {
19246        if self.supports_minimap(cx) {
19247            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19248        }
19249    }
19250
19251    fn refresh_inline_diagnostics(
19252        &mut self,
19253        debounce: bool,
19254        window: &mut Window,
19255        cx: &mut Context<Self>,
19256    ) {
19257        let max_severity = ProjectSettings::get_global(cx)
19258            .diagnostics
19259            .inline
19260            .max_severity
19261            .unwrap_or(self.diagnostics_max_severity);
19262
19263        if !self.inline_diagnostics_enabled()
19264            || !self.diagnostics_enabled()
19265            || !self.show_inline_diagnostics
19266            || max_severity == DiagnosticSeverity::Off
19267        {
19268            self.inline_diagnostics_update = Task::ready(());
19269            self.inline_diagnostics.clear();
19270            return;
19271        }
19272
19273        let debounce_ms = ProjectSettings::get_global(cx)
19274            .diagnostics
19275            .inline
19276            .update_debounce_ms;
19277        let debounce = if debounce && debounce_ms > 0 {
19278            Some(Duration::from_millis(debounce_ms))
19279        } else {
19280            None
19281        };
19282        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19283            if let Some(debounce) = debounce {
19284                cx.background_executor().timer(debounce).await;
19285            }
19286            let Some(snapshot) = editor.upgrade().map(|editor| {
19287                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19288            }) else {
19289                return;
19290            };
19291
19292            let new_inline_diagnostics = cx
19293                .background_spawn(async move {
19294                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19295                    for diagnostic_entry in
19296                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19297                    {
19298                        let message = diagnostic_entry
19299                            .diagnostic
19300                            .message
19301                            .split_once('\n')
19302                            .map(|(line, _)| line)
19303                            .map(SharedString::new)
19304                            .unwrap_or_else(|| {
19305                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19306                            });
19307                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19308                        let (Ok(i) | Err(i)) = inline_diagnostics
19309                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19310                        inline_diagnostics.insert(
19311                            i,
19312                            (
19313                                start_anchor,
19314                                InlineDiagnostic {
19315                                    message,
19316                                    group_id: diagnostic_entry.diagnostic.group_id,
19317                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19318                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19319                                    severity: diagnostic_entry.diagnostic.severity,
19320                                },
19321                            ),
19322                        );
19323                    }
19324                    inline_diagnostics
19325                })
19326                .await;
19327
19328            editor
19329                .update(cx, |editor, cx| {
19330                    editor.inline_diagnostics = new_inline_diagnostics;
19331                    cx.notify();
19332                })
19333                .ok();
19334        });
19335    }
19336
19337    fn pull_diagnostics(
19338        &mut self,
19339        buffer_id: BufferId,
19340        _window: &Window,
19341        cx: &mut Context<Self>,
19342    ) -> Option<()> {
19343        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19344        // skip any LSP updates for it.
19345
19346        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19347            return None;
19348        }
19349        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19350            .diagnostics
19351            .lsp_pull_diagnostics;
19352        if !pull_diagnostics_settings.enabled {
19353            return None;
19354        }
19355        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19356        let project = self.project()?.downgrade();
19357        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19358
19359        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19360            cx.background_executor().timer(debounce).await;
19361            if let Ok(task) = project.update(cx, |project, cx| {
19362                project.lsp_store().update(cx, |lsp_store, cx| {
19363                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19364                })
19365            }) {
19366                task.await.log_err();
19367            }
19368            project
19369                .update(cx, |project, cx| {
19370                    project.lsp_store().update(cx, |lsp_store, cx| {
19371                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19372                    })
19373                })
19374                .log_err();
19375        });
19376
19377        Some(())
19378    }
19379
19380    pub fn set_selections_from_remote(
19381        &mut self,
19382        selections: Vec<Selection<Anchor>>,
19383        pending_selection: Option<Selection<Anchor>>,
19384        window: &mut Window,
19385        cx: &mut Context<Self>,
19386    ) {
19387        let old_cursor_position = self.selections.newest_anchor().head();
19388        self.selections
19389            .change_with(&self.display_snapshot(cx), |s| {
19390                s.select_anchors(selections);
19391                if let Some(pending_selection) = pending_selection {
19392                    s.set_pending(pending_selection, SelectMode::Character);
19393                } else {
19394                    s.clear_pending();
19395                }
19396            });
19397        self.selections_did_change(
19398            false,
19399            &old_cursor_position,
19400            SelectionEffects::default(),
19401            window,
19402            cx,
19403        );
19404    }
19405
19406    pub fn transact(
19407        &mut self,
19408        window: &mut Window,
19409        cx: &mut Context<Self>,
19410        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19411    ) -> Option<TransactionId> {
19412        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19413            this.start_transaction_at(Instant::now(), window, cx);
19414            update(this, window, cx);
19415            this.end_transaction_at(Instant::now(), cx)
19416        })
19417    }
19418
19419    pub fn start_transaction_at(
19420        &mut self,
19421        now: Instant,
19422        window: &mut Window,
19423        cx: &mut Context<Self>,
19424    ) -> Option<TransactionId> {
19425        self.end_selection(window, cx);
19426        if let Some(tx_id) = self
19427            .buffer
19428            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19429        {
19430            self.selection_history
19431                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19432            cx.emit(EditorEvent::TransactionBegun {
19433                transaction_id: tx_id,
19434            });
19435            Some(tx_id)
19436        } else {
19437            None
19438        }
19439    }
19440
19441    pub fn end_transaction_at(
19442        &mut self,
19443        now: Instant,
19444        cx: &mut Context<Self>,
19445    ) -> Option<TransactionId> {
19446        if let Some(transaction_id) = self
19447            .buffer
19448            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19449        {
19450            if let Some((_, end_selections)) =
19451                self.selection_history.transaction_mut(transaction_id)
19452            {
19453                *end_selections = Some(self.selections.disjoint_anchors_arc());
19454            } else {
19455                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19456            }
19457
19458            cx.emit(EditorEvent::Edited { transaction_id });
19459            Some(transaction_id)
19460        } else {
19461            None
19462        }
19463    }
19464
19465    pub fn modify_transaction_selection_history(
19466        &mut self,
19467        transaction_id: TransactionId,
19468        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19469    ) -> bool {
19470        self.selection_history
19471            .transaction_mut(transaction_id)
19472            .map(modify)
19473            .is_some()
19474    }
19475
19476    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19477        if self.selection_mark_mode {
19478            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19479                s.move_with(&mut |_, sel| {
19480                    sel.collapse_to(sel.head(), SelectionGoal::None);
19481                });
19482            })
19483        }
19484        self.selection_mark_mode = true;
19485        cx.notify();
19486    }
19487
19488    pub fn swap_selection_ends(
19489        &mut self,
19490        _: &actions::SwapSelectionEnds,
19491        window: &mut Window,
19492        cx: &mut Context<Self>,
19493    ) {
19494        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19495            s.move_with(&mut |_, sel| {
19496                if sel.start != sel.end {
19497                    sel.reversed = !sel.reversed
19498                }
19499            });
19500        });
19501        self.request_autoscroll(Autoscroll::newest(), cx);
19502        cx.notify();
19503    }
19504
19505    pub fn toggle_focus(
19506        workspace: &mut Workspace,
19507        _: &actions::ToggleFocus,
19508        window: &mut Window,
19509        cx: &mut Context<Workspace>,
19510    ) {
19511        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19512            return;
19513        };
19514        workspace.activate_item(&item, true, true, window, cx);
19515    }
19516
19517    pub fn toggle_fold(
19518        &mut self,
19519        _: &actions::ToggleFold,
19520        window: &mut Window,
19521        cx: &mut Context<Self>,
19522    ) {
19523        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19524            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19525            let selection = self.selections.newest::<Point>(&display_map);
19526
19527            let range = if selection.is_empty() {
19528                let point = selection.head().to_display_point(&display_map);
19529                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19530                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19531                    .to_point(&display_map);
19532                start..end
19533            } else {
19534                selection.range()
19535            };
19536            if display_map.folds_in_range(range).next().is_some() {
19537                self.unfold_lines(&Default::default(), window, cx)
19538            } else {
19539                self.fold(&Default::default(), window, cx)
19540            }
19541        } else {
19542            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19543            let buffer_ids: HashSet<_> = self
19544                .selections
19545                .disjoint_anchor_ranges()
19546                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19547                .collect();
19548
19549            let should_unfold = buffer_ids
19550                .iter()
19551                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19552
19553            for buffer_id in buffer_ids {
19554                if should_unfold {
19555                    self.unfold_buffer(buffer_id, cx);
19556                } else {
19557                    self.fold_buffer(buffer_id, cx);
19558                }
19559            }
19560        }
19561    }
19562
19563    pub fn toggle_fold_recursive(
19564        &mut self,
19565        _: &actions::ToggleFoldRecursive,
19566        window: &mut Window,
19567        cx: &mut Context<Self>,
19568    ) {
19569        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19570
19571        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19572        let range = if selection.is_empty() {
19573            let point = selection.head().to_display_point(&display_map);
19574            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19575            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19576                .to_point(&display_map);
19577            start..end
19578        } else {
19579            selection.range()
19580        };
19581        if display_map.folds_in_range(range).next().is_some() {
19582            self.unfold_recursive(&Default::default(), window, cx)
19583        } else {
19584            self.fold_recursive(&Default::default(), window, cx)
19585        }
19586    }
19587
19588    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19589        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19590            let mut to_fold = Vec::new();
19591            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19592            let selections = self.selections.all_adjusted(&display_map);
19593
19594            for selection in selections {
19595                let range = selection.range().sorted();
19596                let buffer_start_row = range.start.row;
19597
19598                if range.start.row != range.end.row {
19599                    let mut found = false;
19600                    let mut row = range.start.row;
19601                    while row <= range.end.row {
19602                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19603                        {
19604                            found = true;
19605                            row = crease.range().end.row + 1;
19606                            to_fold.push(crease);
19607                        } else {
19608                            row += 1
19609                        }
19610                    }
19611                    if found {
19612                        continue;
19613                    }
19614                }
19615
19616                for row in (0..=range.start.row).rev() {
19617                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19618                        && crease.range().end.row >= buffer_start_row
19619                    {
19620                        to_fold.push(crease);
19621                        if row <= range.start.row {
19622                            break;
19623                        }
19624                    }
19625                }
19626            }
19627
19628            self.fold_creases(to_fold, true, window, cx);
19629        } else {
19630            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19631            let buffer_ids = self
19632                .selections
19633                .disjoint_anchor_ranges()
19634                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19635                .collect::<HashSet<_>>();
19636            for buffer_id in buffer_ids {
19637                self.fold_buffer(buffer_id, cx);
19638            }
19639        }
19640    }
19641
19642    pub fn toggle_fold_all(
19643        &mut self,
19644        _: &actions::ToggleFoldAll,
19645        window: &mut Window,
19646        cx: &mut Context<Self>,
19647    ) {
19648        let has_folds = if self.buffer.read(cx).is_singleton() {
19649            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19650            let has_folds = display_map
19651                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19652                .next()
19653                .is_some();
19654            has_folds
19655        } else {
19656            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19657            let has_folds = buffer_ids
19658                .iter()
19659                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19660            has_folds
19661        };
19662
19663        if has_folds {
19664            self.unfold_all(&actions::UnfoldAll, window, cx);
19665        } else {
19666            self.fold_all(&actions::FoldAll, window, cx);
19667        }
19668    }
19669
19670    fn fold_at_level(
19671        &mut self,
19672        fold_at: &FoldAtLevel,
19673        window: &mut Window,
19674        cx: &mut Context<Self>,
19675    ) {
19676        if !self.buffer.read(cx).is_singleton() {
19677            return;
19678        }
19679
19680        let fold_at_level = fold_at.0;
19681        let snapshot = self.buffer.read(cx).snapshot(cx);
19682        let mut to_fold = Vec::new();
19683        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19684
19685        let row_ranges_to_keep: Vec<Range<u32>> = self
19686            .selections
19687            .all::<Point>(&self.display_snapshot(cx))
19688            .into_iter()
19689            .map(|sel| sel.start.row..sel.end.row)
19690            .collect();
19691
19692        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19693            while start_row < end_row {
19694                match self
19695                    .snapshot(window, cx)
19696                    .crease_for_buffer_row(MultiBufferRow(start_row))
19697                {
19698                    Some(crease) => {
19699                        let nested_start_row = crease.range().start.row + 1;
19700                        let nested_end_row = crease.range().end.row;
19701
19702                        if current_level < fold_at_level {
19703                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19704                        } else if current_level == fold_at_level {
19705                            // Fold iff there is no selection completely contained within the fold region
19706                            if !row_ranges_to_keep.iter().any(|selection| {
19707                                selection.end >= nested_start_row
19708                                    && selection.start <= nested_end_row
19709                            }) {
19710                                to_fold.push(crease);
19711                            }
19712                        }
19713
19714                        start_row = nested_end_row + 1;
19715                    }
19716                    None => start_row += 1,
19717                }
19718            }
19719        }
19720
19721        self.fold_creases(to_fold, true, window, cx);
19722    }
19723
19724    pub fn fold_at_level_1(
19725        &mut self,
19726        _: &actions::FoldAtLevel1,
19727        window: &mut Window,
19728        cx: &mut Context<Self>,
19729    ) {
19730        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
19731    }
19732
19733    pub fn fold_at_level_2(
19734        &mut self,
19735        _: &actions::FoldAtLevel2,
19736        window: &mut Window,
19737        cx: &mut Context<Self>,
19738    ) {
19739        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
19740    }
19741
19742    pub fn fold_at_level_3(
19743        &mut self,
19744        _: &actions::FoldAtLevel3,
19745        window: &mut Window,
19746        cx: &mut Context<Self>,
19747    ) {
19748        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
19749    }
19750
19751    pub fn fold_at_level_4(
19752        &mut self,
19753        _: &actions::FoldAtLevel4,
19754        window: &mut Window,
19755        cx: &mut Context<Self>,
19756    ) {
19757        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
19758    }
19759
19760    pub fn fold_at_level_5(
19761        &mut self,
19762        _: &actions::FoldAtLevel5,
19763        window: &mut Window,
19764        cx: &mut Context<Self>,
19765    ) {
19766        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
19767    }
19768
19769    pub fn fold_at_level_6(
19770        &mut self,
19771        _: &actions::FoldAtLevel6,
19772        window: &mut Window,
19773        cx: &mut Context<Self>,
19774    ) {
19775        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
19776    }
19777
19778    pub fn fold_at_level_7(
19779        &mut self,
19780        _: &actions::FoldAtLevel7,
19781        window: &mut Window,
19782        cx: &mut Context<Self>,
19783    ) {
19784        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
19785    }
19786
19787    pub fn fold_at_level_8(
19788        &mut self,
19789        _: &actions::FoldAtLevel8,
19790        window: &mut Window,
19791        cx: &mut Context<Self>,
19792    ) {
19793        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
19794    }
19795
19796    pub fn fold_at_level_9(
19797        &mut self,
19798        _: &actions::FoldAtLevel9,
19799        window: &mut Window,
19800        cx: &mut Context<Self>,
19801    ) {
19802        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
19803    }
19804
19805    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
19806        if self.buffer.read(cx).is_singleton() {
19807            let mut fold_ranges = Vec::new();
19808            let snapshot = self.buffer.read(cx).snapshot(cx);
19809
19810            for row in 0..snapshot.max_row().0 {
19811                if let Some(foldable_range) = self
19812                    .snapshot(window, cx)
19813                    .crease_for_buffer_row(MultiBufferRow(row))
19814                {
19815                    fold_ranges.push(foldable_range);
19816                }
19817            }
19818
19819            self.fold_creases(fold_ranges, true, window, cx);
19820        } else {
19821            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
19822                editor
19823                    .update_in(cx, |editor, _, cx| {
19824                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
19825                            editor.fold_buffer(buffer_id, cx);
19826                        }
19827                    })
19828                    .ok();
19829            });
19830        }
19831    }
19832
19833    pub fn fold_function_bodies(
19834        &mut self,
19835        _: &actions::FoldFunctionBodies,
19836        window: &mut Window,
19837        cx: &mut Context<Self>,
19838    ) {
19839        let snapshot = self.buffer.read(cx).snapshot(cx);
19840
19841        let ranges = snapshot
19842            .text_object_ranges(
19843                MultiBufferOffset(0)..snapshot.len(),
19844                TreeSitterOptions::default(),
19845            )
19846            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
19847            .collect::<Vec<_>>();
19848
19849        let creases = ranges
19850            .into_iter()
19851            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
19852            .collect();
19853
19854        self.fold_creases(creases, true, window, cx);
19855    }
19856
19857    pub fn fold_recursive(
19858        &mut self,
19859        _: &actions::FoldRecursive,
19860        window: &mut Window,
19861        cx: &mut Context<Self>,
19862    ) {
19863        let mut to_fold = Vec::new();
19864        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19865        let selections = self.selections.all_adjusted(&display_map);
19866
19867        for selection in selections {
19868            let range = selection.range().sorted();
19869            let buffer_start_row = range.start.row;
19870
19871            if range.start.row != range.end.row {
19872                let mut found = false;
19873                for row in range.start.row..=range.end.row {
19874                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19875                        found = true;
19876                        to_fold.push(crease);
19877                    }
19878                }
19879                if found {
19880                    continue;
19881                }
19882            }
19883
19884            for row in (0..=range.start.row).rev() {
19885                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
19886                    if crease.range().end.row >= buffer_start_row {
19887                        to_fold.push(crease);
19888                    } else {
19889                        break;
19890                    }
19891                }
19892            }
19893        }
19894
19895        self.fold_creases(to_fold, true, window, cx);
19896    }
19897
19898    pub fn fold_at(
19899        &mut self,
19900        buffer_row: MultiBufferRow,
19901        window: &mut Window,
19902        cx: &mut Context<Self>,
19903    ) {
19904        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19905
19906        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
19907            let autoscroll = self
19908                .selections
19909                .all::<Point>(&display_map)
19910                .iter()
19911                .any(|selection| crease.range().overlaps(&selection.range()));
19912
19913            self.fold_creases(vec![crease], autoscroll, window, cx);
19914        }
19915    }
19916
19917    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
19918        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19919            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19920            let buffer = display_map.buffer_snapshot();
19921            let selections = self.selections.all::<Point>(&display_map);
19922            let ranges = selections
19923                .iter()
19924                .map(|s| {
19925                    let range = s.display_range(&display_map).sorted();
19926                    let mut start = range.start.to_point(&display_map);
19927                    let mut end = range.end.to_point(&display_map);
19928                    start.column = 0;
19929                    end.column = buffer.line_len(MultiBufferRow(end.row));
19930                    start..end
19931                })
19932                .collect::<Vec<_>>();
19933
19934            self.unfold_ranges(&ranges, true, true, cx);
19935        } else {
19936            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19937            let buffer_ids = self
19938                .selections
19939                .disjoint_anchor_ranges()
19940                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19941                .collect::<HashSet<_>>();
19942            for buffer_id in buffer_ids {
19943                self.unfold_buffer(buffer_id, cx);
19944            }
19945        }
19946    }
19947
19948    pub fn unfold_recursive(
19949        &mut self,
19950        _: &UnfoldRecursive,
19951        _window: &mut Window,
19952        cx: &mut Context<Self>,
19953    ) {
19954        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19955        let selections = self.selections.all::<Point>(&display_map);
19956        let ranges = selections
19957            .iter()
19958            .map(|s| {
19959                let mut range = s.display_range(&display_map).sorted();
19960                *range.start.column_mut() = 0;
19961                *range.end.column_mut() = display_map.line_len(range.end.row());
19962                let start = range.start.to_point(&display_map);
19963                let end = range.end.to_point(&display_map);
19964                start..end
19965            })
19966            .collect::<Vec<_>>();
19967
19968        self.unfold_ranges(&ranges, true, true, cx);
19969    }
19970
19971    pub fn unfold_at(
19972        &mut self,
19973        buffer_row: MultiBufferRow,
19974        _window: &mut Window,
19975        cx: &mut Context<Self>,
19976    ) {
19977        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19978
19979        let intersection_range = Point::new(buffer_row.0, 0)
19980            ..Point::new(
19981                buffer_row.0,
19982                display_map.buffer_snapshot().line_len(buffer_row),
19983            );
19984
19985        let autoscroll = self
19986            .selections
19987            .all::<Point>(&display_map)
19988            .iter()
19989            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
19990
19991        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
19992    }
19993
19994    pub fn unfold_all(
19995        &mut self,
19996        _: &actions::UnfoldAll,
19997        _window: &mut Window,
19998        cx: &mut Context<Self>,
19999    ) {
20000        if self.buffer.read(cx).is_singleton() {
20001            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20002            self.unfold_ranges(
20003                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20004                true,
20005                true,
20006                cx,
20007            );
20008        } else {
20009            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20010                editor
20011                    .update(cx, |editor, cx| {
20012                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20013                            editor.unfold_buffer(buffer_id, cx);
20014                        }
20015                    })
20016                    .ok();
20017            });
20018        }
20019    }
20020
20021    pub fn fold_selected_ranges(
20022        &mut self,
20023        _: &FoldSelectedRanges,
20024        window: &mut Window,
20025        cx: &mut Context<Self>,
20026    ) {
20027        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20028        let selections = self.selections.all_adjusted(&display_map);
20029        let ranges = selections
20030            .into_iter()
20031            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20032            .collect::<Vec<_>>();
20033        self.fold_creases(ranges, true, window, cx);
20034    }
20035
20036    pub fn fold_ranges<T: ToOffset + Clone>(
20037        &mut self,
20038        ranges: Vec<Range<T>>,
20039        auto_scroll: bool,
20040        window: &mut Window,
20041        cx: &mut Context<Self>,
20042    ) {
20043        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20044        let ranges = ranges
20045            .into_iter()
20046            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20047            .collect::<Vec<_>>();
20048        self.fold_creases(ranges, auto_scroll, window, cx);
20049    }
20050
20051    pub fn fold_creases<T: ToOffset + Clone>(
20052        &mut self,
20053        creases: Vec<Crease<T>>,
20054        auto_scroll: bool,
20055        _window: &mut Window,
20056        cx: &mut Context<Self>,
20057    ) {
20058        if creases.is_empty() {
20059            return;
20060        }
20061
20062        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20063
20064        if auto_scroll {
20065            self.request_autoscroll(Autoscroll::fit(), cx);
20066        }
20067
20068        cx.notify();
20069
20070        self.scrollbar_marker_state.dirty = true;
20071        self.folds_did_change(cx);
20072    }
20073
20074    /// Removes any folds whose ranges intersect any of the given ranges.
20075    pub fn unfold_ranges<T: ToOffset + Clone>(
20076        &mut self,
20077        ranges: &[Range<T>],
20078        inclusive: bool,
20079        auto_scroll: bool,
20080        cx: &mut Context<Self>,
20081    ) {
20082        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20083            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20084        });
20085        self.folds_did_change(cx);
20086    }
20087
20088    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20089        self.fold_buffers([buffer_id], cx);
20090    }
20091
20092    pub fn fold_buffers(
20093        &mut self,
20094        buffer_ids: impl IntoIterator<Item = BufferId>,
20095        cx: &mut Context<Self>,
20096    ) {
20097        if self.buffer().read(cx).is_singleton() {
20098            return;
20099        }
20100
20101        let ids_to_fold: Vec<BufferId> = buffer_ids
20102            .into_iter()
20103            .filter(|id| !self.is_buffer_folded(*id, cx))
20104            .collect();
20105
20106        if ids_to_fold.is_empty() {
20107            return;
20108        }
20109
20110        let mut all_folded_excerpt_ids = Vec::new();
20111        for buffer_id in &ids_to_fold {
20112            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20113            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20114        }
20115
20116        self.display_map.update(cx, |display_map, cx| {
20117            display_map.fold_buffers(ids_to_fold.clone(), cx)
20118        });
20119
20120        let snapshot = self.display_snapshot(cx);
20121        self.selections.change_with(&snapshot, |selections| {
20122            for buffer_id in ids_to_fold {
20123                selections.remove_selections_from_buffer(buffer_id);
20124            }
20125        });
20126
20127        cx.emit(EditorEvent::BufferFoldToggled {
20128            ids: all_folded_excerpt_ids,
20129            folded: true,
20130        });
20131        cx.notify();
20132    }
20133
20134    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20135        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20136            return;
20137        }
20138        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20139        self.display_map.update(cx, |display_map, cx| {
20140            display_map.unfold_buffers([buffer_id], cx);
20141        });
20142        cx.emit(EditorEvent::BufferFoldToggled {
20143            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20144            folded: false,
20145        });
20146        cx.notify();
20147    }
20148
20149    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20150        self.display_map.read(cx).is_buffer_folded(buffer)
20151    }
20152
20153    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20154        if self.buffer().read(cx).is_singleton() {
20155            return false;
20156        }
20157        !self.folded_buffers(cx).is_empty()
20158    }
20159
20160    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20161        self.display_map.read(cx).folded_buffers()
20162    }
20163
20164    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20165        self.display_map.update(cx, |display_map, cx| {
20166            display_map.disable_header_for_buffer(buffer_id, cx);
20167        });
20168        cx.notify();
20169    }
20170
20171    /// Removes any folds with the given ranges.
20172    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20173        &mut self,
20174        ranges: &[Range<T>],
20175        type_id: TypeId,
20176        auto_scroll: bool,
20177        cx: &mut Context<Self>,
20178    ) {
20179        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20180            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20181        });
20182        self.folds_did_change(cx);
20183    }
20184
20185    fn remove_folds_with<T: ToOffset + Clone>(
20186        &mut self,
20187        ranges: &[Range<T>],
20188        auto_scroll: bool,
20189        cx: &mut Context<Self>,
20190        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20191    ) {
20192        if ranges.is_empty() {
20193            return;
20194        }
20195
20196        let mut buffers_affected = HashSet::default();
20197        let multi_buffer = self.buffer().read(cx);
20198        for range in ranges {
20199            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20200                buffers_affected.insert(buffer.read(cx).remote_id());
20201            };
20202        }
20203
20204        self.display_map.update(cx, update);
20205
20206        if auto_scroll {
20207            self.request_autoscroll(Autoscroll::fit(), cx);
20208        }
20209
20210        cx.notify();
20211        self.scrollbar_marker_state.dirty = true;
20212        self.active_indent_guides_state.dirty = true;
20213    }
20214
20215    pub fn update_renderer_widths(
20216        &mut self,
20217        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20218        cx: &mut Context<Self>,
20219    ) -> bool {
20220        self.display_map
20221            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20222    }
20223
20224    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20225        self.display_map.read(cx).fold_placeholder.clone()
20226    }
20227
20228    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20229        self.buffer.update(cx, |buffer, cx| {
20230            buffer.set_all_diff_hunks_expanded(cx);
20231        });
20232    }
20233
20234    pub fn expand_all_diff_hunks(
20235        &mut self,
20236        _: &ExpandAllDiffHunks,
20237        _window: &mut Window,
20238        cx: &mut Context<Self>,
20239    ) {
20240        self.buffer.update(cx, |buffer, cx| {
20241            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20242        });
20243    }
20244
20245    pub fn collapse_all_diff_hunks(
20246        &mut self,
20247        _: &CollapseAllDiffHunks,
20248        _window: &mut Window,
20249        cx: &mut Context<Self>,
20250    ) {
20251        self.buffer.update(cx, |buffer, cx| {
20252            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20253        });
20254    }
20255
20256    pub fn toggle_selected_diff_hunks(
20257        &mut self,
20258        _: &ToggleSelectedDiffHunks,
20259        _window: &mut Window,
20260        cx: &mut Context<Self>,
20261    ) {
20262        let ranges: Vec<_> = self
20263            .selections
20264            .disjoint_anchors()
20265            .iter()
20266            .map(|s| s.range())
20267            .collect();
20268        self.toggle_diff_hunks_in_ranges(ranges, cx);
20269    }
20270
20271    pub fn diff_hunks_in_ranges<'a>(
20272        &'a self,
20273        ranges: &'a [Range<Anchor>],
20274        buffer: &'a MultiBufferSnapshot,
20275    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20276        ranges.iter().flat_map(move |range| {
20277            let end_excerpt_id = range.end.excerpt_id;
20278            let range = range.to_point(buffer);
20279            let mut peek_end = range.end;
20280            if range.end.row < buffer.max_row().0 {
20281                peek_end = Point::new(range.end.row + 1, 0);
20282            }
20283            buffer
20284                .diff_hunks_in_range(range.start..peek_end)
20285                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20286        })
20287    }
20288
20289    pub fn has_stageable_diff_hunks_in_ranges(
20290        &self,
20291        ranges: &[Range<Anchor>],
20292        snapshot: &MultiBufferSnapshot,
20293    ) -> bool {
20294        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20295        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20296    }
20297
20298    pub fn toggle_staged_selected_diff_hunks(
20299        &mut self,
20300        _: &::git::ToggleStaged,
20301        _: &mut Window,
20302        cx: &mut Context<Self>,
20303    ) {
20304        let snapshot = self.buffer.read(cx).snapshot(cx);
20305        let ranges: Vec<_> = self
20306            .selections
20307            .disjoint_anchors()
20308            .iter()
20309            .map(|s| s.range())
20310            .collect();
20311        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20312        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20313    }
20314
20315    pub fn set_render_diff_hunk_controls(
20316        &mut self,
20317        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20318        cx: &mut Context<Self>,
20319    ) {
20320        self.render_diff_hunk_controls = render_diff_hunk_controls;
20321        cx.notify();
20322    }
20323
20324    pub fn stage_and_next(
20325        &mut self,
20326        _: &::git::StageAndNext,
20327        window: &mut Window,
20328        cx: &mut Context<Self>,
20329    ) {
20330        self.do_stage_or_unstage_and_next(true, window, cx);
20331    }
20332
20333    pub fn unstage_and_next(
20334        &mut self,
20335        _: &::git::UnstageAndNext,
20336        window: &mut Window,
20337        cx: &mut Context<Self>,
20338    ) {
20339        self.do_stage_or_unstage_and_next(false, window, cx);
20340    }
20341
20342    pub fn stage_or_unstage_diff_hunks(
20343        &mut self,
20344        stage: bool,
20345        ranges: Vec<Range<Anchor>>,
20346        cx: &mut Context<Self>,
20347    ) {
20348        if self.delegate_stage_and_restore {
20349            let snapshot = self.buffer.read(cx).snapshot(cx);
20350            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20351            if !hunks.is_empty() {
20352                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20353            }
20354            return;
20355        }
20356        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20357        cx.spawn(async move |this, cx| {
20358            task.await?;
20359            this.update(cx, |this, cx| {
20360                let snapshot = this.buffer.read(cx).snapshot(cx);
20361                let chunk_by = this
20362                    .diff_hunks_in_ranges(&ranges, &snapshot)
20363                    .chunk_by(|hunk| hunk.buffer_id);
20364                for (buffer_id, hunks) in &chunk_by {
20365                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20366                }
20367            })
20368        })
20369        .detach_and_log_err(cx);
20370    }
20371
20372    fn save_buffers_for_ranges_if_needed(
20373        &mut self,
20374        ranges: &[Range<Anchor>],
20375        cx: &mut Context<Editor>,
20376    ) -> Task<Result<()>> {
20377        let multibuffer = self.buffer.read(cx);
20378        let snapshot = multibuffer.read(cx);
20379        let buffer_ids: HashSet<_> = ranges
20380            .iter()
20381            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20382            .collect();
20383        drop(snapshot);
20384
20385        let mut buffers = HashSet::default();
20386        for buffer_id in buffer_ids {
20387            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20388                let buffer = buffer_entity.read(cx);
20389                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20390                {
20391                    buffers.insert(buffer_entity);
20392                }
20393            }
20394        }
20395
20396        if let Some(project) = &self.project {
20397            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20398        } else {
20399            Task::ready(Ok(()))
20400        }
20401    }
20402
20403    fn do_stage_or_unstage_and_next(
20404        &mut self,
20405        stage: bool,
20406        window: &mut Window,
20407        cx: &mut Context<Self>,
20408    ) {
20409        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20410
20411        if ranges.iter().any(|range| range.start != range.end) {
20412            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20413            return;
20414        }
20415
20416        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20417
20418        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20419        let wrap_around = !all_diff_hunks_expanded;
20420        let snapshot = self.snapshot(window, cx);
20421        let position = self
20422            .selections
20423            .newest::<Point>(&snapshot.display_snapshot)
20424            .head();
20425
20426        self.go_to_hunk_before_or_after_position(
20427            &snapshot,
20428            position,
20429            Direction::Next,
20430            wrap_around,
20431            window,
20432            cx,
20433        );
20434    }
20435
20436    pub(crate) fn do_stage_or_unstage(
20437        &self,
20438        stage: bool,
20439        buffer_id: BufferId,
20440        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20441        cx: &mut App,
20442    ) -> Option<()> {
20443        let project = self.project()?;
20444        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20445        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20446        let buffer_snapshot = buffer.read(cx).snapshot();
20447        let file_exists = buffer_snapshot
20448            .file()
20449            .is_some_and(|file| file.disk_state().exists());
20450        diff.update(cx, |diff, cx| {
20451            diff.stage_or_unstage_hunks(
20452                stage,
20453                &hunks
20454                    .map(|hunk| buffer_diff::DiffHunk {
20455                        buffer_range: hunk.buffer_range,
20456                        // We don't need to pass in word diffs here because they're only used for rendering and
20457                        // this function changes internal state
20458                        base_word_diffs: Vec::default(),
20459                        buffer_word_diffs: Vec::default(),
20460                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20461                            ..hunk.diff_base_byte_range.end.0,
20462                        secondary_status: hunk.status.secondary,
20463                        range: Point::zero()..Point::zero(), // unused
20464                    })
20465                    .collect::<Vec<_>>(),
20466                &buffer_snapshot,
20467                file_exists,
20468                cx,
20469            )
20470        });
20471        None
20472    }
20473
20474    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20475        let ranges: Vec<_> = self
20476            .selections
20477            .disjoint_anchors()
20478            .iter()
20479            .map(|s| s.range())
20480            .collect();
20481        self.buffer
20482            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20483    }
20484
20485    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20486        self.buffer.update(cx, |buffer, cx| {
20487            let ranges = vec![Anchor::min()..Anchor::max()];
20488            if !buffer.all_diff_hunks_expanded()
20489                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20490            {
20491                buffer.collapse_diff_hunks(ranges, cx);
20492                true
20493            } else {
20494                false
20495            }
20496        })
20497    }
20498
20499    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20500        if self.buffer.read(cx).all_diff_hunks_expanded() {
20501            return true;
20502        }
20503        let ranges = vec![Anchor::min()..Anchor::max()];
20504        self.buffer
20505            .read(cx)
20506            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20507    }
20508
20509    fn toggle_diff_hunks_in_ranges(
20510        &mut self,
20511        ranges: Vec<Range<Anchor>>,
20512        cx: &mut Context<Editor>,
20513    ) {
20514        self.buffer.update(cx, |buffer, cx| {
20515            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20516            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20517        })
20518    }
20519
20520    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20521        self.buffer.update(cx, |buffer, cx| {
20522            buffer.toggle_single_diff_hunk(range, cx);
20523        })
20524    }
20525
20526    pub(crate) fn apply_all_diff_hunks(
20527        &mut self,
20528        _: &ApplyAllDiffHunks,
20529        window: &mut Window,
20530        cx: &mut Context<Self>,
20531    ) {
20532        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20533
20534        let buffers = self.buffer.read(cx).all_buffers();
20535        for branch_buffer in buffers {
20536            branch_buffer.update(cx, |branch_buffer, cx| {
20537                branch_buffer.merge_into_base(Vec::new(), cx);
20538            });
20539        }
20540
20541        if let Some(project) = self.project.clone() {
20542            self.save(
20543                SaveOptions {
20544                    format: true,
20545                    autosave: false,
20546                },
20547                project,
20548                window,
20549                cx,
20550            )
20551            .detach_and_log_err(cx);
20552        }
20553    }
20554
20555    pub(crate) fn apply_selected_diff_hunks(
20556        &mut self,
20557        _: &ApplyDiffHunk,
20558        window: &mut Window,
20559        cx: &mut Context<Self>,
20560    ) {
20561        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20562        let snapshot = self.snapshot(window, cx);
20563        let hunks = snapshot.hunks_for_ranges(
20564            self.selections
20565                .all(&snapshot.display_snapshot)
20566                .into_iter()
20567                .map(|selection| selection.range()),
20568        );
20569        let mut ranges_by_buffer = HashMap::default();
20570        self.transact(window, cx, |editor, _window, cx| {
20571            for hunk in hunks {
20572                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20573                    ranges_by_buffer
20574                        .entry(buffer.clone())
20575                        .or_insert_with(Vec::new)
20576                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20577                }
20578            }
20579
20580            for (buffer, ranges) in ranges_by_buffer {
20581                buffer.update(cx, |buffer, cx| {
20582                    buffer.merge_into_base(ranges, cx);
20583                });
20584            }
20585        });
20586
20587        if let Some(project) = self.project.clone() {
20588            self.save(
20589                SaveOptions {
20590                    format: true,
20591                    autosave: false,
20592                },
20593                project,
20594                window,
20595                cx,
20596            )
20597            .detach_and_log_err(cx);
20598        }
20599    }
20600
20601    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20602        if hovered != self.gutter_hovered {
20603            self.gutter_hovered = hovered;
20604            cx.notify();
20605        }
20606    }
20607
20608    pub fn insert_blocks(
20609        &mut self,
20610        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20611        autoscroll: Option<Autoscroll>,
20612        cx: &mut Context<Self>,
20613    ) -> Vec<CustomBlockId> {
20614        let blocks = self
20615            .display_map
20616            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20617        if let Some(autoscroll) = autoscroll {
20618            self.request_autoscroll(autoscroll, cx);
20619        }
20620        cx.notify();
20621        blocks
20622    }
20623
20624    pub fn resize_blocks(
20625        &mut self,
20626        heights: HashMap<CustomBlockId, u32>,
20627        autoscroll: Option<Autoscroll>,
20628        cx: &mut Context<Self>,
20629    ) {
20630        self.display_map
20631            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20632        if let Some(autoscroll) = autoscroll {
20633            self.request_autoscroll(autoscroll, cx);
20634        }
20635        cx.notify();
20636    }
20637
20638    pub fn replace_blocks(
20639        &mut self,
20640        renderers: HashMap<CustomBlockId, RenderBlock>,
20641        autoscroll: Option<Autoscroll>,
20642        cx: &mut Context<Self>,
20643    ) {
20644        self.display_map
20645            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20646        if let Some(autoscroll) = autoscroll {
20647            self.request_autoscroll(autoscroll, cx);
20648        }
20649        cx.notify();
20650    }
20651
20652    pub fn remove_blocks(
20653        &mut self,
20654        block_ids: HashSet<CustomBlockId>,
20655        autoscroll: Option<Autoscroll>,
20656        cx: &mut Context<Self>,
20657    ) {
20658        self.display_map.update(cx, |display_map, cx| {
20659            display_map.remove_blocks(block_ids, cx)
20660        });
20661        if let Some(autoscroll) = autoscroll {
20662            self.request_autoscroll(autoscroll, cx);
20663        }
20664        cx.notify();
20665    }
20666
20667    pub fn row_for_block(
20668        &self,
20669        block_id: CustomBlockId,
20670        cx: &mut Context<Self>,
20671    ) -> Option<DisplayRow> {
20672        self.display_map
20673            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20674    }
20675
20676    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20677        self.focused_block = Some(focused_block);
20678    }
20679
20680    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20681        self.focused_block.take()
20682    }
20683
20684    pub fn insert_creases(
20685        &mut self,
20686        creases: impl IntoIterator<Item = Crease<Anchor>>,
20687        cx: &mut Context<Self>,
20688    ) -> Vec<CreaseId> {
20689        self.display_map
20690            .update(cx, |map, cx| map.insert_creases(creases, cx))
20691    }
20692
20693    pub fn remove_creases(
20694        &mut self,
20695        ids: impl IntoIterator<Item = CreaseId>,
20696        cx: &mut Context<Self>,
20697    ) -> Vec<(CreaseId, Range<Anchor>)> {
20698        self.display_map
20699            .update(cx, |map, cx| map.remove_creases(ids, cx))
20700    }
20701
20702    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
20703        self.display_map
20704            .update(cx, |map, cx| map.snapshot(cx))
20705            .longest_row()
20706    }
20707
20708    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
20709        self.display_map
20710            .update(cx, |map, cx| map.snapshot(cx))
20711            .max_point()
20712    }
20713
20714    pub fn text(&self, cx: &App) -> String {
20715        self.buffer.read(cx).read(cx).text()
20716    }
20717
20718    pub fn is_empty(&self, cx: &App) -> bool {
20719        self.buffer.read(cx).read(cx).is_empty()
20720    }
20721
20722    pub fn text_option(&self, cx: &App) -> Option<String> {
20723        let text = self.text(cx);
20724        let text = text.trim();
20725
20726        if text.is_empty() {
20727            return None;
20728        }
20729
20730        Some(text.to_string())
20731    }
20732
20733    pub fn set_text(
20734        &mut self,
20735        text: impl Into<Arc<str>>,
20736        window: &mut Window,
20737        cx: &mut Context<Self>,
20738    ) {
20739        self.transact(window, cx, |this, _, cx| {
20740            this.buffer
20741                .read(cx)
20742                .as_singleton()
20743                .expect("you can only call set_text on editors for singleton buffers")
20744                .update(cx, |buffer, cx| buffer.set_text(text, cx));
20745        });
20746    }
20747
20748    pub fn display_text(&self, cx: &mut App) -> String {
20749        self.display_map
20750            .update(cx, |map, cx| map.snapshot(cx))
20751            .text()
20752    }
20753
20754    fn create_minimap(
20755        &self,
20756        minimap_settings: MinimapSettings,
20757        window: &mut Window,
20758        cx: &mut Context<Self>,
20759    ) -> Option<Entity<Self>> {
20760        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
20761            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
20762    }
20763
20764    fn initialize_new_minimap(
20765        &self,
20766        minimap_settings: MinimapSettings,
20767        window: &mut Window,
20768        cx: &mut Context<Self>,
20769    ) -> Entity<Self> {
20770        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
20771        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
20772
20773        let mut minimap = Editor::new_internal(
20774            EditorMode::Minimap {
20775                parent: cx.weak_entity(),
20776            },
20777            self.buffer.clone(),
20778            None,
20779            Some(self.display_map.clone()),
20780            window,
20781            cx,
20782        );
20783        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20784        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
20785        minimap.scroll_manager.clone_state(
20786            &self.scroll_manager,
20787            &my_snapshot,
20788            &minimap_snapshot,
20789            cx,
20790        );
20791        minimap.set_text_style_refinement(TextStyleRefinement {
20792            font_size: Some(MINIMAP_FONT_SIZE),
20793            font_weight: Some(MINIMAP_FONT_WEIGHT),
20794            font_family: Some(MINIMAP_FONT_FAMILY),
20795            ..Default::default()
20796        });
20797        minimap.update_minimap_configuration(minimap_settings, cx);
20798        cx.new(|_| minimap)
20799    }
20800
20801    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
20802        let current_line_highlight = minimap_settings
20803            .current_line_highlight
20804            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
20805        self.set_current_line_highlight(Some(current_line_highlight));
20806    }
20807
20808    pub fn minimap(&self) -> Option<&Entity<Self>> {
20809        self.minimap
20810            .as_ref()
20811            .filter(|_| self.minimap_visibility.visible())
20812    }
20813
20814    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
20815        let mut wrap_guides = smallvec![];
20816
20817        if self.show_wrap_guides == Some(false) {
20818            return wrap_guides;
20819        }
20820
20821        let settings = self.buffer.read(cx).language_settings(cx);
20822        if settings.show_wrap_guides {
20823            match self.soft_wrap_mode(cx) {
20824                SoftWrap::Column(soft_wrap) => {
20825                    wrap_guides.push((soft_wrap as usize, true));
20826                }
20827                SoftWrap::Bounded(soft_wrap) => {
20828                    wrap_guides.push((soft_wrap as usize, true));
20829                }
20830                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
20831            }
20832            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
20833        }
20834
20835        wrap_guides
20836    }
20837
20838    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
20839        let settings = self.buffer.read(cx).language_settings(cx);
20840        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
20841        match mode {
20842            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
20843                SoftWrap::None
20844            }
20845            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
20846            language_settings::SoftWrap::PreferredLineLength => {
20847                SoftWrap::Column(settings.preferred_line_length)
20848            }
20849            language_settings::SoftWrap::Bounded => {
20850                SoftWrap::Bounded(settings.preferred_line_length)
20851            }
20852        }
20853    }
20854
20855    pub fn set_soft_wrap_mode(
20856        &mut self,
20857        mode: language_settings::SoftWrap,
20858        cx: &mut Context<Self>,
20859    ) {
20860        self.soft_wrap_mode_override = Some(mode);
20861        cx.notify();
20862    }
20863
20864    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
20865        self.hard_wrap = hard_wrap;
20866        cx.notify();
20867    }
20868
20869    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
20870        self.text_style_refinement = Some(style);
20871    }
20872
20873    /// called by the Element so we know what style we were most recently rendered with.
20874    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
20875        // We intentionally do not inform the display map about the minimap style
20876        // so that wrapping is not recalculated and stays consistent for the editor
20877        // and its linked minimap.
20878        if !self.mode.is_minimap() {
20879            let font = style.text.font();
20880            let font_size = style.text.font_size.to_pixels(window.rem_size());
20881            let display_map = self
20882                .placeholder_display_map
20883                .as_ref()
20884                .filter(|_| self.is_empty(cx))
20885                .unwrap_or(&self.display_map);
20886
20887            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
20888        }
20889        self.style = Some(style);
20890    }
20891
20892    pub fn style(&mut self, cx: &App) -> &EditorStyle {
20893        if self.style.is_none() {
20894            self.style = Some(self.create_style(cx));
20895        }
20896        self.style.as_ref().unwrap()
20897    }
20898
20899    // Called by the element. This method is not designed to be called outside of the editor
20900    // element's layout code because it does not notify when rewrapping is computed synchronously.
20901    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
20902        if self.is_empty(cx) {
20903            self.placeholder_display_map
20904                .as_ref()
20905                .map_or(false, |display_map| {
20906                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
20907                })
20908        } else {
20909            self.display_map
20910                .update(cx, |map, cx| map.set_wrap_width(width, cx))
20911        }
20912    }
20913
20914    pub fn set_soft_wrap(&mut self) {
20915        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
20916    }
20917
20918    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
20919        if self.soft_wrap_mode_override.is_some() {
20920            self.soft_wrap_mode_override.take();
20921        } else {
20922            let soft_wrap = match self.soft_wrap_mode(cx) {
20923                SoftWrap::GitDiff => return,
20924                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
20925                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
20926                    language_settings::SoftWrap::None
20927                }
20928            };
20929            self.soft_wrap_mode_override = Some(soft_wrap);
20930        }
20931        cx.notify();
20932    }
20933
20934    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
20935        let Some(workspace) = self.workspace() else {
20936            return;
20937        };
20938        let fs = workspace.read(cx).app_state().fs.clone();
20939        let current_show = TabBarSettings::get_global(cx).show;
20940        update_settings_file(fs, cx, move |setting, _| {
20941            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
20942        });
20943    }
20944
20945    pub fn toggle_indent_guides(
20946        &mut self,
20947        _: &ToggleIndentGuides,
20948        _: &mut Window,
20949        cx: &mut Context<Self>,
20950    ) {
20951        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
20952            self.buffer
20953                .read(cx)
20954                .language_settings(cx)
20955                .indent_guides
20956                .enabled
20957        });
20958        self.show_indent_guides = Some(!currently_enabled);
20959        cx.notify();
20960    }
20961
20962    fn should_show_indent_guides(&self) -> Option<bool> {
20963        self.show_indent_guides
20964    }
20965
20966    pub fn disable_indent_guides_for_buffer(
20967        &mut self,
20968        buffer_id: BufferId,
20969        cx: &mut Context<Self>,
20970    ) {
20971        self.buffers_with_disabled_indent_guides.insert(buffer_id);
20972        cx.notify();
20973    }
20974
20975    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
20976        self.buffers_with_disabled_indent_guides
20977            .contains(&buffer_id)
20978    }
20979
20980    pub fn toggle_line_numbers(
20981        &mut self,
20982        _: &ToggleLineNumbers,
20983        _: &mut Window,
20984        cx: &mut Context<Self>,
20985    ) {
20986        let mut editor_settings = EditorSettings::get_global(cx).clone();
20987        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
20988        EditorSettings::override_global(editor_settings, cx);
20989    }
20990
20991    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
20992        if let Some(show_line_numbers) = self.show_line_numbers {
20993            return show_line_numbers;
20994        }
20995        EditorSettings::get_global(cx).gutter.line_numbers
20996    }
20997
20998    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
20999        match (
21000            self.use_relative_line_numbers,
21001            EditorSettings::get_global(cx).relative_line_numbers,
21002        ) {
21003            (None, setting) => setting,
21004            (Some(false), _) => RelativeLineNumbers::Disabled,
21005            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21006            (Some(true), _) => RelativeLineNumbers::Enabled,
21007        }
21008    }
21009
21010    pub fn toggle_relative_line_numbers(
21011        &mut self,
21012        _: &ToggleRelativeLineNumbers,
21013        _: &mut Window,
21014        cx: &mut Context<Self>,
21015    ) {
21016        let is_relative = self.relative_line_numbers(cx);
21017        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21018    }
21019
21020    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21021        self.use_relative_line_numbers = is_relative;
21022        cx.notify();
21023    }
21024
21025    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21026        self.show_gutter = show_gutter;
21027        cx.notify();
21028    }
21029
21030    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21031        self.show_scrollbars = ScrollbarAxes {
21032            horizontal: show,
21033            vertical: show,
21034        };
21035        cx.notify();
21036    }
21037
21038    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21039        self.show_scrollbars.vertical = show;
21040        cx.notify();
21041    }
21042
21043    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21044        self.show_scrollbars.horizontal = show;
21045        cx.notify();
21046    }
21047
21048    pub fn set_minimap_visibility(
21049        &mut self,
21050        minimap_visibility: MinimapVisibility,
21051        window: &mut Window,
21052        cx: &mut Context<Self>,
21053    ) {
21054        if self.minimap_visibility != minimap_visibility {
21055            if minimap_visibility.visible() && self.minimap.is_none() {
21056                let minimap_settings = EditorSettings::get_global(cx).minimap;
21057                self.minimap =
21058                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21059            }
21060            self.minimap_visibility = minimap_visibility;
21061            cx.notify();
21062        }
21063    }
21064
21065    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21066        self.set_show_scrollbars(false, cx);
21067        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21068    }
21069
21070    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21071        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21072    }
21073
21074    /// Normally the text in full mode and auto height editors is padded on the
21075    /// left side by roughly half a character width for improved hit testing.
21076    ///
21077    /// Use this method to disable this for cases where this is not wanted (e.g.
21078    /// if you want to align the editor text with some other text above or below)
21079    /// or if you want to add this padding to single-line editors.
21080    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21081        self.offset_content = offset_content;
21082        cx.notify();
21083    }
21084
21085    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21086        self.show_line_numbers = Some(show_line_numbers);
21087        cx.notify();
21088    }
21089
21090    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21091        self.disable_expand_excerpt_buttons = true;
21092        cx.notify();
21093    }
21094
21095    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21096        self.number_deleted_lines = number;
21097        cx.notify();
21098    }
21099
21100    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21101        self.delegate_expand_excerpts = delegate;
21102    }
21103
21104    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21105        self.delegate_stage_and_restore = delegate;
21106    }
21107
21108    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21109        self.delegate_open_excerpts = delegate;
21110    }
21111
21112    pub fn set_on_local_selections_changed(
21113        &mut self,
21114        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21115    ) {
21116        self.on_local_selections_changed = callback;
21117    }
21118
21119    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21120        self.suppress_selection_callback = suppress;
21121    }
21122
21123    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21124        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21125        cx.notify();
21126    }
21127
21128    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21129        self.show_code_actions = Some(show_code_actions);
21130        cx.notify();
21131    }
21132
21133    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21134        self.show_runnables = Some(show_runnables);
21135        cx.notify();
21136    }
21137
21138    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21139        self.show_breakpoints = Some(show_breakpoints);
21140        cx.notify();
21141    }
21142
21143    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21144        self.show_diff_review_button = show;
21145        cx.notify();
21146    }
21147
21148    pub fn show_diff_review_button(&self) -> bool {
21149        self.show_diff_review_button
21150    }
21151
21152    pub fn render_diff_review_button(
21153        &self,
21154        display_row: DisplayRow,
21155        width: Pixels,
21156        cx: &mut Context<Self>,
21157    ) -> impl IntoElement {
21158        let text_color = cx.theme().colors().text;
21159        let icon_color = cx.theme().colors().icon_accent;
21160
21161        h_flex()
21162            .id("diff_review_button")
21163            .cursor_pointer()
21164            .w(width - px(1.))
21165            .h(relative(0.9))
21166            .justify_center()
21167            .rounded_sm()
21168            .border_1()
21169            .border_color(text_color.opacity(0.1))
21170            .bg(text_color.opacity(0.15))
21171            .hover(|s| {
21172                s.bg(icon_color.opacity(0.4))
21173                    .border_color(icon_color.opacity(0.5))
21174            })
21175            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21176            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21177            .on_mouse_down(
21178                gpui::MouseButton::Left,
21179                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21180                    editor.start_diff_review_drag(display_row, window, cx);
21181                }),
21182            )
21183    }
21184
21185    pub fn start_diff_review_drag(
21186        &mut self,
21187        display_row: DisplayRow,
21188        window: &mut Window,
21189        cx: &mut Context<Self>,
21190    ) {
21191        let snapshot = self.snapshot(window, cx);
21192        let point = snapshot
21193            .display_snapshot
21194            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21195        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21196        self.diff_review_drag_state = Some(DiffReviewDragState {
21197            start_anchor: anchor,
21198            current_anchor: anchor,
21199        });
21200        cx.notify();
21201    }
21202
21203    pub fn update_diff_review_drag(
21204        &mut self,
21205        display_row: DisplayRow,
21206        window: &mut Window,
21207        cx: &mut Context<Self>,
21208    ) {
21209        if self.diff_review_drag_state.is_none() {
21210            return;
21211        }
21212        let snapshot = self.snapshot(window, cx);
21213        let point = snapshot
21214            .display_snapshot
21215            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21216        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21217        if let Some(drag_state) = &mut self.diff_review_drag_state {
21218            drag_state.current_anchor = anchor;
21219            cx.notify();
21220        }
21221    }
21222
21223    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21224        if let Some(drag_state) = self.diff_review_drag_state.take() {
21225            let snapshot = self.snapshot(window, cx);
21226            let range = drag_state.row_range(&snapshot.display_snapshot);
21227            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21228        }
21229        cx.notify();
21230    }
21231
21232    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21233        self.diff_review_drag_state = None;
21234        cx.notify();
21235    }
21236
21237    /// Calculates the appropriate block height for the diff review overlay.
21238    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21239    /// and 2 lines per comment when expanded.
21240    fn calculate_overlay_height(
21241        &self,
21242        hunk_key: &DiffHunkKey,
21243        comments_expanded: bool,
21244        snapshot: &MultiBufferSnapshot,
21245    ) -> u32 {
21246        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21247        let base_height: u32 = 2; // Input row with avatar and buttons
21248
21249        if comment_count == 0 {
21250            base_height
21251        } else if comments_expanded {
21252            // Header (1 line) + 2 lines per comment
21253            base_height + 1 + (comment_count as u32 * 2)
21254        } else {
21255            // Just header when collapsed
21256            base_height + 1
21257        }
21258    }
21259
21260    pub fn show_diff_review_overlay(
21261        &mut self,
21262        display_range: Range<DisplayRow>,
21263        window: &mut Window,
21264        cx: &mut Context<Self>,
21265    ) {
21266        let Range { start, end } = display_range.sorted();
21267
21268        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21269        let editor_snapshot = self.snapshot(window, cx);
21270
21271        // Convert display rows to multibuffer points
21272        let start_point = editor_snapshot
21273            .display_snapshot
21274            .display_point_to_point(start.as_display_point(), Bias::Left);
21275        let end_point = editor_snapshot
21276            .display_snapshot
21277            .display_point_to_point(end.as_display_point(), Bias::Left);
21278        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21279
21280        // Create anchor range for the selected lines (start of first line to end of last line)
21281        let line_end = Point::new(
21282            end_point.row,
21283            buffer_snapshot.line_len(end_multi_buffer_row),
21284        );
21285        let anchor_range =
21286            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21287
21288        // Compute the hunk key for this display row
21289        let file_path = buffer_snapshot
21290            .file_at(start_point)
21291            .map(|file: &Arc<dyn language::File>| file.path().clone())
21292            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21293        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21294        let new_hunk_key = DiffHunkKey {
21295            file_path,
21296            hunk_start_anchor,
21297        };
21298
21299        // Check if we already have an overlay for this hunk
21300        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21301            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21302        }) {
21303            // Just focus the existing overlay's prompt editor
21304            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21305            window.focus(&focus_handle, cx);
21306            return;
21307        }
21308
21309        // Dismiss overlays that have no comments for their hunks
21310        self.dismiss_overlays_without_comments(cx);
21311
21312        // Get the current user's avatar URI from the project's user_store
21313        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21314            let user_store = project.read(cx).user_store();
21315            user_store
21316                .read(cx)
21317                .current_user()
21318                .map(|user| user.avatar_uri.clone())
21319        });
21320
21321        // Create anchor at the end of the last row so the block appears immediately below it
21322        // Use multibuffer coordinates for anchor creation
21323        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21324        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21325
21326        // Use the hunk key we already computed
21327        let hunk_key = new_hunk_key;
21328
21329        // Create the prompt editor for the review input
21330        let prompt_editor = cx.new(|cx| {
21331            let mut editor = Editor::single_line(window, cx);
21332            editor.set_placeholder_text("Add a review comment...", window, cx);
21333            editor
21334        });
21335
21336        // Register the Newline action on the prompt editor to submit the review
21337        let parent_editor = cx.entity().downgrade();
21338        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21339            prompt_editor.register_action({
21340                let parent_editor = parent_editor.clone();
21341                move |_: &crate::actions::Newline, window, cx| {
21342                    if let Some(editor) = parent_editor.upgrade() {
21343                        editor.update(cx, |editor, cx| {
21344                            editor.submit_diff_review_comment(window, cx);
21345                        });
21346                    }
21347                }
21348            })
21349        });
21350
21351        // Calculate initial height based on existing comments for this hunk
21352        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21353
21354        // Create the overlay block
21355        let prompt_editor_for_render = prompt_editor.clone();
21356        let hunk_key_for_render = hunk_key.clone();
21357        let editor_handle = cx.entity().downgrade();
21358        let block = BlockProperties {
21359            style: BlockStyle::Sticky,
21360            placement: BlockPlacement::Below(anchor),
21361            height: Some(initial_height),
21362            render: Arc::new(move |cx| {
21363                Self::render_diff_review_overlay(
21364                    &prompt_editor_for_render,
21365                    &hunk_key_for_render,
21366                    &editor_handle,
21367                    cx,
21368                )
21369            }),
21370            priority: 0,
21371        };
21372
21373        let block_ids = self.insert_blocks([block], None, cx);
21374        let Some(block_id) = block_ids.into_iter().next() else {
21375            log::error!("Failed to insert diff review overlay block");
21376            return;
21377        };
21378
21379        self.diff_review_overlays.push(DiffReviewOverlay {
21380            anchor_range,
21381            block_id,
21382            prompt_editor: prompt_editor.clone(),
21383            hunk_key,
21384            comments_expanded: true,
21385            inline_edit_editors: HashMap::default(),
21386            inline_edit_subscriptions: HashMap::default(),
21387            user_avatar_uri,
21388            _subscription: subscription,
21389        });
21390
21391        // Focus the prompt editor
21392        let focus_handle = prompt_editor.focus_handle(cx);
21393        window.focus(&focus_handle, cx);
21394
21395        cx.notify();
21396    }
21397
21398    /// Dismisses all diff review overlays.
21399    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21400        if self.diff_review_overlays.is_empty() {
21401            return;
21402        }
21403        let block_ids: HashSet<_> = self
21404            .diff_review_overlays
21405            .drain(..)
21406            .map(|overlay| overlay.block_id)
21407            .collect();
21408        self.remove_blocks(block_ids, None, cx);
21409        cx.notify();
21410    }
21411
21412    /// Dismisses overlays that have no comments stored for their hunks.
21413    /// Keeps overlays that have at least one comment.
21414    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21415        let snapshot = self.buffer.read(cx).snapshot(cx);
21416
21417        // First, compute which overlays have comments (to avoid borrow issues with retain)
21418        let overlays_with_comments: Vec<bool> = self
21419            .diff_review_overlays
21420            .iter()
21421            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21422            .collect();
21423
21424        // Now collect block IDs to remove and retain overlays
21425        let mut block_ids_to_remove = HashSet::default();
21426        let mut index = 0;
21427        self.diff_review_overlays.retain(|overlay| {
21428            let has_comments = overlays_with_comments[index];
21429            index += 1;
21430            if !has_comments {
21431                block_ids_to_remove.insert(overlay.block_id);
21432            }
21433            has_comments
21434        });
21435
21436        if !block_ids_to_remove.is_empty() {
21437            self.remove_blocks(block_ids_to_remove, None, cx);
21438            cx.notify();
21439        }
21440    }
21441
21442    /// Refreshes the diff review overlay block to update its height and render function.
21443    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21444    fn refresh_diff_review_overlay_height(
21445        &mut self,
21446        hunk_key: &DiffHunkKey,
21447        _window: &mut Window,
21448        cx: &mut Context<Self>,
21449    ) {
21450        // Extract all needed data from overlay first to avoid borrow conflicts
21451        let snapshot = self.buffer.read(cx).snapshot(cx);
21452        let (comments_expanded, block_id, prompt_editor) = {
21453            let Some(overlay) = self
21454                .diff_review_overlays
21455                .iter()
21456                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21457            else {
21458                return;
21459            };
21460
21461            (
21462                overlay.comments_expanded,
21463                overlay.block_id,
21464                overlay.prompt_editor.clone(),
21465            )
21466        };
21467
21468        // Calculate new height
21469        let snapshot = self.buffer.read(cx).snapshot(cx);
21470        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21471
21472        // Update the block height using resize_blocks (avoids flicker)
21473        let mut heights = HashMap::default();
21474        heights.insert(block_id, new_height);
21475        self.resize_blocks(heights, None, cx);
21476
21477        // Update the render function using replace_blocks (avoids flicker)
21478        let hunk_key_for_render = hunk_key.clone();
21479        let editor_handle = cx.entity().downgrade();
21480        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21481            Arc::new(move |cx| {
21482                Self::render_diff_review_overlay(
21483                    &prompt_editor,
21484                    &hunk_key_for_render,
21485                    &editor_handle,
21486                    cx,
21487                )
21488            });
21489
21490        let mut renderers = HashMap::default();
21491        renderers.insert(block_id, render);
21492        self.replace_blocks(renderers, None, cx);
21493    }
21494
21495    /// Action handler for SubmitDiffReviewComment.
21496    pub fn submit_diff_review_comment_action(
21497        &mut self,
21498        _: &SubmitDiffReviewComment,
21499        window: &mut Window,
21500        cx: &mut Context<Self>,
21501    ) {
21502        self.submit_diff_review_comment(window, cx);
21503    }
21504
21505    /// Stores the diff review comment locally.
21506    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21507    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21508        // Find the overlay that currently has focus
21509        let overlay_index = self
21510            .diff_review_overlays
21511            .iter()
21512            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21513        let Some(overlay_index) = overlay_index else {
21514            return;
21515        };
21516        let overlay = &self.diff_review_overlays[overlay_index];
21517
21518        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21519        if comment_text.is_empty() {
21520            return;
21521        }
21522
21523        let anchor_range = overlay.anchor_range.clone();
21524        let hunk_key = overlay.hunk_key.clone();
21525
21526        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21527
21528        // Clear the prompt editor but keep the overlay open
21529        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21530            overlay.prompt_editor.update(cx, |editor, cx| {
21531                editor.clear(window, cx);
21532            });
21533        }
21534
21535        // Refresh the overlay to update the block height for the new comment
21536        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21537
21538        cx.notify();
21539    }
21540
21541    /// Returns the prompt editor for the diff review overlay, if one is active.
21542    /// This is primarily used for testing.
21543    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21544        self.diff_review_overlays
21545            .first()
21546            .map(|overlay| &overlay.prompt_editor)
21547    }
21548
21549    /// Returns the line range for the first diff review overlay, if one is active.
21550    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21551    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21552        let overlay = self.diff_review_overlays.first()?;
21553        let snapshot = self.buffer.read(cx).snapshot(cx);
21554        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21555        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21556        let start_row = snapshot
21557            .point_to_buffer_point(start_point)
21558            .map(|(_, p, _)| p.row)
21559            .unwrap_or(start_point.row);
21560        let end_row = snapshot
21561            .point_to_buffer_point(end_point)
21562            .map(|(_, p, _)| p.row)
21563            .unwrap_or(end_point.row);
21564        Some((start_row, end_row))
21565    }
21566
21567    /// Sets whether the comments section is expanded in the diff review overlay.
21568    /// This is primarily used for testing.
21569    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21570        for overlay in &mut self.diff_review_overlays {
21571            overlay.comments_expanded = expanded;
21572        }
21573        cx.notify();
21574    }
21575
21576    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21577    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21578        a.file_path == b.file_path
21579            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21580    }
21581
21582    /// Returns comments for a specific hunk, ordered by creation time.
21583    pub fn comments_for_hunk<'a>(
21584        &'a self,
21585        key: &DiffHunkKey,
21586        snapshot: &MultiBufferSnapshot,
21587    ) -> &'a [StoredReviewComment] {
21588        let key_point = key.hunk_start_anchor.to_point(snapshot);
21589        self.stored_review_comments
21590            .iter()
21591            .find(|(k, _)| {
21592                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21593            })
21594            .map(|(_, comments)| comments.as_slice())
21595            .unwrap_or(&[])
21596    }
21597
21598    /// Returns the total count of stored review comments across all hunks.
21599    pub fn total_review_comment_count(&self) -> usize {
21600        self.stored_review_comments
21601            .iter()
21602            .map(|(_, v)| v.len())
21603            .sum()
21604    }
21605
21606    /// Returns the count of comments for a specific hunk.
21607    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21608        let key_point = key.hunk_start_anchor.to_point(snapshot);
21609        self.stored_review_comments
21610            .iter()
21611            .find(|(k, _)| {
21612                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21613            })
21614            .map(|(_, v)| v.len())
21615            .unwrap_or(0)
21616    }
21617
21618    /// Adds a new review comment to a specific hunk.
21619    pub fn add_review_comment(
21620        &mut self,
21621        hunk_key: DiffHunkKey,
21622        comment: String,
21623        anchor_range: Range<Anchor>,
21624        cx: &mut Context<Self>,
21625    ) -> usize {
21626        let id = self.next_review_comment_id;
21627        self.next_review_comment_id += 1;
21628
21629        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21630
21631        let snapshot = self.buffer.read(cx).snapshot(cx);
21632        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21633
21634        // Find existing entry for this hunk or add a new one
21635        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21636            k.file_path == hunk_key.file_path
21637                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21638        }) {
21639            comments.push(stored_comment);
21640        } else {
21641            self.stored_review_comments
21642                .push((hunk_key, vec![stored_comment]));
21643        }
21644
21645        cx.emit(EditorEvent::ReviewCommentsChanged {
21646            total_count: self.total_review_comment_count(),
21647        });
21648        cx.notify();
21649        id
21650    }
21651
21652    /// Removes a review comment by ID from any hunk.
21653    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21654        for (_, comments) in self.stored_review_comments.iter_mut() {
21655            if let Some(index) = comments.iter().position(|c| c.id == id) {
21656                comments.remove(index);
21657                cx.emit(EditorEvent::ReviewCommentsChanged {
21658                    total_count: self.total_review_comment_count(),
21659                });
21660                cx.notify();
21661                return true;
21662            }
21663        }
21664        false
21665    }
21666
21667    /// Updates a review comment's text by ID.
21668    pub fn update_review_comment(
21669        &mut self,
21670        id: usize,
21671        new_comment: String,
21672        cx: &mut Context<Self>,
21673    ) -> bool {
21674        for (_, comments) in self.stored_review_comments.iter_mut() {
21675            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21676                comment.comment = new_comment;
21677                comment.is_editing = false;
21678                cx.emit(EditorEvent::ReviewCommentsChanged {
21679                    total_count: self.total_review_comment_count(),
21680                });
21681                cx.notify();
21682                return true;
21683            }
21684        }
21685        false
21686    }
21687
21688    /// Sets a comment's editing state.
21689    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21690        for (_, comments) in self.stored_review_comments.iter_mut() {
21691            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21692                comment.is_editing = is_editing;
21693                cx.notify();
21694                return;
21695            }
21696        }
21697    }
21698
21699    /// Takes all stored comments from all hunks, clearing the storage.
21700    /// Returns a Vec of (hunk_key, comments) pairs.
21701    pub fn take_all_review_comments(
21702        &mut self,
21703        cx: &mut Context<Self>,
21704    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
21705        // Dismiss all overlays when taking comments (e.g., when sending to agent)
21706        self.dismiss_all_diff_review_overlays(cx);
21707        let comments = std::mem::take(&mut self.stored_review_comments);
21708        // Reset the ID counter since all comments have been taken
21709        self.next_review_comment_id = 0;
21710        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
21711        cx.notify();
21712        comments
21713    }
21714
21715    /// Removes review comments whose anchors are no longer valid or whose
21716    /// associated diff hunks no longer exist.
21717    ///
21718    /// This should be called when the buffer changes to prevent orphaned comments
21719    /// from accumulating.
21720    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
21721        let snapshot = self.buffer.read(cx).snapshot(cx);
21722        let original_count = self.total_review_comment_count();
21723
21724        // Remove comments with invalid hunk anchors
21725        self.stored_review_comments
21726            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
21727
21728        // Also clean up individual comments with invalid anchor ranges
21729        for (_, comments) in &mut self.stored_review_comments {
21730            comments.retain(|comment| {
21731                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
21732            });
21733        }
21734
21735        // Remove empty hunk entries
21736        self.stored_review_comments
21737            .retain(|(_, comments)| !comments.is_empty());
21738
21739        let new_count = self.total_review_comment_count();
21740        if new_count != original_count {
21741            cx.emit(EditorEvent::ReviewCommentsChanged {
21742                total_count: new_count,
21743            });
21744            cx.notify();
21745        }
21746    }
21747
21748    /// Toggles the expanded state of the comments section in the overlay.
21749    pub fn toggle_review_comments_expanded(
21750        &mut self,
21751        _: &ToggleReviewCommentsExpanded,
21752        window: &mut Window,
21753        cx: &mut Context<Self>,
21754    ) {
21755        // Find the overlay that currently has focus, or use the first one
21756        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
21757            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
21758                overlay.comments_expanded = !overlay.comments_expanded;
21759                Some(overlay.hunk_key.clone())
21760            } else {
21761                None
21762            }
21763        });
21764
21765        // If no focused overlay found, toggle the first one
21766        let hunk_key = overlay_info.or_else(|| {
21767            self.diff_review_overlays.first_mut().map(|overlay| {
21768                overlay.comments_expanded = !overlay.comments_expanded;
21769                overlay.hunk_key.clone()
21770            })
21771        });
21772
21773        if let Some(hunk_key) = hunk_key {
21774            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21775            cx.notify();
21776        }
21777    }
21778
21779    /// Handles the EditReviewComment action - sets a comment into editing mode.
21780    pub fn edit_review_comment(
21781        &mut self,
21782        action: &EditReviewComment,
21783        window: &mut Window,
21784        cx: &mut Context<Self>,
21785    ) {
21786        let comment_id = action.id;
21787
21788        // Set the comment to editing mode
21789        self.set_comment_editing(comment_id, true, cx);
21790
21791        // Find the overlay that contains this comment and create an inline editor if needed
21792        // First, find which hunk this comment belongs to
21793        let hunk_key = self
21794            .stored_review_comments
21795            .iter()
21796            .find_map(|(key, comments)| {
21797                if comments.iter().any(|c| c.id == comment_id) {
21798                    Some(key.clone())
21799                } else {
21800                    None
21801                }
21802            });
21803
21804        let snapshot = self.buffer.read(cx).snapshot(cx);
21805        if let Some(hunk_key) = hunk_key {
21806            if let Some(overlay) = self
21807                .diff_review_overlays
21808                .iter_mut()
21809                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21810            {
21811                if let std::collections::hash_map::Entry::Vacant(entry) =
21812                    overlay.inline_edit_editors.entry(comment_id)
21813                {
21814                    // Find the comment text
21815                    let comment_text = self
21816                        .stored_review_comments
21817                        .iter()
21818                        .flat_map(|(_, comments)| comments)
21819                        .find(|c| c.id == comment_id)
21820                        .map(|c| c.comment.clone())
21821                        .unwrap_or_default();
21822
21823                    // Create inline editor
21824                    let parent_editor = cx.entity().downgrade();
21825                    let inline_editor = cx.new(|cx| {
21826                        let mut editor = Editor::single_line(window, cx);
21827                        editor.set_text(&*comment_text, window, cx);
21828                        // Select all text for easy replacement
21829                        editor.select_all(&crate::actions::SelectAll, window, cx);
21830                        editor
21831                    });
21832
21833                    // Register the Newline action to confirm the edit
21834                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
21835                        inline_editor.register_action({
21836                            let parent_editor = parent_editor.clone();
21837                            move |_: &crate::actions::Newline, window, cx| {
21838                                if let Some(editor) = parent_editor.upgrade() {
21839                                    editor.update(cx, |editor, cx| {
21840                                        editor.confirm_edit_review_comment(comment_id, window, cx);
21841                                    });
21842                                }
21843                            }
21844                        })
21845                    });
21846
21847                    // Store the subscription to keep the action handler alive
21848                    overlay
21849                        .inline_edit_subscriptions
21850                        .insert(comment_id, subscription);
21851
21852                    // Focus the inline editor
21853                    let focus_handle = inline_editor.focus_handle(cx);
21854                    window.focus(&focus_handle, cx);
21855
21856                    entry.insert(inline_editor);
21857                }
21858            }
21859        }
21860
21861        cx.notify();
21862    }
21863
21864    /// Confirms an inline edit of a review comment.
21865    pub fn confirm_edit_review_comment(
21866        &mut self,
21867        comment_id: usize,
21868        _window: &mut Window,
21869        cx: &mut Context<Self>,
21870    ) {
21871        // Get the new text from the inline editor
21872        // Find the overlay containing this comment's inline editor
21873        let snapshot = self.buffer.read(cx).snapshot(cx);
21874        let hunk_key = self
21875            .stored_review_comments
21876            .iter()
21877            .find_map(|(key, comments)| {
21878                if comments.iter().any(|c| c.id == comment_id) {
21879                    Some(key.clone())
21880                } else {
21881                    None
21882                }
21883            });
21884
21885        let new_text = hunk_key
21886            .as_ref()
21887            .and_then(|hunk_key| {
21888                self.diff_review_overlays
21889                    .iter()
21890                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21891            })
21892            .as_ref()
21893            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
21894            .map(|editor| editor.read(cx).text(cx).trim().to_string());
21895
21896        if let Some(new_text) = new_text {
21897            if !new_text.is_empty() {
21898                self.update_review_comment(comment_id, new_text, cx);
21899            }
21900        }
21901
21902        // Remove the inline editor and its subscription
21903        if let Some(hunk_key) = hunk_key {
21904            if let Some(overlay) = self
21905                .diff_review_overlays
21906                .iter_mut()
21907                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21908            {
21909                overlay.inline_edit_editors.remove(&comment_id);
21910                overlay.inline_edit_subscriptions.remove(&comment_id);
21911            }
21912        }
21913
21914        // Clear editing state
21915        self.set_comment_editing(comment_id, false, cx);
21916    }
21917
21918    /// Cancels an inline edit of a review comment.
21919    pub fn cancel_edit_review_comment(
21920        &mut self,
21921        comment_id: usize,
21922        _window: &mut Window,
21923        cx: &mut Context<Self>,
21924    ) {
21925        // Find which hunk this comment belongs to
21926        let hunk_key = self
21927            .stored_review_comments
21928            .iter()
21929            .find_map(|(key, comments)| {
21930                if comments.iter().any(|c| c.id == comment_id) {
21931                    Some(key.clone())
21932                } else {
21933                    None
21934                }
21935            });
21936
21937        // Remove the inline editor and its subscription
21938        if let Some(hunk_key) = hunk_key {
21939            let snapshot = self.buffer.read(cx).snapshot(cx);
21940            if let Some(overlay) = self
21941                .diff_review_overlays
21942                .iter_mut()
21943                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
21944            {
21945                overlay.inline_edit_editors.remove(&comment_id);
21946                overlay.inline_edit_subscriptions.remove(&comment_id);
21947            }
21948        }
21949
21950        // Clear editing state
21951        self.set_comment_editing(comment_id, false, cx);
21952    }
21953
21954    /// Action handler for ConfirmEditReviewComment.
21955    pub fn confirm_edit_review_comment_action(
21956        &mut self,
21957        action: &ConfirmEditReviewComment,
21958        window: &mut Window,
21959        cx: &mut Context<Self>,
21960    ) {
21961        self.confirm_edit_review_comment(action.id, window, cx);
21962    }
21963
21964    /// Action handler for CancelEditReviewComment.
21965    pub fn cancel_edit_review_comment_action(
21966        &mut self,
21967        action: &CancelEditReviewComment,
21968        window: &mut Window,
21969        cx: &mut Context<Self>,
21970    ) {
21971        self.cancel_edit_review_comment(action.id, window, cx);
21972    }
21973
21974    /// Handles the DeleteReviewComment action - removes a comment.
21975    pub fn delete_review_comment(
21976        &mut self,
21977        action: &DeleteReviewComment,
21978        window: &mut Window,
21979        cx: &mut Context<Self>,
21980    ) {
21981        // Get the hunk key before removing the comment
21982        // Find the hunk key from the comment itself
21983        let comment_id = action.id;
21984        let hunk_key = self
21985            .stored_review_comments
21986            .iter()
21987            .find_map(|(key, comments)| {
21988                if comments.iter().any(|c| c.id == comment_id) {
21989                    Some(key.clone())
21990                } else {
21991                    None
21992                }
21993            });
21994
21995        // Also get it from the overlay for refresh purposes
21996        let overlay_hunk_key = self
21997            .diff_review_overlays
21998            .first()
21999            .map(|o| o.hunk_key.clone());
22000
22001        self.remove_review_comment(action.id, cx);
22002
22003        // Refresh the overlay height after removing a comment
22004        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22005            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22006        }
22007    }
22008
22009    fn render_diff_review_overlay(
22010        prompt_editor: &Entity<Editor>,
22011        hunk_key: &DiffHunkKey,
22012        editor_handle: &WeakEntity<Editor>,
22013        cx: &mut BlockContext,
22014    ) -> AnyElement {
22015        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22016            if ranges.is_empty() {
22017                return None;
22018            }
22019            let formatted: Vec<String> = ranges
22020                .iter()
22021                .map(|(start, end)| {
22022                    let start_line = start + 1;
22023                    let end_line = end + 1;
22024                    if start_line == end_line {
22025                        format!("Line {start_line}")
22026                    } else {
22027                        format!("Lines {start_line}-{end_line}")
22028                    }
22029                })
22030                .collect();
22031            // Don't show label for single line in single excerpt
22032            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22033                return None;
22034            }
22035            Some(formatted.join(""))
22036        }
22037
22038        let theme = cx.theme();
22039        let colors = theme.colors();
22040
22041        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22042            editor_handle
22043                .upgrade()
22044                .map(|editor| {
22045                    let editor = editor.read(cx);
22046                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22047                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22048                    let (expanded, editors, avatar_uri, line_ranges) = editor
22049                        .diff_review_overlays
22050                        .iter()
22051                        .find(|overlay| {
22052                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22053                        })
22054                        .map(|o| {
22055                            let start_point = o.anchor_range.start.to_point(&snapshot);
22056                            let end_point = o.anchor_range.end.to_point(&snapshot);
22057                            // Get line ranges per excerpt to detect discontinuities
22058                            let buffer_ranges =
22059                                snapshot.range_to_buffer_ranges(start_point..end_point);
22060                            let ranges: Vec<(u32, u32)> = buffer_ranges
22061                                .iter()
22062                                .map(|(buffer, range, _)| {
22063                                    let start = buffer.offset_to_point(range.start.0).row;
22064                                    let end = buffer.offset_to_point(range.end.0).row;
22065                                    (start, end)
22066                                })
22067                                .collect();
22068                            (
22069                                o.comments_expanded,
22070                                o.inline_edit_editors.clone(),
22071                                o.user_avatar_uri.clone(),
22072                                if ranges.is_empty() {
22073                                    None
22074                                } else {
22075                                    Some(ranges)
22076                                },
22077                            )
22078                        })
22079                        .unwrap_or((true, HashMap::default(), None, None));
22080                    (comments, expanded, editors, avatar_uri, line_ranges)
22081                })
22082                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22083
22084        let comment_count = comments.len();
22085        let avatar_size = px(20.);
22086        let action_icon_size = IconSize::XSmall;
22087
22088        v_flex()
22089            .w_full()
22090            .bg(colors.editor_background)
22091            .border_b_1()
22092            .border_color(colors.border)
22093            .px_2()
22094            .pb_2()
22095            .gap_2()
22096            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22097            .when_some(line_ranges, |el, ranges| {
22098                let label = format_line_ranges(&ranges);
22099                if let Some(label) = label {
22100                    el.child(
22101                        h_flex()
22102                            .w_full()
22103                            .px_2()
22104                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22105                    )
22106                } else {
22107                    el
22108                }
22109            })
22110            // Top row: editable input with user's avatar
22111            .child(
22112                h_flex()
22113                    .w_full()
22114                    .items_center()
22115                    .gap_2()
22116                    .px_2()
22117                    .py_1p5()
22118                    .rounded_md()
22119                    .bg(colors.surface_background)
22120                    .child(
22121                        div()
22122                            .size(avatar_size)
22123                            .flex_shrink_0()
22124                            .rounded_full()
22125                            .overflow_hidden()
22126                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22127                                Avatar::new(avatar_uri.clone())
22128                                    .size(avatar_size)
22129                                    .into_any_element()
22130                            } else {
22131                                Icon::new(IconName::Person)
22132                                    .size(IconSize::Small)
22133                                    .color(ui::Color::Muted)
22134                                    .into_any_element()
22135                            }),
22136                    )
22137                    .child(
22138                        div()
22139                            .flex_1()
22140                            .border_1()
22141                            .border_color(colors.border)
22142                            .rounded_md()
22143                            .bg(colors.editor_background)
22144                            .px_2()
22145                            .py_1()
22146                            .child(prompt_editor.clone()),
22147                    )
22148                    .child(
22149                        h_flex()
22150                            .flex_shrink_0()
22151                            .gap_1()
22152                            .child(
22153                                IconButton::new("diff-review-close", IconName::Close)
22154                                    .icon_color(ui::Color::Muted)
22155                                    .icon_size(action_icon_size)
22156                                    .tooltip(Tooltip::text("Close"))
22157                                    .on_click(|_, window, cx| {
22158                                        window
22159                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22160                                    }),
22161                            )
22162                            .child(
22163                                IconButton::new("diff-review-add", IconName::Return)
22164                                    .icon_color(ui::Color::Muted)
22165                                    .icon_size(action_icon_size)
22166                                    .tooltip(Tooltip::text("Add comment"))
22167                                    .on_click(|_, window, cx| {
22168                                        window.dispatch_action(
22169                                            Box::new(crate::actions::SubmitDiffReviewComment),
22170                                            cx,
22171                                        );
22172                                    }),
22173                            ),
22174                    ),
22175            )
22176            // Expandable comments section (only shown when there are comments)
22177            .when(comment_count > 0, |el| {
22178                el.child(Self::render_comments_section(
22179                    comments,
22180                    comments_expanded,
22181                    inline_editors,
22182                    user_avatar_uri,
22183                    avatar_size,
22184                    action_icon_size,
22185                    colors,
22186                ))
22187            })
22188            .into_any_element()
22189    }
22190
22191    fn render_comments_section(
22192        comments: Vec<StoredReviewComment>,
22193        expanded: bool,
22194        inline_editors: HashMap<usize, Entity<Editor>>,
22195        user_avatar_uri: Option<SharedUri>,
22196        avatar_size: Pixels,
22197        action_icon_size: IconSize,
22198        colors: &theme::ThemeColors,
22199    ) -> impl IntoElement {
22200        let comment_count = comments.len();
22201
22202        v_flex()
22203            .w_full()
22204            .gap_1()
22205            // Header with expand/collapse toggle
22206            .child(
22207                h_flex()
22208                    .id("review-comments-header")
22209                    .w_full()
22210                    .items_center()
22211                    .gap_1()
22212                    .px_2()
22213                    .py_1()
22214                    .cursor_pointer()
22215                    .rounded_md()
22216                    .hover(|style| style.bg(colors.ghost_element_hover))
22217                    .on_click(|_, window: &mut Window, cx| {
22218                        window.dispatch_action(
22219                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22220                            cx,
22221                        );
22222                    })
22223                    .child(
22224                        Icon::new(if expanded {
22225                            IconName::ChevronDown
22226                        } else {
22227                            IconName::ChevronRight
22228                        })
22229                        .size(IconSize::Small)
22230                        .color(ui::Color::Muted),
22231                    )
22232                    .child(
22233                        Label::new(format!(
22234                            "{} Comment{}",
22235                            comment_count,
22236                            if comment_count == 1 { "" } else { "s" }
22237                        ))
22238                        .size(LabelSize::Small)
22239                        .color(Color::Muted),
22240                    ),
22241            )
22242            // Comments list (when expanded)
22243            .when(expanded, |el| {
22244                el.children(comments.into_iter().map(|comment| {
22245                    let inline_editor = inline_editors.get(&comment.id).cloned();
22246                    Self::render_comment_row(
22247                        comment,
22248                        inline_editor,
22249                        user_avatar_uri.clone(),
22250                        avatar_size,
22251                        action_icon_size,
22252                        colors,
22253                    )
22254                }))
22255            })
22256    }
22257
22258    fn render_comment_row(
22259        comment: StoredReviewComment,
22260        inline_editor: Option<Entity<Editor>>,
22261        user_avatar_uri: Option<SharedUri>,
22262        avatar_size: Pixels,
22263        action_icon_size: IconSize,
22264        colors: &theme::ThemeColors,
22265    ) -> impl IntoElement {
22266        let comment_id = comment.id;
22267        let is_editing = inline_editor.is_some();
22268
22269        h_flex()
22270            .w_full()
22271            .items_center()
22272            .gap_2()
22273            .px_2()
22274            .py_1p5()
22275            .rounded_md()
22276            .bg(colors.surface_background)
22277            .child(
22278                div()
22279                    .size(avatar_size)
22280                    .flex_shrink_0()
22281                    .rounded_full()
22282                    .overflow_hidden()
22283                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22284                        Avatar::new(avatar_uri.clone())
22285                            .size(avatar_size)
22286                            .into_any_element()
22287                    } else {
22288                        Icon::new(IconName::Person)
22289                            .size(IconSize::Small)
22290                            .color(ui::Color::Muted)
22291                            .into_any_element()
22292                    }),
22293            )
22294            .child(if let Some(editor) = inline_editor {
22295                // Inline edit mode: show an editable text field
22296                div()
22297                    .flex_1()
22298                    .border_1()
22299                    .border_color(colors.border)
22300                    .rounded_md()
22301                    .bg(colors.editor_background)
22302                    .px_2()
22303                    .py_1()
22304                    .child(editor)
22305                    .into_any_element()
22306            } else {
22307                // Display mode: show the comment text
22308                div()
22309                    .flex_1()
22310                    .text_sm()
22311                    .text_color(colors.text)
22312                    .child(comment.comment)
22313                    .into_any_element()
22314            })
22315            .child(if is_editing {
22316                // Editing mode: show close and confirm buttons
22317                h_flex()
22318                    .gap_1()
22319                    .child(
22320                        IconButton::new(
22321                            format!("diff-review-cancel-edit-{comment_id}"),
22322                            IconName::Close,
22323                        )
22324                        .icon_color(ui::Color::Muted)
22325                        .icon_size(action_icon_size)
22326                        .tooltip(Tooltip::text("Cancel"))
22327                        .on_click(move |_, window, cx| {
22328                            window.dispatch_action(
22329                                Box::new(crate::actions::CancelEditReviewComment {
22330                                    id: comment_id,
22331                                }),
22332                                cx,
22333                            );
22334                        }),
22335                    )
22336                    .child(
22337                        IconButton::new(
22338                            format!("diff-review-confirm-edit-{comment_id}"),
22339                            IconName::Return,
22340                        )
22341                        .icon_color(ui::Color::Muted)
22342                        .icon_size(action_icon_size)
22343                        .tooltip(Tooltip::text("Confirm"))
22344                        .on_click(move |_, window, cx| {
22345                            window.dispatch_action(
22346                                Box::new(crate::actions::ConfirmEditReviewComment {
22347                                    id: comment_id,
22348                                }),
22349                                cx,
22350                            );
22351                        }),
22352                    )
22353                    .into_any_element()
22354            } else {
22355                // Display mode: no action buttons for now (edit/delete not yet implemented)
22356                gpui::Empty.into_any_element()
22357            })
22358    }
22359
22360    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22361        if self.display_map.read(cx).masked != masked {
22362            self.display_map.update(cx, |map, _| map.masked = masked);
22363        }
22364        cx.notify()
22365    }
22366
22367    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22368        self.show_wrap_guides = Some(show_wrap_guides);
22369        cx.notify();
22370    }
22371
22372    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22373        self.show_indent_guides = Some(show_indent_guides);
22374        cx.notify();
22375    }
22376
22377    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22378        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22379            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22380                && let Some(dir) = file.abs_path(cx).parent()
22381            {
22382                return Some(dir.to_owned());
22383            }
22384        }
22385
22386        None
22387    }
22388
22389    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22390        self.active_excerpt(cx)?
22391            .1
22392            .read(cx)
22393            .file()
22394            .and_then(|f| f.as_local())
22395    }
22396
22397    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22398        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22399            let buffer = buffer.read(cx);
22400            if let Some(project_path) = buffer.project_path(cx) {
22401                let project = self.project()?.read(cx);
22402                project.absolute_path(&project_path, cx)
22403            } else {
22404                buffer
22405                    .file()
22406                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22407            }
22408        })
22409    }
22410
22411    pub fn reveal_in_finder(
22412        &mut self,
22413        _: &RevealInFileManager,
22414        _window: &mut Window,
22415        cx: &mut Context<Self>,
22416    ) {
22417        if let Some(path) = self.target_file_abs_path(cx) {
22418            if let Some(project) = self.project() {
22419                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22420            } else {
22421                cx.reveal_path(&path);
22422            }
22423        }
22424    }
22425
22426    pub fn copy_path(
22427        &mut self,
22428        _: &zed_actions::workspace::CopyPath,
22429        _window: &mut Window,
22430        cx: &mut Context<Self>,
22431    ) {
22432        if let Some(path) = self.target_file_abs_path(cx)
22433            && let Some(path) = path.to_str()
22434        {
22435            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22436        } else {
22437            cx.propagate();
22438        }
22439    }
22440
22441    pub fn copy_relative_path(
22442        &mut self,
22443        _: &zed_actions::workspace::CopyRelativePath,
22444        _window: &mut Window,
22445        cx: &mut Context<Self>,
22446    ) {
22447        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22448            let project = self.project()?.read(cx);
22449            let path = buffer.read(cx).file()?.path();
22450            let path = path.display(project.path_style(cx));
22451            Some(path)
22452        }) {
22453            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22454        } else {
22455            cx.propagate();
22456        }
22457    }
22458
22459    /// Returns the project path for the editor's buffer, if any buffer is
22460    /// opened in the editor.
22461    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22462        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22463            buffer.read(cx).project_path(cx)
22464        } else {
22465            None
22466        }
22467    }
22468
22469    // Returns true if the editor handled a go-to-line request
22470    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22471        maybe!({
22472            let breakpoint_store = self.breakpoint_store.as_ref()?;
22473
22474            let (active_stack_frame, debug_line_pane_id) = {
22475                let store = breakpoint_store.read(cx);
22476                let active_stack_frame = store.active_position().cloned();
22477                let debug_line_pane_id = store.active_debug_line_pane_id();
22478                (active_stack_frame, debug_line_pane_id)
22479            };
22480
22481            let Some(active_stack_frame) = active_stack_frame else {
22482                self.clear_row_highlights::<ActiveDebugLine>();
22483                return None;
22484            };
22485
22486            if let Some(debug_line_pane_id) = debug_line_pane_id {
22487                if let Some(workspace) = self
22488                    .workspace
22489                    .as_ref()
22490                    .and_then(|(workspace, _)| workspace.upgrade())
22491                {
22492                    let editor_pane_id = workspace
22493                        .read(cx)
22494                        .pane_for_item_id(cx.entity_id())
22495                        .map(|pane| pane.entity_id());
22496
22497                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22498                        self.clear_row_highlights::<ActiveDebugLine>();
22499                        return None;
22500                    }
22501                }
22502            }
22503
22504            let position = active_stack_frame.position;
22505            let buffer_id = position.buffer_id?;
22506            let snapshot = self
22507                .project
22508                .as_ref()?
22509                .read(cx)
22510                .buffer_for_id(buffer_id, cx)?
22511                .read(cx)
22512                .snapshot();
22513
22514            let mut handled = false;
22515            for (id, _, ExcerptRange { context, .. }) in
22516                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22517            {
22518                if context.start.cmp(&position, &snapshot).is_ge()
22519                    || context.end.cmp(&position, &snapshot).is_lt()
22520                {
22521                    continue;
22522                }
22523                let snapshot = self.buffer.read(cx).snapshot(cx);
22524                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22525
22526                handled = true;
22527                self.clear_row_highlights::<ActiveDebugLine>();
22528
22529                self.go_to_line::<ActiveDebugLine>(
22530                    multibuffer_anchor,
22531                    Some(cx.theme().colors().editor_debugger_active_line_background),
22532                    window,
22533                    cx,
22534                );
22535
22536                cx.notify();
22537            }
22538
22539            handled.then_some(())
22540        })
22541        .is_some()
22542    }
22543
22544    pub fn copy_file_name_without_extension(
22545        &mut self,
22546        _: &CopyFileNameWithoutExtension,
22547        _: &mut Window,
22548        cx: &mut Context<Self>,
22549    ) {
22550        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22551            let file = buffer.read(cx).file()?;
22552            file.path().file_stem()
22553        }) {
22554            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22555        }
22556    }
22557
22558    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22559        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22560            let file = buffer.read(cx).file()?;
22561            Some(file.file_name(cx))
22562        }) {
22563            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22564        }
22565    }
22566
22567    pub fn toggle_git_blame(
22568        &mut self,
22569        _: &::git::Blame,
22570        window: &mut Window,
22571        cx: &mut Context<Self>,
22572    ) {
22573        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22574
22575        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22576            self.start_git_blame(true, window, cx);
22577        }
22578
22579        cx.notify();
22580    }
22581
22582    pub fn toggle_git_blame_inline(
22583        &mut self,
22584        _: &ToggleGitBlameInline,
22585        window: &mut Window,
22586        cx: &mut Context<Self>,
22587    ) {
22588        self.toggle_git_blame_inline_internal(true, window, cx);
22589        cx.notify();
22590    }
22591
22592    pub fn open_git_blame_commit(
22593        &mut self,
22594        _: &OpenGitBlameCommit,
22595        window: &mut Window,
22596        cx: &mut Context<Self>,
22597    ) {
22598        self.open_git_blame_commit_internal(window, cx);
22599    }
22600
22601    fn open_git_blame_commit_internal(
22602        &mut self,
22603        window: &mut Window,
22604        cx: &mut Context<Self>,
22605    ) -> Option<()> {
22606        let blame = self.blame.as_ref()?;
22607        let snapshot = self.snapshot(window, cx);
22608        let cursor = self
22609            .selections
22610            .newest::<Point>(&snapshot.display_snapshot)
22611            .head();
22612        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22613        let (_, blame_entry) = blame
22614            .update(cx, |blame, cx| {
22615                blame
22616                    .blame_for_rows(
22617                        &[RowInfo {
22618                            buffer_id: Some(buffer.remote_id()),
22619                            buffer_row: Some(point.row),
22620                            ..Default::default()
22621                        }],
22622                        cx,
22623                    )
22624                    .next()
22625            })
22626            .flatten()?;
22627        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22628        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22629        let workspace = self.workspace()?.downgrade();
22630        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22631        None
22632    }
22633
22634    pub fn git_blame_inline_enabled(&self) -> bool {
22635        self.git_blame_inline_enabled
22636    }
22637
22638    pub fn toggle_selection_menu(
22639        &mut self,
22640        _: &ToggleSelectionMenu,
22641        _: &mut Window,
22642        cx: &mut Context<Self>,
22643    ) {
22644        self.show_selection_menu = self
22645            .show_selection_menu
22646            .map(|show_selections_menu| !show_selections_menu)
22647            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22648
22649        cx.notify();
22650    }
22651
22652    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22653        self.show_selection_menu
22654            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22655    }
22656
22657    fn start_git_blame(
22658        &mut self,
22659        user_triggered: bool,
22660        window: &mut Window,
22661        cx: &mut Context<Self>,
22662    ) {
22663        if let Some(project) = self.project() {
22664            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22665                && buffer.read(cx).file().is_none()
22666            {
22667                return;
22668            }
22669
22670            let focused = self.focus_handle(cx).contains_focused(window, cx);
22671
22672            let project = project.clone();
22673            let blame = cx
22674                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22675            self.blame_subscription =
22676                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22677            self.blame = Some(blame);
22678        }
22679    }
22680
22681    fn toggle_git_blame_inline_internal(
22682        &mut self,
22683        user_triggered: bool,
22684        window: &mut Window,
22685        cx: &mut Context<Self>,
22686    ) {
22687        if self.git_blame_inline_enabled {
22688            self.git_blame_inline_enabled = false;
22689            self.show_git_blame_inline = false;
22690            self.show_git_blame_inline_delay_task.take();
22691        } else {
22692            self.git_blame_inline_enabled = true;
22693            self.start_git_blame_inline(user_triggered, window, cx);
22694        }
22695
22696        cx.notify();
22697    }
22698
22699    fn start_git_blame_inline(
22700        &mut self,
22701        user_triggered: bool,
22702        window: &mut Window,
22703        cx: &mut Context<Self>,
22704    ) {
22705        self.start_git_blame(user_triggered, window, cx);
22706
22707        if ProjectSettings::get_global(cx)
22708            .git
22709            .inline_blame_delay()
22710            .is_some()
22711        {
22712            self.start_inline_blame_timer(window, cx);
22713        } else {
22714            self.show_git_blame_inline = true
22715        }
22716    }
22717
22718    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22719        self.blame.as_ref()
22720    }
22721
22722    pub fn show_git_blame_gutter(&self) -> bool {
22723        self.show_git_blame_gutter
22724    }
22725
22726    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
22727        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
22728    }
22729
22730    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
22731        self.show_git_blame_inline
22732            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
22733            && !self.newest_selection_head_on_empty_line(cx)
22734            && self.has_blame_entries(cx)
22735    }
22736
22737    fn has_blame_entries(&self, cx: &App) -> bool {
22738        self.blame()
22739            .is_some_and(|blame| blame.read(cx).has_generated_entries())
22740    }
22741
22742    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
22743        let cursor_anchor = self.selections.newest_anchor().head();
22744
22745        let snapshot = self.buffer.read(cx).snapshot(cx);
22746        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
22747
22748        snapshot.line_len(buffer_row) == 0
22749    }
22750
22751    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
22752        let buffer_and_selection = maybe!({
22753            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
22754            let selection_range = selection.range();
22755
22756            let multi_buffer = self.buffer().read(cx);
22757            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
22758            let buffer_ranges = multi_buffer_snapshot
22759                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
22760
22761            let (buffer, range, _) = if selection.reversed {
22762                buffer_ranges.first()
22763            } else {
22764                buffer_ranges.last()
22765            }?;
22766
22767            let buffer_range = range.to_point(buffer);
22768
22769            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
22770                return Some((
22771                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
22772                    buffer_range.start.row..buffer_range.end.row,
22773                ));
22774            };
22775
22776            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
22777            let start =
22778                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
22779            let end =
22780                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
22781
22782            Some((
22783                multi_buffer.buffer(buffer.remote_id()).unwrap(),
22784                start.row..end.row,
22785            ))
22786        });
22787
22788        let Some((buffer, selection)) = buffer_and_selection else {
22789            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
22790        };
22791
22792        let Some(project) = self.project() else {
22793            return Task::ready(Err(anyhow!("editor does not have project")));
22794        };
22795
22796        project.update(cx, |project, cx| {
22797            project.get_permalink_to_line(&buffer, selection, cx)
22798        })
22799    }
22800
22801    pub fn copy_permalink_to_line(
22802        &mut self,
22803        _: &CopyPermalinkToLine,
22804        window: &mut Window,
22805        cx: &mut Context<Self>,
22806    ) {
22807        let permalink_task = self.get_permalink_to_line(cx);
22808        let workspace = self.workspace();
22809
22810        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22811            Ok(permalink) => {
22812                cx.update(|_, cx| {
22813                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
22814                })
22815                .ok();
22816            }
22817            Err(err) => {
22818                let message = format!("Failed to copy permalink: {err}");
22819
22820                anyhow::Result::<()>::Err(err).log_err();
22821
22822                if let Some(workspace) = workspace {
22823                    workspace
22824                        .update_in(cx, |workspace, _, cx| {
22825                            struct CopyPermalinkToLine;
22826
22827                            workspace.show_toast(
22828                                Toast::new(
22829                                    NotificationId::unique::<CopyPermalinkToLine>(),
22830                                    message,
22831                                ),
22832                                cx,
22833                            )
22834                        })
22835                        .ok();
22836                }
22837            }
22838        })
22839        .detach();
22840    }
22841
22842    pub fn copy_file_location(
22843        &mut self,
22844        _: &CopyFileLocation,
22845        _: &mut Window,
22846        cx: &mut Context<Self>,
22847    ) {
22848        let selection = self
22849            .selections
22850            .newest::<Point>(&self.display_snapshot(cx))
22851            .start
22852            .row
22853            + 1;
22854        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22855            let project = self.project()?.read(cx);
22856            let file = buffer.read(cx).file()?;
22857            let path = file.path().display(project.path_style(cx));
22858
22859            Some(format!("{path}:{selection}"))
22860        }) {
22861            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
22862        }
22863    }
22864
22865    pub fn open_permalink_to_line(
22866        &mut self,
22867        _: &OpenPermalinkToLine,
22868        window: &mut Window,
22869        cx: &mut Context<Self>,
22870    ) {
22871        let permalink_task = self.get_permalink_to_line(cx);
22872        let workspace = self.workspace();
22873
22874        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
22875            Ok(permalink) => {
22876                cx.update(|_, cx| {
22877                    cx.open_url(permalink.as_ref());
22878                })
22879                .ok();
22880            }
22881            Err(err) => {
22882                let message = format!("Failed to open permalink: {err}");
22883
22884                anyhow::Result::<()>::Err(err).log_err();
22885
22886                if let Some(workspace) = workspace {
22887                    workspace.update(cx, |workspace, cx| {
22888                        struct OpenPermalinkToLine;
22889
22890                        workspace.show_toast(
22891                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
22892                            cx,
22893                        )
22894                    });
22895                }
22896            }
22897        })
22898        .detach();
22899    }
22900
22901    pub fn insert_uuid_v4(
22902        &mut self,
22903        _: &InsertUuidV4,
22904        window: &mut Window,
22905        cx: &mut Context<Self>,
22906    ) {
22907        self.insert_uuid(UuidVersion::V4, window, cx);
22908    }
22909
22910    pub fn insert_uuid_v7(
22911        &mut self,
22912        _: &InsertUuidV7,
22913        window: &mut Window,
22914        cx: &mut Context<Self>,
22915    ) {
22916        self.insert_uuid(UuidVersion::V7, window, cx);
22917    }
22918
22919    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
22920        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
22921        self.transact(window, cx, |this, window, cx| {
22922            let edits = this
22923                .selections
22924                .all::<Point>(&this.display_snapshot(cx))
22925                .into_iter()
22926                .map(|selection| {
22927                    let uuid = match version {
22928                        UuidVersion::V4 => uuid::Uuid::new_v4(),
22929                        UuidVersion::V7 => uuid::Uuid::now_v7(),
22930                    };
22931
22932                    (selection.range(), uuid.to_string())
22933                });
22934            this.edit(edits, cx);
22935            this.refresh_edit_prediction(true, false, window, cx);
22936        });
22937    }
22938
22939    pub fn open_selections_in_multibuffer(
22940        &mut self,
22941        _: &OpenSelectionsInMultibuffer,
22942        window: &mut Window,
22943        cx: &mut Context<Self>,
22944    ) {
22945        let multibuffer = self.buffer.read(cx);
22946
22947        let Some(buffer) = multibuffer.as_singleton() else {
22948            return;
22949        };
22950
22951        let Some(workspace) = self.workspace() else {
22952            return;
22953        };
22954
22955        let title = multibuffer.title(cx).to_string();
22956
22957        let locations = self
22958            .selections
22959            .all_anchors(&self.display_snapshot(cx))
22960            .iter()
22961            .map(|selection| {
22962                (
22963                    buffer.clone(),
22964                    (selection.start.text_anchor..selection.end.text_anchor)
22965                        .to_point(buffer.read(cx)),
22966                )
22967            })
22968            .into_group_map();
22969
22970        cx.spawn_in(window, async move |_, cx| {
22971            workspace.update_in(cx, |workspace, window, cx| {
22972                Self::open_locations_in_multibuffer(
22973                    workspace,
22974                    locations,
22975                    format!("Selections for '{title}'"),
22976                    false,
22977                    false,
22978                    MultibufferSelectionMode::All,
22979                    window,
22980                    cx,
22981                );
22982            })
22983        })
22984        .detach();
22985    }
22986
22987    /// Adds a row highlight for the given range. If a row has multiple highlights, the
22988    /// last highlight added will be used.
22989    ///
22990    /// If the range ends at the beginning of a line, then that line will not be highlighted.
22991    pub fn highlight_rows<T: 'static>(
22992        &mut self,
22993        range: Range<Anchor>,
22994        color: Hsla,
22995        options: RowHighlightOptions,
22996        cx: &mut Context<Self>,
22997    ) {
22998        let snapshot = self.buffer().read(cx).snapshot(cx);
22999        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23000        let ix = row_highlights.binary_search_by(|highlight| {
23001            Ordering::Equal
23002                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23003                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23004        });
23005
23006        if let Err(mut ix) = ix {
23007            let index = post_inc(&mut self.highlight_order);
23008
23009            // If this range intersects with the preceding highlight, then merge it with
23010            // the preceding highlight. Otherwise insert a new highlight.
23011            let mut merged = false;
23012            if ix > 0 {
23013                let prev_highlight = &mut row_highlights[ix - 1];
23014                if prev_highlight
23015                    .range
23016                    .end
23017                    .cmp(&range.start, &snapshot)
23018                    .is_ge()
23019                {
23020                    ix -= 1;
23021                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23022                        prev_highlight.range.end = range.end;
23023                    }
23024                    merged = true;
23025                    prev_highlight.index = index;
23026                    prev_highlight.color = color;
23027                    prev_highlight.options = options;
23028                }
23029            }
23030
23031            if !merged {
23032                row_highlights.insert(
23033                    ix,
23034                    RowHighlight {
23035                        range,
23036                        index,
23037                        color,
23038                        options,
23039                        type_id: TypeId::of::<T>(),
23040                    },
23041                );
23042            }
23043
23044            // If any of the following highlights intersect with this one, merge them.
23045            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23046                let highlight = &row_highlights[ix];
23047                if next_highlight
23048                    .range
23049                    .start
23050                    .cmp(&highlight.range.end, &snapshot)
23051                    .is_le()
23052                {
23053                    if next_highlight
23054                        .range
23055                        .end
23056                        .cmp(&highlight.range.end, &snapshot)
23057                        .is_gt()
23058                    {
23059                        row_highlights[ix].range.end = next_highlight.range.end;
23060                    }
23061                    row_highlights.remove(ix + 1);
23062                } else {
23063                    break;
23064                }
23065            }
23066        }
23067    }
23068
23069    /// Remove any highlighted row ranges of the given type that intersect the
23070    /// given ranges.
23071    pub fn remove_highlighted_rows<T: 'static>(
23072        &mut self,
23073        ranges_to_remove: Vec<Range<Anchor>>,
23074        cx: &mut Context<Self>,
23075    ) {
23076        let snapshot = self.buffer().read(cx).snapshot(cx);
23077        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23078        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23079        row_highlights.retain(|highlight| {
23080            while let Some(range_to_remove) = ranges_to_remove.peek() {
23081                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23082                    Ordering::Less | Ordering::Equal => {
23083                        ranges_to_remove.next();
23084                    }
23085                    Ordering::Greater => {
23086                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23087                            Ordering::Less | Ordering::Equal => {
23088                                return false;
23089                            }
23090                            Ordering::Greater => break,
23091                        }
23092                    }
23093                }
23094            }
23095
23096            true
23097        })
23098    }
23099
23100    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23101    pub fn clear_row_highlights<T: 'static>(&mut self) {
23102        self.highlighted_rows.remove(&TypeId::of::<T>());
23103    }
23104
23105    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23106    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23107        self.highlighted_rows
23108            .get(&TypeId::of::<T>())
23109            .map_or(&[] as &[_], |vec| vec.as_slice())
23110            .iter()
23111            .map(|highlight| (highlight.range.clone(), highlight.color))
23112    }
23113
23114    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23115    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23116    /// Allows to ignore certain kinds of highlights.
23117    pub fn highlighted_display_rows(
23118        &self,
23119        window: &mut Window,
23120        cx: &mut App,
23121    ) -> BTreeMap<DisplayRow, LineHighlight> {
23122        let snapshot = self.snapshot(window, cx);
23123        let mut used_highlight_orders = HashMap::default();
23124        self.highlighted_rows
23125            .iter()
23126            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23127            .fold(
23128                BTreeMap::<DisplayRow, LineHighlight>::new(),
23129                |mut unique_rows, highlight| {
23130                    let start = highlight.range.start.to_display_point(&snapshot);
23131                    let end = highlight.range.end.to_display_point(&snapshot);
23132                    let start_row = start.row().0;
23133                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23134                    {
23135                        end.row().0.saturating_sub(1)
23136                    } else {
23137                        end.row().0
23138                    };
23139                    for row in start_row..=end_row {
23140                        let used_index =
23141                            used_highlight_orders.entry(row).or_insert(highlight.index);
23142                        if highlight.index >= *used_index {
23143                            *used_index = highlight.index;
23144                            unique_rows.insert(
23145                                DisplayRow(row),
23146                                LineHighlight {
23147                                    include_gutter: highlight.options.include_gutter,
23148                                    border: None,
23149                                    background: highlight.color.into(),
23150                                    type_id: Some(highlight.type_id),
23151                                },
23152                            );
23153                        }
23154                    }
23155                    unique_rows
23156                },
23157            )
23158    }
23159
23160    pub fn highlighted_display_row_for_autoscroll(
23161        &self,
23162        snapshot: &DisplaySnapshot,
23163    ) -> Option<DisplayRow> {
23164        self.highlighted_rows
23165            .values()
23166            .flat_map(|highlighted_rows| highlighted_rows.iter())
23167            .filter_map(|highlight| {
23168                if highlight.options.autoscroll {
23169                    Some(highlight.range.start.to_display_point(snapshot).row())
23170                } else {
23171                    None
23172                }
23173            })
23174            .min()
23175    }
23176
23177    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23178        self.highlight_background(
23179            HighlightKey::SearchWithinRange,
23180            ranges,
23181            |_, colors| colors.colors().editor_document_highlight_read_background,
23182            cx,
23183        )
23184    }
23185
23186    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23187        self.breadcrumb_header = Some(new_header);
23188    }
23189
23190    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23191        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23192    }
23193
23194    pub fn highlight_background(
23195        &mut self,
23196        key: HighlightKey,
23197        ranges: &[Range<Anchor>],
23198        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23199        cx: &mut Context<Self>,
23200    ) {
23201        self.background_highlights
23202            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23203        self.scrollbar_marker_state.dirty = true;
23204        cx.notify();
23205    }
23206
23207    pub fn clear_background_highlights(
23208        &mut self,
23209        key: HighlightKey,
23210        cx: &mut Context<Self>,
23211    ) -> Option<BackgroundHighlight> {
23212        let text_highlights = self.background_highlights.remove(&key)?;
23213        if !text_highlights.1.is_empty() {
23214            self.scrollbar_marker_state.dirty = true;
23215            cx.notify();
23216        }
23217        Some(text_highlights)
23218    }
23219
23220    pub fn highlight_gutter<T: 'static>(
23221        &mut self,
23222        ranges: impl Into<Vec<Range<Anchor>>>,
23223        color_fetcher: fn(&App) -> Hsla,
23224        cx: &mut Context<Self>,
23225    ) {
23226        self.gutter_highlights
23227            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23228        cx.notify();
23229    }
23230
23231    pub fn clear_gutter_highlights<T: 'static>(
23232        &mut self,
23233        cx: &mut Context<Self>,
23234    ) -> Option<GutterHighlight> {
23235        cx.notify();
23236        self.gutter_highlights.remove(&TypeId::of::<T>())
23237    }
23238
23239    pub fn insert_gutter_highlight<T: 'static>(
23240        &mut self,
23241        range: Range<Anchor>,
23242        color_fetcher: fn(&App) -> Hsla,
23243        cx: &mut Context<Self>,
23244    ) {
23245        let snapshot = self.buffer().read(cx).snapshot(cx);
23246        let mut highlights = self
23247            .gutter_highlights
23248            .remove(&TypeId::of::<T>())
23249            .map(|(_, highlights)| highlights)
23250            .unwrap_or_default();
23251        let ix = highlights.binary_search_by(|highlight| {
23252            Ordering::Equal
23253                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23254                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23255        });
23256        if let Err(ix) = ix {
23257            highlights.insert(ix, range);
23258        }
23259        self.gutter_highlights
23260            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23261    }
23262
23263    pub fn remove_gutter_highlights<T: 'static>(
23264        &mut self,
23265        ranges_to_remove: Vec<Range<Anchor>>,
23266        cx: &mut Context<Self>,
23267    ) {
23268        let snapshot = self.buffer().read(cx).snapshot(cx);
23269        let Some((color_fetcher, mut gutter_highlights)) =
23270            self.gutter_highlights.remove(&TypeId::of::<T>())
23271        else {
23272            return;
23273        };
23274        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23275        gutter_highlights.retain(|highlight| {
23276            while let Some(range_to_remove) = ranges_to_remove.peek() {
23277                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23278                    Ordering::Less | Ordering::Equal => {
23279                        ranges_to_remove.next();
23280                    }
23281                    Ordering::Greater => {
23282                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23283                            Ordering::Less | Ordering::Equal => {
23284                                return false;
23285                            }
23286                            Ordering::Greater => break,
23287                        }
23288                    }
23289                }
23290            }
23291
23292            true
23293        });
23294        self.gutter_highlights
23295            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23296    }
23297
23298    #[cfg(any(test, feature = "test-support"))]
23299    pub fn all_text_highlights(
23300        &self,
23301        window: &mut Window,
23302        cx: &mut Context<Self>,
23303    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23304        let snapshot = self.snapshot(window, cx);
23305        self.display_map.update(cx, |display_map, _| {
23306            display_map
23307                .all_text_highlights()
23308                .map(|(_, highlight)| {
23309                    let (style, ranges) = highlight.as_ref();
23310                    (
23311                        *style,
23312                        ranges
23313                            .iter()
23314                            .map(|range| range.clone().to_display_points(&snapshot))
23315                            .collect(),
23316                    )
23317                })
23318                .collect()
23319        })
23320    }
23321
23322    #[cfg(any(test, feature = "test-support"))]
23323    pub fn all_text_background_highlights(
23324        &self,
23325        window: &mut Window,
23326        cx: &mut Context<Self>,
23327    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23328        let snapshot = self.snapshot(window, cx);
23329        let buffer = &snapshot.buffer_snapshot();
23330        let start = buffer.anchor_before(MultiBufferOffset(0));
23331        let end = buffer.anchor_after(buffer.len());
23332        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23333    }
23334
23335    #[cfg(any(test, feature = "test-support"))]
23336    pub fn sorted_background_highlights_in_range(
23337        &self,
23338        search_range: Range<Anchor>,
23339        display_snapshot: &DisplaySnapshot,
23340        theme: &Theme,
23341    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23342        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23343        res.sort_by(|a, b| {
23344            a.0.start
23345                .cmp(&b.0.start)
23346                .then_with(|| a.0.end.cmp(&b.0.end))
23347                .then_with(|| a.1.cmp(&b.1))
23348        });
23349        res
23350    }
23351
23352    #[cfg(any(test, feature = "test-support"))]
23353    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23354        let snapshot = self.buffer().read(cx).snapshot(cx);
23355
23356        let highlights = self
23357            .background_highlights
23358            .get(&HighlightKey::BufferSearchHighlights);
23359
23360        if let Some((_color, ranges)) = highlights {
23361            ranges
23362                .iter()
23363                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23364                .collect_vec()
23365        } else {
23366            vec![]
23367        }
23368    }
23369
23370    fn document_highlights_for_position<'a>(
23371        &'a self,
23372        position: Anchor,
23373        buffer: &'a MultiBufferSnapshot,
23374    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23375        let read_highlights = self
23376            .background_highlights
23377            .get(&HighlightKey::DocumentHighlightRead)
23378            .map(|h| &h.1);
23379        let write_highlights = self
23380            .background_highlights
23381            .get(&HighlightKey::DocumentHighlightWrite)
23382            .map(|h| &h.1);
23383        let left_position = position.bias_left(buffer);
23384        let right_position = position.bias_right(buffer);
23385        read_highlights
23386            .into_iter()
23387            .chain(write_highlights)
23388            .flat_map(move |ranges| {
23389                let start_ix = match ranges.binary_search_by(|probe| {
23390                    let cmp = probe.end.cmp(&left_position, buffer);
23391                    if cmp.is_ge() {
23392                        Ordering::Greater
23393                    } else {
23394                        Ordering::Less
23395                    }
23396                }) {
23397                    Ok(i) | Err(i) => i,
23398                };
23399
23400                ranges[start_ix..]
23401                    .iter()
23402                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23403            })
23404    }
23405
23406    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23407        self.background_highlights
23408            .get(&key)
23409            .is_some_and(|(_, highlights)| !highlights.is_empty())
23410    }
23411
23412    /// Returns all background highlights for a given range.
23413    ///
23414    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23415    pub fn background_highlights_in_range(
23416        &self,
23417        search_range: Range<Anchor>,
23418        display_snapshot: &DisplaySnapshot,
23419        theme: &Theme,
23420    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23421        let mut results = Vec::new();
23422        for (color_fetcher, ranges) in self.background_highlights.values() {
23423            let start_ix = match ranges.binary_search_by(|probe| {
23424                let cmp = probe
23425                    .end
23426                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23427                if cmp.is_gt() {
23428                    Ordering::Greater
23429                } else {
23430                    Ordering::Less
23431                }
23432            }) {
23433                Ok(i) | Err(i) => i,
23434            };
23435            for (index, range) in ranges[start_ix..].iter().enumerate() {
23436                if range
23437                    .start
23438                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23439                    .is_ge()
23440                {
23441                    break;
23442                }
23443
23444                let color = color_fetcher(&(start_ix + index), theme);
23445                let start = range.start.to_display_point(display_snapshot);
23446                let end = range.end.to_display_point(display_snapshot);
23447                results.push((start..end, color))
23448            }
23449        }
23450        results
23451    }
23452
23453    pub fn gutter_highlights_in_range(
23454        &self,
23455        search_range: Range<Anchor>,
23456        display_snapshot: &DisplaySnapshot,
23457        cx: &App,
23458    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23459        let mut results = Vec::new();
23460        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23461            let color = color_fetcher(cx);
23462            let start_ix = match ranges.binary_search_by(|probe| {
23463                let cmp = probe
23464                    .end
23465                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23466                if cmp.is_gt() {
23467                    Ordering::Greater
23468                } else {
23469                    Ordering::Less
23470                }
23471            }) {
23472                Ok(i) | Err(i) => i,
23473            };
23474            for range in &ranges[start_ix..] {
23475                if range
23476                    .start
23477                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23478                    .is_ge()
23479                {
23480                    break;
23481                }
23482
23483                let start = range.start.to_display_point(display_snapshot);
23484                let end = range.end.to_display_point(display_snapshot);
23485                results.push((start..end, color))
23486            }
23487        }
23488        results
23489    }
23490
23491    /// Get the text ranges corresponding to the redaction query
23492    pub fn redacted_ranges(
23493        &self,
23494        search_range: Range<Anchor>,
23495        display_snapshot: &DisplaySnapshot,
23496        cx: &App,
23497    ) -> Vec<Range<DisplayPoint>> {
23498        display_snapshot
23499            .buffer_snapshot()
23500            .redacted_ranges(search_range, |file| {
23501                if let Some(file) = file {
23502                    file.is_private()
23503                        && EditorSettings::get(
23504                            Some(SettingsLocation {
23505                                worktree_id: file.worktree_id(cx),
23506                                path: file.path().as_ref(),
23507                            }),
23508                            cx,
23509                        )
23510                        .redact_private_values
23511                } else {
23512                    false
23513                }
23514            })
23515            .map(|range| {
23516                range.start.to_display_point(display_snapshot)
23517                    ..range.end.to_display_point(display_snapshot)
23518            })
23519            .collect()
23520    }
23521
23522    pub fn highlight_text_key(
23523        &mut self,
23524        key: HighlightKey,
23525        ranges: Vec<Range<Anchor>>,
23526        style: HighlightStyle,
23527        merge: bool,
23528        cx: &mut Context<Self>,
23529    ) {
23530        self.display_map.update(cx, |map, cx| {
23531            map.highlight_text(key, ranges, style, merge, cx);
23532        });
23533        cx.notify();
23534    }
23535
23536    pub fn highlight_text(
23537        &mut self,
23538        key: HighlightKey,
23539        ranges: Vec<Range<Anchor>>,
23540        style: HighlightStyle,
23541        cx: &mut Context<Self>,
23542    ) {
23543        self.display_map.update(cx, |map, cx| {
23544            map.highlight_text(key, ranges, style, false, cx)
23545        });
23546        cx.notify();
23547    }
23548
23549    pub fn text_highlights<'a>(
23550        &'a self,
23551        key: HighlightKey,
23552        cx: &'a App,
23553    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23554        self.display_map.read(cx).text_highlights(key)
23555    }
23556
23557    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23558        let cleared = self
23559            .display_map
23560            .update(cx, |map, _| map.clear_highlights(key));
23561        if cleared {
23562            cx.notify();
23563        }
23564    }
23565
23566    pub fn clear_highlights_with(
23567        &mut self,
23568        f: &mut dyn FnMut(&HighlightKey) -> bool,
23569        cx: &mut Context<Self>,
23570    ) {
23571        let cleared = self
23572            .display_map
23573            .update(cx, |map, _| map.clear_highlights_with(f));
23574        if cleared {
23575            cx.notify();
23576        }
23577    }
23578
23579    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23580        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23581            && self.focus_handle.is_focused(window)
23582    }
23583
23584    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23585        self.show_cursor_when_unfocused = is_enabled;
23586        cx.notify();
23587    }
23588
23589    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23590        cx.notify();
23591    }
23592
23593    fn on_debug_session_event(
23594        &mut self,
23595        _session: Entity<Session>,
23596        event: &SessionEvent,
23597        cx: &mut Context<Self>,
23598    ) {
23599        if let SessionEvent::InvalidateInlineValue = event {
23600            self.refresh_inline_values(cx);
23601        }
23602    }
23603
23604    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23605        let Some(semantics) = self.semantics_provider.clone() else {
23606            return;
23607        };
23608
23609        if !self.inline_value_cache.enabled {
23610            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23611            self.splice_inlays(&inlays, Vec::new(), cx);
23612            return;
23613        }
23614
23615        let current_execution_position = self
23616            .highlighted_rows
23617            .get(&TypeId::of::<ActiveDebugLine>())
23618            .and_then(|lines| lines.last().map(|line| line.range.end));
23619
23620        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23621            let inline_values = editor
23622                .update(cx, |editor, cx| {
23623                    let Some(current_execution_position) = current_execution_position else {
23624                        return Some(Task::ready(Ok(Vec::new())));
23625                    };
23626
23627                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23628                        let snapshot = buffer.snapshot(cx);
23629
23630                        let excerpt = snapshot.excerpt_containing(
23631                            current_execution_position..current_execution_position,
23632                        )?;
23633
23634                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23635                    })?;
23636
23637                    let range =
23638                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23639
23640                    semantics.inline_values(buffer, range, cx)
23641                })
23642                .ok()
23643                .flatten()?
23644                .await
23645                .context("refreshing debugger inlays")
23646                .log_err()?;
23647
23648            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23649
23650            for (buffer_id, inline_value) in inline_values
23651                .into_iter()
23652                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23653            {
23654                buffer_inline_values
23655                    .entry(buffer_id)
23656                    .or_default()
23657                    .push(inline_value);
23658            }
23659
23660            editor
23661                .update(cx, |editor, cx| {
23662                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23663                    let mut new_inlays = Vec::default();
23664
23665                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23666                        let buffer_id = buffer_snapshot.remote_id();
23667                        buffer_inline_values
23668                            .get(&buffer_id)
23669                            .into_iter()
23670                            .flatten()
23671                            .for_each(|hint| {
23672                                let inlay = Inlay::debugger(
23673                                    post_inc(&mut editor.next_inlay_id),
23674                                    Anchor::in_buffer(excerpt_id, hint.position),
23675                                    hint.text(),
23676                                );
23677                                if !inlay.text().chars().contains(&'\n') {
23678                                    new_inlays.push(inlay);
23679                                }
23680                            });
23681                    }
23682
23683                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23684                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23685
23686                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23687                })
23688                .ok()?;
23689            Some(())
23690        });
23691    }
23692
23693    fn on_buffer_event(
23694        &mut self,
23695        multibuffer: &Entity<MultiBuffer>,
23696        event: &multi_buffer::Event,
23697        window: &mut Window,
23698        cx: &mut Context<Self>,
23699    ) {
23700        match event {
23701            multi_buffer::Event::Edited {
23702                edited_buffer,
23703                is_local,
23704            } => {
23705                self.scrollbar_marker_state.dirty = true;
23706                self.active_indent_guides_state.dirty = true;
23707                self.refresh_active_diagnostics(cx);
23708                self.refresh_code_actions(window, cx);
23709                self.refresh_single_line_folds(window, cx);
23710                let snapshot = self.snapshot(window, cx);
23711                self.refresh_matching_bracket_highlights(&snapshot, cx);
23712                self.refresh_outline_symbols_at_cursor(cx);
23713                self.refresh_sticky_headers(&snapshot, cx);
23714                if *is_local && self.has_active_edit_prediction() {
23715                    self.update_visible_edit_prediction(window, cx);
23716                }
23717
23718                // Clean up orphaned review comments after edits
23719                self.cleanup_orphaned_review_comments(cx);
23720
23721                if let Some(buffer) = edited_buffer {
23722                    if buffer.read(cx).file().is_none() {
23723                        cx.emit(EditorEvent::TitleChanged);
23724                    }
23725
23726                    if self.project.is_some() {
23727                        let buffer_id = buffer.read(cx).remote_id();
23728                        self.register_buffer(buffer_id, cx);
23729                        self.update_lsp_data(Some(buffer_id), window, cx);
23730                        self.refresh_inlay_hints(
23731                            InlayHintRefreshReason::BufferEdited(buffer_id),
23732                            cx,
23733                        );
23734                    }
23735                }
23736
23737                cx.emit(EditorEvent::BufferEdited);
23738                cx.emit(SearchEvent::MatchesInvalidated);
23739
23740                let Some(project) = &self.project else { return };
23741                let (telemetry, is_via_ssh) = {
23742                    let project = project.read(cx);
23743                    let telemetry = project.client().telemetry().clone();
23744                    let is_via_ssh = project.is_via_remote_server();
23745                    (telemetry, is_via_ssh)
23746                };
23747                telemetry.log_edit_event("editor", is_via_ssh);
23748            }
23749            multi_buffer::Event::ExcerptsAdded {
23750                buffer,
23751                predecessor,
23752                excerpts,
23753            } => {
23754                let buffer_id = buffer.read(cx).remote_id();
23755                if self.buffer.read(cx).diff_for(buffer_id).is_none()
23756                    && let Some(project) = &self.project
23757                {
23758                    update_uncommitted_diff_for_buffer(
23759                        cx.entity(),
23760                        project,
23761                        [buffer.clone()],
23762                        self.buffer.clone(),
23763                        cx,
23764                    )
23765                    .detach();
23766                }
23767                self.semantic_token_state
23768                    .invalidate_buffer(&buffer.read(cx).remote_id());
23769                self.update_lsp_data(Some(buffer_id), window, cx);
23770                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23771                self.refresh_runnables(window, cx);
23772                self.colorize_brackets(false, cx);
23773                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
23774                cx.emit(EditorEvent::ExcerptsAdded {
23775                    buffer: buffer.clone(),
23776                    predecessor: *predecessor,
23777                    excerpts: excerpts.clone(),
23778                });
23779            }
23780            multi_buffer::Event::ExcerptsRemoved {
23781                ids,
23782                removed_buffer_ids,
23783            } => {
23784                if let Some(inlay_hints) = &mut self.inlay_hints {
23785                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
23786                }
23787                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
23788                for buffer_id in removed_buffer_ids {
23789                    self.registered_buffers.remove(buffer_id);
23790                    self.clear_runnables(Some(*buffer_id));
23791                    self.semantic_token_state.invalidate_buffer(buffer_id);
23792                    self.display_map.update(cx, |display_map, cx| {
23793                        display_map.invalidate_semantic_highlights(*buffer_id);
23794                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
23795                    });
23796                }
23797
23798                self.display_map.update(cx, |display_map, cx| {
23799                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
23800                });
23801
23802                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23803                cx.emit(EditorEvent::ExcerptsRemoved {
23804                    ids: ids.clone(),
23805                    removed_buffer_ids: removed_buffer_ids.clone(),
23806                });
23807            }
23808            multi_buffer::Event::ExcerptsEdited {
23809                excerpt_ids,
23810                buffer_ids,
23811            } => {
23812                self.display_map.update(cx, |map, cx| {
23813                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
23814                });
23815                cx.emit(EditorEvent::ExcerptsEdited {
23816                    ids: excerpt_ids.clone(),
23817                });
23818            }
23819            multi_buffer::Event::ExcerptsExpanded { ids } => {
23820                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
23821                self.refresh_document_highlights(cx);
23822                let snapshot = multibuffer.read(cx).snapshot(cx);
23823                for id in ids {
23824                    self.bracket_fetched_tree_sitter_chunks.remove(id);
23825                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
23826                        self.semantic_token_state
23827                            .invalidate_buffer(&buffer.remote_id());
23828                    }
23829                }
23830                self.colorize_brackets(false, cx);
23831                self.update_lsp_data(None, window, cx);
23832                self.refresh_runnables(window, cx);
23833                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
23834            }
23835            multi_buffer::Event::Reparsed(buffer_id) => {
23836                self.clear_runnables(Some(*buffer_id));
23837                self.refresh_runnables(window, cx);
23838                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
23839                self.colorize_brackets(true, cx);
23840                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23841
23842                cx.emit(EditorEvent::Reparsed(*buffer_id));
23843            }
23844            multi_buffer::Event::DiffHunksToggled => {
23845                self.refresh_runnables(window, cx);
23846            }
23847            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
23848                if !is_fresh_language {
23849                    self.registered_buffers.remove(&buffer_id);
23850                }
23851                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
23852                cx.emit(EditorEvent::Reparsed(*buffer_id));
23853                cx.notify();
23854            }
23855            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
23856            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
23857            multi_buffer::Event::FileHandleChanged
23858            | multi_buffer::Event::Reloaded
23859            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
23860            multi_buffer::Event::DiagnosticsUpdated => {
23861                self.update_diagnostics_state(window, cx);
23862            }
23863            _ => {}
23864        };
23865    }
23866
23867    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
23868        if !self.diagnostics_enabled() {
23869            return;
23870        }
23871        self.refresh_active_diagnostics(cx);
23872        self.refresh_inline_diagnostics(true, window, cx);
23873        self.scrollbar_marker_state.dirty = true;
23874        cx.notify();
23875    }
23876
23877    pub fn start_temporary_diff_override(&mut self) {
23878        self.load_diff_task.take();
23879        self.temporary_diff_override = true;
23880    }
23881
23882    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
23883        self.temporary_diff_override = false;
23884        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
23885        self.buffer.update(cx, |buffer, cx| {
23886            buffer.set_all_diff_hunks_collapsed(cx);
23887        });
23888
23889        if let Some(project) = self.project.clone() {
23890            self.load_diff_task = Some(
23891                update_uncommitted_diff_for_buffer(
23892                    cx.entity(),
23893                    &project,
23894                    self.buffer.read(cx).all_buffers(),
23895                    self.buffer.clone(),
23896                    cx,
23897                )
23898                .shared(),
23899            );
23900        }
23901    }
23902
23903    fn on_display_map_changed(
23904        &mut self,
23905        _: Entity<DisplayMap>,
23906        _: &mut Window,
23907        cx: &mut Context<Self>,
23908    ) {
23909        cx.notify();
23910    }
23911
23912    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
23913        if !self.mode.is_full() {
23914            return None;
23915        }
23916
23917        let theme_settings = theme::ThemeSettings::get_global(cx);
23918        let theme = cx.theme();
23919        let accent_colors = theme.accents().clone();
23920
23921        let accent_overrides = theme_settings
23922            .theme_overrides
23923            .get(theme.name.as_ref())
23924            .map(|theme_style| &theme_style.accents)
23925            .into_iter()
23926            .flatten()
23927            .chain(
23928                theme_settings
23929                    .experimental_theme_overrides
23930                    .as_ref()
23931                    .map(|overrides| &overrides.accents)
23932                    .into_iter()
23933                    .flatten(),
23934            )
23935            .flat_map(|accent| accent.0.clone().map(SharedString::from))
23936            .collect();
23937
23938        Some(AccentData {
23939            colors: accent_colors,
23940            overrides: accent_overrides,
23941        })
23942    }
23943
23944    fn fetch_applicable_language_settings(
23945        &self,
23946        cx: &App,
23947    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
23948        if !self.mode.is_full() {
23949            return HashMap::default();
23950        }
23951
23952        self.buffer().read(cx).all_buffers().into_iter().fold(
23953            HashMap::default(),
23954            |mut acc, buffer| {
23955                let buffer = buffer.read(cx);
23956                let language = buffer.language().map(|language| language.name());
23957                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
23958                    let file = buffer.file();
23959                    v.insert(language_settings(language, file, cx).into_owned());
23960                }
23961                acc
23962            },
23963        )
23964    }
23965
23966    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
23967        let new_language_settings = self.fetch_applicable_language_settings(cx);
23968        let language_settings_changed = new_language_settings != self.applicable_language_settings;
23969        self.applicable_language_settings = new_language_settings;
23970
23971        let new_accents = self.fetch_accent_data(cx);
23972        let accents_changed = new_accents != self.accent_data;
23973        self.accent_data = new_accents;
23974
23975        if self.diagnostics_enabled() {
23976            let new_severity = EditorSettings::get_global(cx)
23977                .diagnostics_max_severity
23978                .unwrap_or(DiagnosticSeverity::Hint);
23979            self.set_max_diagnostics_severity(new_severity, cx);
23980        }
23981        self.refresh_runnables(window, cx);
23982        self.update_edit_prediction_settings(cx);
23983        self.refresh_edit_prediction(true, false, window, cx);
23984        self.refresh_inline_values(cx);
23985
23986        let old_cursor_shape = self.cursor_shape;
23987        let old_show_breadcrumbs = self.show_breadcrumbs;
23988
23989        {
23990            let editor_settings = EditorSettings::get_global(cx);
23991            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
23992            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
23993            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
23994            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
23995        }
23996
23997        if old_cursor_shape != self.cursor_shape {
23998            cx.emit(EditorEvent::CursorShapeChanged);
23999        }
24000
24001        if old_show_breadcrumbs != self.show_breadcrumbs {
24002            cx.emit(EditorEvent::BreadcrumbsChanged);
24003        }
24004
24005        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24006            let project_settings = ProjectSettings::get_global(cx);
24007            (
24008                project_settings.session.restore_unsaved_buffers,
24009                project_settings.diagnostics.inline.enabled,
24010                project_settings.git.inline_blame.enabled,
24011            )
24012        };
24013        self.buffer_serialization = self
24014            .should_serialize_buffer()
24015            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24016
24017        if self.mode.is_full() {
24018            if self.show_inline_diagnostics != show_inline_diagnostics {
24019                self.show_inline_diagnostics = show_inline_diagnostics;
24020                self.refresh_inline_diagnostics(false, window, cx);
24021            }
24022
24023            if self.git_blame_inline_enabled != inline_blame_enabled {
24024                self.toggle_git_blame_inline_internal(false, window, cx);
24025            }
24026
24027            let minimap_settings = EditorSettings::get_global(cx).minimap;
24028            if self.minimap_visibility != MinimapVisibility::Disabled {
24029                if self.minimap_visibility.settings_visibility()
24030                    != minimap_settings.minimap_enabled()
24031                {
24032                    self.set_minimap_visibility(
24033                        MinimapVisibility::for_mode(self.mode(), cx),
24034                        window,
24035                        cx,
24036                    );
24037                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24038                    minimap_entity.update(cx, |minimap_editor, cx| {
24039                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24040                    })
24041                }
24042            }
24043
24044            if language_settings_changed || accents_changed {
24045                self.colorize_brackets(true, cx);
24046            }
24047
24048            if language_settings_changed {
24049                self.clear_disabled_lsp_folding_ranges(window, cx);
24050                self.refresh_document_symbols(None, cx);
24051            }
24052
24053            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24054                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24055            }) {
24056                if !inlay_splice.is_empty() {
24057                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24058                }
24059                self.refresh_document_colors(None, window, cx);
24060            }
24061
24062            self.refresh_inlay_hints(
24063                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24064                    self.selections.newest_anchor().head(),
24065                    &self.buffer.read(cx).snapshot(cx),
24066                    cx,
24067                )),
24068                cx,
24069            );
24070
24071            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24072                .global_lsp_settings
24073                .semantic_token_rules
24074                .clone();
24075            let semantic_token_rules_changed = self
24076                .semantic_token_state
24077                .update_rules(new_semantic_token_rules);
24078            if language_settings_changed || semantic_token_rules_changed {
24079                self.invalidate_semantic_tokens(None);
24080                self.refresh_semantic_tokens(None, None, cx);
24081            }
24082        }
24083
24084        cx.notify();
24085    }
24086
24087    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24088        if !self.mode.is_full() {
24089            return;
24090        }
24091
24092        let new_accents = self.fetch_accent_data(cx);
24093        if new_accents != self.accent_data {
24094            self.accent_data = new_accents;
24095            self.colorize_brackets(true, cx);
24096        }
24097
24098        self.invalidate_semantic_tokens(None);
24099        self.refresh_semantic_tokens(None, None, cx);
24100    }
24101
24102    pub fn set_searchable(&mut self, searchable: bool) {
24103        self.searchable = searchable;
24104    }
24105
24106    pub fn searchable(&self) -> bool {
24107        self.searchable
24108    }
24109
24110    pub fn open_excerpts_in_split(
24111        &mut self,
24112        _: &OpenExcerptsSplit,
24113        window: &mut Window,
24114        cx: &mut Context<Self>,
24115    ) {
24116        self.open_excerpts_common(None, true, window, cx)
24117    }
24118
24119    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24120        self.open_excerpts_common(None, false, window, cx)
24121    }
24122
24123    pub(crate) fn open_excerpts_common(
24124        &mut self,
24125        jump_data: Option<JumpData>,
24126        split: bool,
24127        window: &mut Window,
24128        cx: &mut Context<Self>,
24129    ) {
24130        if self.buffer.read(cx).is_singleton() {
24131            cx.propagate();
24132            return;
24133        }
24134
24135        let mut new_selections_by_buffer = HashMap::default();
24136        match &jump_data {
24137            Some(JumpData::MultiBufferPoint {
24138                excerpt_id,
24139                position,
24140                anchor,
24141                line_offset_from_top,
24142            }) => {
24143                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24144                if let Some(buffer) = multi_buffer_snapshot
24145                    .buffer_id_for_excerpt(*excerpt_id)
24146                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24147                {
24148                    let buffer_snapshot = buffer.read(cx).snapshot();
24149                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24150                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24151                    } else {
24152                        buffer_snapshot.clip_point(*position, Bias::Left)
24153                    };
24154                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24155                    new_selections_by_buffer.insert(
24156                        buffer,
24157                        (
24158                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24159                            Some(*line_offset_from_top),
24160                        ),
24161                    );
24162                }
24163            }
24164            Some(JumpData::MultiBufferRow {
24165                row,
24166                line_offset_from_top,
24167            }) => {
24168                let point = MultiBufferPoint::new(row.0, 0);
24169                if let Some((buffer, buffer_point, _)) =
24170                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24171                {
24172                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24173                    new_selections_by_buffer
24174                        .entry(buffer)
24175                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24176                        .0
24177                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24178                }
24179            }
24180            None => {
24181                let selections = self
24182                    .selections
24183                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24184                let multi_buffer = self.buffer.read(cx);
24185                for selection in selections {
24186                    for (snapshot, range, _, anchor) in multi_buffer
24187                        .snapshot(cx)
24188                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24189                    {
24190                        if let Some(anchor) = anchor {
24191                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24192                            else {
24193                                continue;
24194                            };
24195                            let offset = text::ToOffset::to_offset(
24196                                &anchor.text_anchor,
24197                                &buffer_handle.read(cx).snapshot(),
24198                            );
24199                            let range = BufferOffset(offset)..BufferOffset(offset);
24200                            new_selections_by_buffer
24201                                .entry(buffer_handle)
24202                                .or_insert((Vec::new(), None))
24203                                .0
24204                                .push(range)
24205                        } else {
24206                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24207                            else {
24208                                continue;
24209                            };
24210                            new_selections_by_buffer
24211                                .entry(buffer_handle)
24212                                .or_insert((Vec::new(), None))
24213                                .0
24214                                .push(range)
24215                        }
24216                    }
24217                }
24218            }
24219        }
24220
24221        if self.delegate_open_excerpts {
24222            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24223                .into_iter()
24224                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24225                .collect();
24226            if !selections_by_buffer.is_empty() {
24227                cx.emit(EditorEvent::OpenExcerptsRequested {
24228                    selections_by_buffer,
24229                    split,
24230                });
24231            }
24232            return;
24233        }
24234
24235        let Some(workspace) = self.workspace() else {
24236            cx.propagate();
24237            return;
24238        };
24239
24240        new_selections_by_buffer
24241            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24242
24243        if new_selections_by_buffer.is_empty() {
24244            return;
24245        }
24246
24247        Self::open_buffers_in_workspace(
24248            workspace.downgrade(),
24249            new_selections_by_buffer,
24250            split,
24251            window,
24252            cx,
24253        );
24254    }
24255
24256    pub(crate) fn open_buffers_in_workspace(
24257        workspace: WeakEntity<Workspace>,
24258        new_selections_by_buffer: HashMap<
24259            Entity<language::Buffer>,
24260            (Vec<Range<BufferOffset>>, Option<u32>),
24261        >,
24262        split: bool,
24263        window: &mut Window,
24264        cx: &mut App,
24265    ) {
24266        // We defer the pane interaction because we ourselves are a workspace item
24267        // and activating a new item causes the pane to call a method on us reentrantly,
24268        // which panics if we're on the stack.
24269        window.defer(cx, move |window, cx| {
24270            workspace
24271                .update(cx, |workspace, cx| {
24272                    let pane = if split {
24273                        workspace.adjacent_pane(window, cx)
24274                    } else {
24275                        workspace.active_pane().clone()
24276                    };
24277
24278                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24279                        let buffer_read = buffer.read(cx);
24280                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24281                            (true, project::File::from_dyn(Some(file)).is_some())
24282                        } else {
24283                            (false, false)
24284                        };
24285
24286                        // If project file is none workspace.open_project_item will fail to open the excerpt
24287                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24288                        // so we check if there's a tab match in that case first
24289                        let editor = (!has_file || !is_project_file)
24290                            .then(|| {
24291                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24292                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24293                                // Instead, we try to activate the existing editor in the pane first.
24294                                let (editor, pane_item_index, pane_item_id) =
24295                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24296                                        let editor = item.downcast::<Editor>()?;
24297                                        let singleton_buffer =
24298                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24299                                        if singleton_buffer == buffer {
24300                                            Some((editor, i, item.item_id()))
24301                                        } else {
24302                                            None
24303                                        }
24304                                    })?;
24305                                pane.update(cx, |pane, cx| {
24306                                    pane.activate_item(pane_item_index, true, true, window, cx);
24307                                    if !PreviewTabsSettings::get_global(cx)
24308                                        .enable_preview_from_multibuffer
24309                                    {
24310                                        pane.unpreview_item_if_preview(pane_item_id);
24311                                    }
24312                                });
24313                                Some(editor)
24314                            })
24315                            .flatten()
24316                            .unwrap_or_else(|| {
24317                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24318                                    .enable_keep_preview_on_code_navigation;
24319                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24320                                    .enable_preview_from_multibuffer;
24321                                workspace.open_project_item::<Self>(
24322                                    pane.clone(),
24323                                    buffer,
24324                                    true,
24325                                    true,
24326                                    keep_old_preview,
24327                                    allow_new_preview,
24328                                    window,
24329                                    cx,
24330                                )
24331                            });
24332
24333                        editor.update(cx, |editor, cx| {
24334                            if has_file && !is_project_file {
24335                                editor.set_read_only(true);
24336                            }
24337                            let autoscroll = match scroll_offset {
24338                                Some(scroll_offset) => {
24339                                    Autoscroll::top_relative(scroll_offset as usize)
24340                                }
24341                                None => Autoscroll::newest(),
24342                            };
24343                            let nav_history = editor.nav_history.take();
24344                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24345                            let Some((excerpt_id, _, buffer_snapshot)) =
24346                                multibuffer_snapshot.as_singleton()
24347                            else {
24348                                return;
24349                            };
24350                            editor.change_selections(
24351                                SelectionEffects::scroll(autoscroll),
24352                                window,
24353                                cx,
24354                                |s| {
24355                                    s.select_ranges(ranges.into_iter().map(|range| {
24356                                        let range = buffer_snapshot.anchor_before(range.start)
24357                                            ..buffer_snapshot.anchor_after(range.end);
24358                                        multibuffer_snapshot
24359                                            .anchor_range_in_excerpt(excerpt_id, range)
24360                                            .unwrap()
24361                                    }));
24362                                },
24363                            );
24364                            editor.nav_history = nav_history;
24365                        });
24366                    }
24367                })
24368                .ok();
24369        });
24370    }
24371
24372    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24373        let snapshot = self.buffer.read(cx).read(cx);
24374        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24375        Some(
24376            ranges
24377                .iter()
24378                .map(move |range| {
24379                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24380                })
24381                .collect(),
24382        )
24383    }
24384
24385    fn selection_replacement_ranges(
24386        &self,
24387        range: Range<MultiBufferOffsetUtf16>,
24388        cx: &mut App,
24389    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24390        let selections = self
24391            .selections
24392            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24393        let newest_selection = selections
24394            .iter()
24395            .max_by_key(|selection| selection.id)
24396            .unwrap();
24397        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24398        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24399        let snapshot = self.buffer.read(cx).read(cx);
24400        selections
24401            .into_iter()
24402            .map(|mut selection| {
24403                selection.start.0.0 =
24404                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24405                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24406                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24407                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24408            })
24409            .collect()
24410    }
24411
24412    fn report_editor_event(
24413        &self,
24414        reported_event: ReportEditorEvent,
24415        file_extension: Option<String>,
24416        cx: &App,
24417    ) {
24418        if cfg!(any(test, feature = "test-support")) {
24419            return;
24420        }
24421
24422        let Some(project) = &self.project else { return };
24423
24424        // If None, we are in a file without an extension
24425        let file = self
24426            .buffer
24427            .read(cx)
24428            .as_singleton()
24429            .and_then(|b| b.read(cx).file());
24430        let file_extension = file_extension.or(file
24431            .as_ref()
24432            .and_then(|file| Path::new(file.file_name(cx)).extension())
24433            .and_then(|e| e.to_str())
24434            .map(|a| a.to_string()));
24435
24436        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24437            .map(|vim_mode| vim_mode.0)
24438            .unwrap_or(false);
24439
24440        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24441        let copilot_enabled = edit_predictions_provider
24442            == language::language_settings::EditPredictionProvider::Copilot;
24443        let copilot_enabled_for_language = self
24444            .buffer
24445            .read(cx)
24446            .language_settings(cx)
24447            .show_edit_predictions;
24448
24449        let project = project.read(cx);
24450        let event_type = reported_event.event_type();
24451
24452        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24453            telemetry::event!(
24454                event_type,
24455                type = if auto_saved {"autosave"} else {"manual"},
24456                file_extension,
24457                vim_mode,
24458                copilot_enabled,
24459                copilot_enabled_for_language,
24460                edit_predictions_provider,
24461                is_via_ssh = project.is_via_remote_server(),
24462            );
24463        } else {
24464            telemetry::event!(
24465                event_type,
24466                file_extension,
24467                vim_mode,
24468                copilot_enabled,
24469                copilot_enabled_for_language,
24470                edit_predictions_provider,
24471                is_via_ssh = project.is_via_remote_server(),
24472            );
24473        };
24474    }
24475
24476    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24477    /// with each line being an array of {text, highlight} objects.
24478    fn copy_highlight_json(
24479        &mut self,
24480        _: &CopyHighlightJson,
24481        window: &mut Window,
24482        cx: &mut Context<Self>,
24483    ) {
24484        #[derive(Serialize)]
24485        struct Chunk<'a> {
24486            text: String,
24487            highlight: Option<&'a str>,
24488        }
24489
24490        let snapshot = self.buffer.read(cx).snapshot(cx);
24491        let range = self
24492            .selected_text_range(false, window, cx)
24493            .and_then(|selection| {
24494                if selection.range.is_empty() {
24495                    None
24496                } else {
24497                    Some(
24498                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24499                            selection.range.start,
24500                        )))
24501                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24502                                selection.range.end,
24503                            ))),
24504                    )
24505                }
24506            })
24507            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24508
24509        let chunks = snapshot.chunks(range, true);
24510        let mut lines = Vec::new();
24511        let mut line: VecDeque<Chunk> = VecDeque::new();
24512
24513        let Some(style) = self.style.as_ref() else {
24514            return;
24515        };
24516
24517        for chunk in chunks {
24518            let highlight = chunk
24519                .syntax_highlight_id
24520                .and_then(|id| id.name(&style.syntax));
24521            let mut chunk_lines = chunk.text.split('\n').peekable();
24522            while let Some(text) = chunk_lines.next() {
24523                let mut merged_with_last_token = false;
24524                if let Some(last_token) = line.back_mut()
24525                    && last_token.highlight == highlight
24526                {
24527                    last_token.text.push_str(text);
24528                    merged_with_last_token = true;
24529                }
24530
24531                if !merged_with_last_token {
24532                    line.push_back(Chunk {
24533                        text: text.into(),
24534                        highlight,
24535                    });
24536                }
24537
24538                if chunk_lines.peek().is_some() {
24539                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24540                        line.pop_front();
24541                    }
24542                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24543                        line.pop_back();
24544                    }
24545
24546                    lines.push(mem::take(&mut line));
24547                }
24548            }
24549        }
24550
24551        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24552            return;
24553        };
24554        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24555    }
24556
24557    pub fn open_context_menu(
24558        &mut self,
24559        _: &OpenContextMenu,
24560        window: &mut Window,
24561        cx: &mut Context<Self>,
24562    ) {
24563        self.request_autoscroll(Autoscroll::newest(), cx);
24564        let position = self
24565            .selections
24566            .newest_display(&self.display_snapshot(cx))
24567            .start;
24568        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24569    }
24570
24571    pub fn replay_insert_event(
24572        &mut self,
24573        text: &str,
24574        relative_utf16_range: Option<Range<isize>>,
24575        window: &mut Window,
24576        cx: &mut Context<Self>,
24577    ) {
24578        if !self.input_enabled {
24579            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24580            return;
24581        }
24582        if let Some(relative_utf16_range) = relative_utf16_range {
24583            let selections = self
24584                .selections
24585                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24586            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24587                let new_ranges = selections.into_iter().map(|range| {
24588                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24589                        range
24590                            .head()
24591                            .0
24592                            .0
24593                            .saturating_add_signed(relative_utf16_range.start),
24594                    ));
24595                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24596                        range
24597                            .head()
24598                            .0
24599                            .0
24600                            .saturating_add_signed(relative_utf16_range.end),
24601                    ));
24602                    start..end
24603                });
24604                s.select_ranges(new_ranges);
24605            });
24606        }
24607
24608        self.handle_input(text, window, cx);
24609    }
24610
24611    pub fn is_focused(&self, window: &Window) -> bool {
24612        self.focus_handle.is_focused(window)
24613    }
24614
24615    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24616        cx.emit(EditorEvent::Focused);
24617
24618        if let Some(descendant) = self
24619            .last_focused_descendant
24620            .take()
24621            .and_then(|descendant| descendant.upgrade())
24622        {
24623            window.focus(&descendant, cx);
24624        } else {
24625            if let Some(blame) = self.blame.as_ref() {
24626                blame.update(cx, GitBlame::focus)
24627            }
24628
24629            self.blink_manager.update(cx, BlinkManager::enable);
24630            self.show_cursor_names(window, cx);
24631            self.buffer.update(cx, |buffer, cx| {
24632                buffer.finalize_last_transaction(cx);
24633                if self.leader_id.is_none() {
24634                    buffer.set_active_selections(
24635                        &self.selections.disjoint_anchors_arc(),
24636                        self.selections.line_mode(),
24637                        self.cursor_shape,
24638                        cx,
24639                    );
24640                }
24641            });
24642
24643            if let Some(position_map) = self.last_position_map.clone() {
24644                EditorElement::mouse_moved(
24645                    self,
24646                    &MouseMoveEvent {
24647                        position: window.mouse_position(),
24648                        pressed_button: None,
24649                        modifiers: window.modifiers(),
24650                    },
24651                    &position_map,
24652                    None,
24653                    window,
24654                    cx,
24655                );
24656            }
24657        }
24658    }
24659
24660    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24661        cx.emit(EditorEvent::FocusedIn)
24662    }
24663
24664    fn handle_focus_out(
24665        &mut self,
24666        event: FocusOutEvent,
24667        _window: &mut Window,
24668        cx: &mut Context<Self>,
24669    ) {
24670        if event.blurred != self.focus_handle {
24671            self.last_focused_descendant = Some(event.blurred);
24672        }
24673        self.selection_drag_state = SelectionDragState::None;
24674        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24675    }
24676
24677    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24678        self.blink_manager.update(cx, BlinkManager::disable);
24679        self.buffer
24680            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24681
24682        if let Some(blame) = self.blame.as_ref() {
24683            blame.update(cx, GitBlame::blur)
24684        }
24685        if !self.hover_state.focused(window, cx) {
24686            hide_hover(self, cx);
24687        }
24688        if !self
24689            .context_menu
24690            .borrow()
24691            .as_ref()
24692            .is_some_and(|context_menu| context_menu.focused(window, cx))
24693        {
24694            self.hide_context_menu(window, cx);
24695        }
24696        self.take_active_edit_prediction(cx);
24697        cx.emit(EditorEvent::Blurred);
24698        cx.notify();
24699    }
24700
24701    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24702        let mut pending: String = window
24703            .pending_input_keystrokes()
24704            .into_iter()
24705            .flatten()
24706            .filter_map(|keystroke| keystroke.key_char.clone())
24707            .collect();
24708
24709        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24710            pending = "".to_string();
24711        }
24712
24713        let existing_pending = self
24714            .text_highlights(HighlightKey::PendingInput, cx)
24715            .map(|(_, ranges)| ranges.to_vec());
24716        if existing_pending.is_none() && pending.is_empty() {
24717            return;
24718        }
24719        let transaction =
24720            self.transact(window, cx, |this, window, cx| {
24721                let selections = this
24722                    .selections
24723                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
24724                let edits = selections
24725                    .iter()
24726                    .map(|selection| (selection.end..selection.end, pending.clone()));
24727                this.edit(edits, cx);
24728                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24729                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
24730                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
24731                    }));
24732                });
24733                if let Some(existing_ranges) = existing_pending {
24734                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
24735                    this.edit(edits, cx);
24736                }
24737            });
24738
24739        let snapshot = self.snapshot(window, cx);
24740        let ranges = self
24741            .selections
24742            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
24743            .into_iter()
24744            .map(|selection| {
24745                snapshot.buffer_snapshot().anchor_after(selection.end)
24746                    ..snapshot
24747                        .buffer_snapshot()
24748                        .anchor_before(selection.end + pending.len())
24749            })
24750            .collect();
24751
24752        if pending.is_empty() {
24753            self.clear_highlights(HighlightKey::PendingInput, cx);
24754        } else {
24755            self.highlight_text(
24756                HighlightKey::PendingInput,
24757                ranges,
24758                HighlightStyle {
24759                    underline: Some(UnderlineStyle {
24760                        thickness: px(1.),
24761                        color: None,
24762                        wavy: false,
24763                    }),
24764                    ..Default::default()
24765                },
24766                cx,
24767            );
24768        }
24769
24770        self.ime_transaction = self.ime_transaction.or(transaction);
24771        if let Some(transaction) = self.ime_transaction {
24772            self.buffer.update(cx, |buffer, cx| {
24773                buffer.group_until_transaction(transaction, cx);
24774            });
24775        }
24776
24777        if self
24778            .text_highlights(HighlightKey::PendingInput, cx)
24779            .is_none()
24780        {
24781            self.ime_transaction.take();
24782        }
24783    }
24784
24785    pub fn register_action_renderer(
24786        &mut self,
24787        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
24788    ) -> Subscription {
24789        let id = self.next_editor_action_id.post_inc();
24790        self.editor_actions
24791            .borrow_mut()
24792            .insert(id, Box::new(listener));
24793
24794        let editor_actions = self.editor_actions.clone();
24795        Subscription::new(move || {
24796            editor_actions.borrow_mut().remove(&id);
24797        })
24798    }
24799
24800    pub fn register_action<A: Action>(
24801        &mut self,
24802        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
24803    ) -> Subscription {
24804        let id = self.next_editor_action_id.post_inc();
24805        let listener = Arc::new(listener);
24806        self.editor_actions.borrow_mut().insert(
24807            id,
24808            Box::new(move |_, window, _| {
24809                let listener = listener.clone();
24810                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
24811                    let action = action.downcast_ref().unwrap();
24812                    if phase == DispatchPhase::Bubble {
24813                        listener(action, window, cx)
24814                    }
24815                })
24816            }),
24817        );
24818
24819        let editor_actions = self.editor_actions.clone();
24820        Subscription::new(move || {
24821            editor_actions.borrow_mut().remove(&id);
24822        })
24823    }
24824
24825    pub fn file_header_size(&self) -> u32 {
24826        FILE_HEADER_HEIGHT
24827    }
24828
24829    pub fn restore(
24830        &mut self,
24831        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
24832        window: &mut Window,
24833        cx: &mut Context<Self>,
24834    ) {
24835        self.buffer().update(cx, |multi_buffer, cx| {
24836            for (buffer_id, changes) in revert_changes {
24837                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
24838                    buffer.update(cx, |buffer, cx| {
24839                        buffer.edit(
24840                            changes
24841                                .into_iter()
24842                                .map(|(range, text)| (range, text.to_string())),
24843                            None,
24844                            cx,
24845                        );
24846                    });
24847                }
24848            }
24849        });
24850        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
24851            selections.refresh()
24852        });
24853    }
24854
24855    pub fn to_pixel_point(
24856        &mut self,
24857        source: Anchor,
24858        editor_snapshot: &EditorSnapshot,
24859        window: &mut Window,
24860        cx: &mut App,
24861    ) -> Option<gpui::Point<Pixels>> {
24862        let source_point = source.to_display_point(editor_snapshot);
24863        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
24864    }
24865
24866    pub fn display_to_pixel_point(
24867        &mut self,
24868        source: DisplayPoint,
24869        editor_snapshot: &EditorSnapshot,
24870        window: &mut Window,
24871        cx: &mut App,
24872    ) -> Option<gpui::Point<Pixels>> {
24873        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
24874        let text_layout_details = self.text_layout_details(window, cx);
24875        let scroll_top = text_layout_details
24876            .scroll_anchor
24877            .scroll_position(editor_snapshot)
24878            .y;
24879
24880        if source.row().as_f64() < scroll_top.floor() {
24881            return None;
24882        }
24883        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
24884        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
24885        Some(gpui::Point::new(source_x, source_y))
24886    }
24887
24888    pub fn has_visible_completions_menu(&self) -> bool {
24889        !self.edit_prediction_preview_is_active()
24890            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
24891                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
24892            })
24893    }
24894
24895    pub fn register_addon<T: Addon>(&mut self, instance: T) {
24896        if self.mode.is_minimap() {
24897            return;
24898        }
24899        self.addons
24900            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
24901    }
24902
24903    pub fn unregister_addon<T: Addon>(&mut self) {
24904        self.addons.remove(&std::any::TypeId::of::<T>());
24905    }
24906
24907    pub fn addon<T: Addon>(&self) -> Option<&T> {
24908        let type_id = std::any::TypeId::of::<T>();
24909        self.addons
24910            .get(&type_id)
24911            .and_then(|item| item.to_any().downcast_ref::<T>())
24912    }
24913
24914    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
24915        let type_id = std::any::TypeId::of::<T>();
24916        self.addons
24917            .get_mut(&type_id)
24918            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
24919    }
24920
24921    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
24922        let text_layout_details = self.text_layout_details(window, cx);
24923        let style = &text_layout_details.editor_style;
24924        let font_id = window.text_system().resolve_font(&style.text.font());
24925        let font_size = style.text.font_size.to_pixels(window.rem_size());
24926        let line_height = style.text.line_height_in_pixels(window.rem_size());
24927        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
24928        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
24929
24930        CharacterDimensions {
24931            em_width,
24932            em_advance,
24933            line_height,
24934        }
24935    }
24936
24937    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
24938        self.load_diff_task.clone()
24939    }
24940
24941    fn read_metadata_from_db(
24942        &mut self,
24943        item_id: u64,
24944        workspace_id: WorkspaceId,
24945        window: &mut Window,
24946        cx: &mut Context<Editor>,
24947    ) {
24948        if self.buffer_kind(cx) == ItemBufferKind::Singleton
24949            && !self.mode.is_minimap()
24950            && WorkspaceSettings::get(None, cx).restore_on_startup
24951                != RestoreOnStartupBehavior::EmptyTab
24952        {
24953            let buffer_snapshot = OnceCell::new();
24954
24955            // Get file path for path-based fold lookup
24956            let file_path: Option<Arc<Path>> =
24957                self.buffer().read(cx).as_singleton().and_then(|buffer| {
24958                    project::File::from_dyn(buffer.read(cx).file())
24959                        .map(|file| Arc::from(file.abs_path(cx)))
24960                });
24961
24962            // Try file_folds (path-based) first, fallback to editor_folds (migration)
24963            let (folds, needs_migration) = if let Some(ref path) = file_path {
24964                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
24965                    && !folds.is_empty()
24966                {
24967                    (Some(folds), false)
24968                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
24969                    && !folds.is_empty()
24970                {
24971                    // Found old editor_folds data, will migrate to file_folds
24972                    (Some(folds), true)
24973                } else {
24974                    (None, false)
24975                }
24976            } else {
24977                // No file path, try editor_folds as fallback
24978                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
24979                (folds.filter(|f| !f.is_empty()), false)
24980            };
24981
24982            if let Some(folds) = folds {
24983                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
24984                let snapshot_len = snapshot.len().0;
24985
24986                // Helper: search for fingerprint in buffer, return offset if found
24987                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
24988                    // Ensure we start at a character boundary (defensive)
24989                    let search_start = snapshot
24990                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
24991                        .0;
24992                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
24993
24994                    let mut byte_offset = search_start;
24995                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
24996                        if byte_offset > search_end {
24997                            break;
24998                        }
24999                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25000                            return Some(byte_offset);
25001                        }
25002                        byte_offset += ch.len_utf8();
25003                    }
25004                    None
25005                };
25006
25007                // Track search position to handle duplicate fingerprints correctly.
25008                // Folds are stored in document order, so we advance after each match.
25009                let mut search_start = 0usize;
25010
25011                // Collect db_folds for migration (only folds with valid fingerprints)
25012                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25013
25014                let valid_folds: Vec<_> = folds
25015                    .into_iter()
25016                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25017                        // Skip folds without fingerprints (old data before migration)
25018                        let sfp = start_fp?;
25019                        let efp = end_fp?;
25020                        let efp_len = efp.len();
25021
25022                        // Fast path: check if fingerprints match at stored offsets
25023                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25024                        let start_matches = stored_start < snapshot_len
25025                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25026                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25027                        let end_matches = efp_check_pos >= stored_start
25028                            && stored_end <= snapshot_len
25029                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25030
25031                        let (new_start, new_end) = if start_matches && end_matches {
25032                            // Offsets unchanged, use stored values
25033                            (stored_start, stored_end)
25034                        } else if sfp == efp {
25035                            // Short fold: identical fingerprints can only match once per search
25036                            // Use stored fold length to compute new_end
25037                            let new_start = find_fingerprint(&sfp, search_start)?;
25038                            let fold_len = stored_end - stored_start;
25039                            let new_end = new_start + fold_len;
25040                            (new_start, new_end)
25041                        } else {
25042                            // Slow path: search for fingerprints in buffer
25043                            let new_start = find_fingerprint(&sfp, search_start)?;
25044                            // Search for end_fp after start, then add efp_len to get actual fold end
25045                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25046                            let new_end = efp_pos + efp_len;
25047                            (new_start, new_end)
25048                        };
25049
25050                        // Advance search position for next fold
25051                        search_start = new_end;
25052
25053                        // Validate fold makes sense (end must be after start)
25054                        if new_end <= new_start {
25055                            return None;
25056                        }
25057
25058                        // Collect for migration if needed
25059                        if needs_migration {
25060                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25061                        }
25062
25063                        Some(
25064                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25065                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25066                        )
25067                    })
25068                    .collect();
25069
25070                if !valid_folds.is_empty() {
25071                    self.fold_ranges(valid_folds, false, window, cx);
25072
25073                    // Migrate from editor_folds to file_folds if we loaded from old table
25074                    if needs_migration {
25075                        if let Some(ref path) = file_path {
25076                            let path = path.clone();
25077                            cx.spawn(async move |_, _| {
25078                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25079                                    .await
25080                                    .log_err();
25081                            })
25082                            .detach();
25083                        }
25084                    }
25085                }
25086            }
25087
25088            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25089                && !selections.is_empty()
25090            {
25091                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25092                // skip adding the initial selection to selection history
25093                self.selection_history.mode = SelectionHistoryMode::Skipping;
25094                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25095                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25096                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25097                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25098                    }));
25099                });
25100                self.selection_history.mode = SelectionHistoryMode::Normal;
25101            };
25102        }
25103
25104        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25105    }
25106
25107    /// Load folds from the file_folds database table by file path.
25108    /// Used when manually opening a file that was previously closed.
25109    fn load_folds_from_db(
25110        &mut self,
25111        workspace_id: WorkspaceId,
25112        file_path: PathBuf,
25113        window: &mut Window,
25114        cx: &mut Context<Editor>,
25115    ) {
25116        if self.mode.is_minimap()
25117            || WorkspaceSettings::get(None, cx).restore_on_startup
25118                == RestoreOnStartupBehavior::EmptyTab
25119        {
25120            return;
25121        }
25122
25123        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25124            return;
25125        };
25126        if folds.is_empty() {
25127            return;
25128        }
25129
25130        let snapshot = self.buffer.read(cx).snapshot(cx);
25131        let snapshot_len = snapshot.len().0;
25132
25133        // Helper: search for fingerprint in buffer, return offset if found
25134        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25135            let search_start = snapshot
25136                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25137                .0;
25138            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25139
25140            let mut byte_offset = search_start;
25141            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25142                if byte_offset > search_end {
25143                    break;
25144                }
25145                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25146                    return Some(byte_offset);
25147                }
25148                byte_offset += ch.len_utf8();
25149            }
25150            None
25151        };
25152
25153        let mut search_start = 0usize;
25154
25155        let valid_folds: Vec<_> = folds
25156            .into_iter()
25157            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25158                let sfp = start_fp?;
25159                let efp = end_fp?;
25160                let efp_len = efp.len();
25161
25162                let start_matches = stored_start < snapshot_len
25163                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25164                let efp_check_pos = stored_end.saturating_sub(efp_len);
25165                let end_matches = efp_check_pos >= stored_start
25166                    && stored_end <= snapshot_len
25167                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25168
25169                let (new_start, new_end) = if start_matches && end_matches {
25170                    (stored_start, stored_end)
25171                } else if sfp == efp {
25172                    let new_start = find_fingerprint(&sfp, search_start)?;
25173                    let fold_len = stored_end - stored_start;
25174                    let new_end = new_start + fold_len;
25175                    (new_start, new_end)
25176                } else {
25177                    let new_start = find_fingerprint(&sfp, search_start)?;
25178                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25179                    let new_end = efp_pos + efp_len;
25180                    (new_start, new_end)
25181                };
25182
25183                search_start = new_end;
25184
25185                if new_end <= new_start {
25186                    return None;
25187                }
25188
25189                Some(
25190                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25191                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25192                )
25193            })
25194            .collect();
25195
25196        if !valid_folds.is_empty() {
25197            self.fold_ranges(valid_folds, false, window, cx);
25198        }
25199    }
25200
25201    fn lsp_data_enabled(&self) -> bool {
25202        self.enable_lsp_data && self.mode().is_full()
25203    }
25204
25205    fn update_lsp_data(
25206        &mut self,
25207        for_buffer: Option<BufferId>,
25208        window: &mut Window,
25209        cx: &mut Context<'_, Self>,
25210    ) {
25211        if !self.lsp_data_enabled() {
25212            return;
25213        }
25214
25215        if let Some(buffer_id) = for_buffer {
25216            self.pull_diagnostics(buffer_id, window, cx);
25217        }
25218        self.refresh_semantic_tokens(for_buffer, None, cx);
25219        self.refresh_document_colors(for_buffer, window, cx);
25220        self.refresh_folding_ranges(for_buffer, window, cx);
25221        self.refresh_document_symbols(for_buffer, cx);
25222    }
25223
25224    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25225        if !self.lsp_data_enabled() {
25226            return;
25227        }
25228        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25229            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25230        }
25231    }
25232
25233    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25234        if !self.lsp_data_enabled() {
25235            return;
25236        }
25237
25238        if !self.registered_buffers.contains_key(&buffer_id)
25239            && let Some(project) = self.project.as_ref()
25240        {
25241            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25242                project.update(cx, |project, cx| {
25243                    self.registered_buffers.insert(
25244                        buffer_id,
25245                        project.register_buffer_with_language_servers(&buffer, cx),
25246                    );
25247                });
25248            } else {
25249                self.registered_buffers.remove(&buffer_id);
25250            }
25251        }
25252    }
25253
25254    fn create_style(&self, cx: &App) -> EditorStyle {
25255        let settings = ThemeSettings::get_global(cx);
25256
25257        let mut text_style = match self.mode {
25258            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25259                color: cx.theme().colors().editor_foreground,
25260                font_family: settings.ui_font.family.clone(),
25261                font_features: settings.ui_font.features.clone(),
25262                font_fallbacks: settings.ui_font.fallbacks.clone(),
25263                font_size: rems(0.875).into(),
25264                font_weight: settings.ui_font.weight,
25265                line_height: relative(settings.buffer_line_height.value()),
25266                ..Default::default()
25267            },
25268            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25269                color: cx.theme().colors().editor_foreground,
25270                font_family: settings.buffer_font.family.clone(),
25271                font_features: settings.buffer_font.features.clone(),
25272                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25273                font_size: settings.buffer_font_size(cx).into(),
25274                font_weight: settings.buffer_font.weight,
25275                line_height: relative(settings.buffer_line_height.value()),
25276                ..Default::default()
25277            },
25278        };
25279        if let Some(text_style_refinement) = &self.text_style_refinement {
25280            text_style.refine(text_style_refinement)
25281        }
25282
25283        let background = match self.mode {
25284            EditorMode::SingleLine => cx.theme().system().transparent,
25285            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25286            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25287            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25288        };
25289
25290        EditorStyle {
25291            background,
25292            border: cx.theme().colors().border,
25293            local_player: cx.theme().players().local(),
25294            text: text_style,
25295            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25296            syntax: cx.theme().syntax().clone(),
25297            status: cx.theme().status().clone(),
25298            inlay_hints_style: make_inlay_hints_style(cx),
25299            edit_prediction_styles: make_suggestion_styles(cx),
25300            unnecessary_code_fade: settings.unnecessary_code_fade,
25301            show_underlines: self.diagnostics_enabled(),
25302        }
25303    }
25304
25305    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25306        let multibuffer = self.buffer().read(cx);
25307        let is_singleton = multibuffer.is_singleton();
25308        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25309        let buffer = multibuffer.buffer(*buffer_id)?;
25310
25311        let buffer = buffer.read(cx);
25312        let settings = ThemeSettings::get_global(cx);
25313        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25314        let mut breadcrumbs = if is_singleton {
25315            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25316                buffer
25317                    .snapshot()
25318                    .resolve_file_path(
25319                        self.project
25320                            .as_ref()
25321                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25322                            .unwrap_or_default(),
25323                        cx,
25324                    )
25325                    .unwrap_or_else(|| {
25326                        if multibuffer.is_singleton() {
25327                            multibuffer.title(cx).to_string()
25328                        } else {
25329                            "untitled".to_string()
25330                        }
25331                    })
25332            });
25333            vec![BreadcrumbText {
25334                text,
25335                highlights: None,
25336                font: Some(settings.buffer_font.clone()),
25337            }]
25338        } else {
25339            vec![]
25340        };
25341
25342        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25343            text: symbol.text.clone(),
25344            highlights: Some(symbol.highlight_ranges.clone()),
25345            font: Some(settings.buffer_font.clone()),
25346        }));
25347        Some(breadcrumbs)
25348    }
25349
25350    fn disable_lsp_data(&mut self) {
25351        self.enable_lsp_data = false;
25352    }
25353
25354    fn disable_runnables(&mut self) {
25355        self.enable_runnables = false;
25356    }
25357}
25358
25359fn edit_for_markdown_paste<'a>(
25360    buffer: &MultiBufferSnapshot,
25361    range: Range<MultiBufferOffset>,
25362    to_insert: &'a str,
25363    url: Option<url::Url>,
25364) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25365    if url.is_none() {
25366        return (range, Cow::Borrowed(to_insert));
25367    };
25368
25369    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25370
25371    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25372        Cow::Borrowed(to_insert)
25373    } else {
25374        Cow::Owned(format!("[{old_text}]({to_insert})"))
25375    };
25376    (range, new_text)
25377}
25378
25379fn process_completion_for_edit(
25380    completion: &Completion,
25381    intent: CompletionIntent,
25382    buffer: &Entity<Buffer>,
25383    cursor_position: &text::Anchor,
25384    cx: &mut Context<Editor>,
25385) -> CompletionEdit {
25386    let buffer = buffer.read(cx);
25387    let buffer_snapshot = buffer.snapshot();
25388    let (snippet, new_text) = if completion.is_snippet() {
25389        let mut snippet_source = completion.new_text.clone();
25390        // Workaround for typescript language server issues so that methods don't expand within
25391        // strings and functions with type expressions. The previous point is used because the query
25392        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25393        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25394        let previous_point = if previous_point.column > 0 {
25395            cursor_position.to_previous_offset(&buffer_snapshot)
25396        } else {
25397            cursor_position.to_offset(&buffer_snapshot)
25398        };
25399        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25400            && scope.prefers_label_for_snippet_in_completion()
25401            && let Some(label) = completion.label()
25402            && matches!(
25403                completion.kind(),
25404                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25405            )
25406        {
25407            snippet_source = label;
25408        }
25409        match Snippet::parse(&snippet_source).log_err() {
25410            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25411            None => (None, completion.new_text.clone()),
25412        }
25413    } else {
25414        (None, completion.new_text.clone())
25415    };
25416
25417    let mut range_to_replace = {
25418        let replace_range = &completion.replace_range;
25419        if let CompletionSource::Lsp {
25420            insert_range: Some(insert_range),
25421            ..
25422        } = &completion.source
25423        {
25424            debug_assert_eq!(
25425                insert_range.start, replace_range.start,
25426                "insert_range and replace_range should start at the same position"
25427            );
25428            debug_assert!(
25429                insert_range
25430                    .start
25431                    .cmp(cursor_position, &buffer_snapshot)
25432                    .is_le(),
25433                "insert_range should start before or at cursor position"
25434            );
25435            debug_assert!(
25436                replace_range
25437                    .start
25438                    .cmp(cursor_position, &buffer_snapshot)
25439                    .is_le(),
25440                "replace_range should start before or at cursor position"
25441            );
25442
25443            let should_replace = match intent {
25444                CompletionIntent::CompleteWithInsert => false,
25445                CompletionIntent::CompleteWithReplace => true,
25446                CompletionIntent::Complete | CompletionIntent::Compose => {
25447                    let insert_mode =
25448                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25449                            .completions
25450                            .lsp_insert_mode;
25451                    match insert_mode {
25452                        LspInsertMode::Insert => false,
25453                        LspInsertMode::Replace => true,
25454                        LspInsertMode::ReplaceSubsequence => {
25455                            let mut text_to_replace = buffer.chars_for_range(
25456                                buffer.anchor_before(replace_range.start)
25457                                    ..buffer.anchor_after(replace_range.end),
25458                            );
25459                            let mut current_needle = text_to_replace.next();
25460                            for haystack_ch in completion.label.text.chars() {
25461                                if let Some(needle_ch) = current_needle
25462                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25463                                {
25464                                    current_needle = text_to_replace.next();
25465                                }
25466                            }
25467                            current_needle.is_none()
25468                        }
25469                        LspInsertMode::ReplaceSuffix => {
25470                            if replace_range
25471                                .end
25472                                .cmp(cursor_position, &buffer_snapshot)
25473                                .is_gt()
25474                            {
25475                                let range_after_cursor = *cursor_position..replace_range.end;
25476                                let text_after_cursor = buffer
25477                                    .text_for_range(
25478                                        buffer.anchor_before(range_after_cursor.start)
25479                                            ..buffer.anchor_after(range_after_cursor.end),
25480                                    )
25481                                    .collect::<String>()
25482                                    .to_ascii_lowercase();
25483                                completion
25484                                    .label
25485                                    .text
25486                                    .to_ascii_lowercase()
25487                                    .ends_with(&text_after_cursor)
25488                            } else {
25489                                true
25490                            }
25491                        }
25492                    }
25493                }
25494            };
25495
25496            if should_replace {
25497                replace_range.clone()
25498            } else {
25499                insert_range.clone()
25500            }
25501        } else {
25502            replace_range.clone()
25503        }
25504    };
25505
25506    if range_to_replace
25507        .end
25508        .cmp(cursor_position, &buffer_snapshot)
25509        .is_lt()
25510    {
25511        range_to_replace.end = *cursor_position;
25512    }
25513
25514    let replace_range = range_to_replace.to_offset(buffer);
25515    CompletionEdit {
25516        new_text,
25517        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25518        snippet,
25519    }
25520}
25521
25522struct CompletionEdit {
25523    new_text: String,
25524    replace_range: Range<BufferOffset>,
25525    snippet: Option<Snippet>,
25526}
25527
25528fn comment_delimiter_for_newline(
25529    start_point: &Point,
25530    buffer: &MultiBufferSnapshot,
25531    language: &LanguageScope,
25532) -> Option<Arc<str>> {
25533    let delimiters = language.line_comment_prefixes();
25534    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25535    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25536
25537    let num_of_whitespaces = snapshot
25538        .chars_for_range(range.clone())
25539        .take_while(|c| c.is_whitespace())
25540        .count();
25541    let comment_candidate = snapshot
25542        .chars_for_range(range.clone())
25543        .skip(num_of_whitespaces)
25544        .take(max_len_of_delimiter + 2)
25545        .collect::<String>();
25546    let (delimiter, trimmed_len, is_repl) = delimiters
25547        .iter()
25548        .filter_map(|delimiter| {
25549            let prefix = delimiter.trim_end();
25550            if comment_candidate.starts_with(prefix) {
25551                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25552                {
25553                    stripped_comment.starts_with(" %%")
25554                } else {
25555                    false
25556                };
25557                Some((delimiter, prefix.len(), is_repl))
25558            } else {
25559                None
25560            }
25561        })
25562        .max_by_key(|(_, len, _)| *len)?;
25563
25564    if let Some(BlockCommentConfig {
25565        start: block_start, ..
25566    }) = language.block_comment()
25567    {
25568        let block_start_trimmed = block_start.trim_end();
25569        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25570            let line_content = snapshot
25571                .chars_for_range(range.clone())
25572                .skip(num_of_whitespaces)
25573                .take(block_start_trimmed.len())
25574                .collect::<String>();
25575
25576            if line_content.starts_with(block_start_trimmed) {
25577                return None;
25578            }
25579        }
25580    }
25581
25582    let cursor_is_placed_after_comment_marker =
25583        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25584    if cursor_is_placed_after_comment_marker {
25585        if !is_repl {
25586            return Some(delimiter.clone());
25587        }
25588
25589        let line_content_after_cursor: String = snapshot
25590            .chars_for_range(range)
25591            .skip(start_point.column as usize)
25592            .collect();
25593
25594        if line_content_after_cursor.trim().is_empty() {
25595            return None;
25596        } else {
25597            return Some(delimiter.clone());
25598        }
25599    } else {
25600        None
25601    }
25602}
25603
25604fn documentation_delimiter_for_newline(
25605    start_point: &Point,
25606    buffer: &MultiBufferSnapshot,
25607    language: &LanguageScope,
25608    newline_config: &mut NewlineConfig,
25609) -> Option<Arc<str>> {
25610    let BlockCommentConfig {
25611        start: start_tag,
25612        end: end_tag,
25613        prefix: delimiter,
25614        tab_size: len,
25615    } = language.documentation_comment()?;
25616    let is_within_block_comment = buffer
25617        .language_scope_at(*start_point)
25618        .is_some_and(|scope| scope.override_name() == Some("comment"));
25619    if !is_within_block_comment {
25620        return None;
25621    }
25622
25623    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25624
25625    let num_of_whitespaces = snapshot
25626        .chars_for_range(range.clone())
25627        .take_while(|c| c.is_whitespace())
25628        .count();
25629
25630    // 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.
25631    let column = start_point.column;
25632    let cursor_is_after_start_tag = {
25633        let start_tag_len = start_tag.len();
25634        let start_tag_line = snapshot
25635            .chars_for_range(range.clone())
25636            .skip(num_of_whitespaces)
25637            .take(start_tag_len)
25638            .collect::<String>();
25639        if start_tag_line.starts_with(start_tag.as_ref()) {
25640            num_of_whitespaces + start_tag_len <= column as usize
25641        } else {
25642            false
25643        }
25644    };
25645
25646    let cursor_is_after_delimiter = {
25647        let delimiter_trim = delimiter.trim_end();
25648        let delimiter_line = snapshot
25649            .chars_for_range(range.clone())
25650            .skip(num_of_whitespaces)
25651            .take(delimiter_trim.len())
25652            .collect::<String>();
25653        if delimiter_line.starts_with(delimiter_trim) {
25654            num_of_whitespaces + delimiter_trim.len() <= column as usize
25655        } else {
25656            false
25657        }
25658    };
25659
25660    let mut needs_extra_line = false;
25661    let mut extra_line_additional_indent = IndentSize::spaces(0);
25662
25663    let cursor_is_before_end_tag_if_exists = {
25664        let mut char_position = 0u32;
25665        let mut end_tag_offset = None;
25666
25667        'outer: for chunk in snapshot.text_for_range(range) {
25668            if let Some(byte_pos) = chunk.find(&**end_tag) {
25669                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25670                end_tag_offset = Some(char_position + chars_before_match);
25671                break 'outer;
25672            }
25673            char_position += chunk.chars().count() as u32;
25674        }
25675
25676        if let Some(end_tag_offset) = end_tag_offset {
25677            let cursor_is_before_end_tag = column <= end_tag_offset;
25678            if cursor_is_after_start_tag {
25679                if cursor_is_before_end_tag {
25680                    needs_extra_line = true;
25681                }
25682                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25683                if cursor_is_at_start_of_end_tag {
25684                    extra_line_additional_indent.len = *len;
25685                }
25686            }
25687            cursor_is_before_end_tag
25688        } else {
25689            true
25690        }
25691    };
25692
25693    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25694        && cursor_is_before_end_tag_if_exists
25695    {
25696        let additional_indent = if cursor_is_after_start_tag {
25697            IndentSize::spaces(*len)
25698        } else {
25699            IndentSize::spaces(0)
25700        };
25701
25702        *newline_config = NewlineConfig::Newline {
25703            additional_indent,
25704            extra_line_additional_indent: if needs_extra_line {
25705                Some(extra_line_additional_indent)
25706            } else {
25707                None
25708            },
25709            prevent_auto_indent: true,
25710        };
25711        Some(delimiter.clone())
25712    } else {
25713        None
25714    }
25715}
25716
25717const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25718
25719fn list_delimiter_for_newline(
25720    start_point: &Point,
25721    buffer: &MultiBufferSnapshot,
25722    language: &LanguageScope,
25723    newline_config: &mut NewlineConfig,
25724) -> Option<Arc<str>> {
25725    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25726
25727    let num_of_whitespaces = snapshot
25728        .chars_for_range(range.clone())
25729        .take_while(|c| c.is_whitespace())
25730        .count();
25731
25732    let task_list_entries: Vec<_> = language
25733        .task_list()
25734        .into_iter()
25735        .flat_map(|config| {
25736            config
25737                .prefixes
25738                .iter()
25739                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25740        })
25741        .collect();
25742    let unordered_list_entries: Vec<_> = language
25743        .unordered_list()
25744        .iter()
25745        .map(|marker| (marker.as_ref(), marker.as_ref()))
25746        .collect();
25747
25748    let all_entries: Vec<_> = task_list_entries
25749        .into_iter()
25750        .chain(unordered_list_entries)
25751        .collect();
25752
25753    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25754        let candidate: String = snapshot
25755            .chars_for_range(range.clone())
25756            .skip(num_of_whitespaces)
25757            .take(max_prefix_len)
25758            .collect();
25759
25760        if let Some((prefix, continuation)) = all_entries
25761            .iter()
25762            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25763            .max_by_key(|(prefix, _)| prefix.len())
25764        {
25765            let end_of_prefix = num_of_whitespaces + prefix.len();
25766            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25767            let has_content_after_marker = snapshot
25768                .chars_for_range(range)
25769                .skip(end_of_prefix)
25770                .any(|c| !c.is_whitespace());
25771
25772            if has_content_after_marker && cursor_is_after_prefix {
25773                return Some((*continuation).into());
25774            }
25775
25776            if start_point.column as usize == end_of_prefix {
25777                if num_of_whitespaces == 0 {
25778                    *newline_config = NewlineConfig::ClearCurrentLine;
25779                } else {
25780                    *newline_config = NewlineConfig::UnindentCurrentLine {
25781                        continuation: (*continuation).into(),
25782                    };
25783                }
25784            }
25785
25786            return None;
25787        }
25788    }
25789
25790    let candidate: String = snapshot
25791        .chars_for_range(range.clone())
25792        .skip(num_of_whitespaces)
25793        .take(ORDERED_LIST_MAX_MARKER_LEN)
25794        .collect();
25795
25796    for ordered_config in language.ordered_list() {
25797        let regex = match Regex::new(&ordered_config.pattern) {
25798            Ok(r) => r,
25799            Err(_) => continue,
25800        };
25801
25802        if let Some(captures) = regex.captures(&candidate) {
25803            let full_match = captures.get(0)?;
25804            let marker_len = full_match.len();
25805            let end_of_prefix = num_of_whitespaces + marker_len;
25806            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25807
25808            let has_content_after_marker = snapshot
25809                .chars_for_range(range)
25810                .skip(end_of_prefix)
25811                .any(|c| !c.is_whitespace());
25812
25813            if has_content_after_marker && cursor_is_after_prefix {
25814                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25815                let continuation = ordered_config
25816                    .format
25817                    .replace("{1}", &(number + 1).to_string());
25818                return Some(continuation.into());
25819            }
25820
25821            if start_point.column as usize == end_of_prefix {
25822                let continuation = ordered_config.format.replace("{1}", "1");
25823                if num_of_whitespaces == 0 {
25824                    *newline_config = NewlineConfig::ClearCurrentLine;
25825                } else {
25826                    *newline_config = NewlineConfig::UnindentCurrentLine {
25827                        continuation: continuation.into(),
25828                    };
25829                }
25830            }
25831
25832            return None;
25833        }
25834    }
25835
25836    None
25837}
25838
25839fn is_list_prefix_row(
25840    row: MultiBufferRow,
25841    buffer: &MultiBufferSnapshot,
25842    language: &LanguageScope,
25843) -> bool {
25844    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25845        return false;
25846    };
25847
25848    let num_of_whitespaces = snapshot
25849        .chars_for_range(range.clone())
25850        .take_while(|c| c.is_whitespace())
25851        .count();
25852
25853    let task_list_prefixes: Vec<_> = language
25854        .task_list()
25855        .into_iter()
25856        .flat_map(|config| {
25857            config
25858                .prefixes
25859                .iter()
25860                .map(|p| p.as_ref())
25861                .collect::<Vec<_>>()
25862        })
25863        .collect();
25864    let unordered_list_markers: Vec<_> = language
25865        .unordered_list()
25866        .iter()
25867        .map(|marker| marker.as_ref())
25868        .collect();
25869    let all_prefixes: Vec<_> = task_list_prefixes
25870        .into_iter()
25871        .chain(unordered_list_markers)
25872        .collect();
25873    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
25874        let candidate: String = snapshot
25875            .chars_for_range(range.clone())
25876            .skip(num_of_whitespaces)
25877            .take(max_prefix_len)
25878            .collect();
25879        if all_prefixes
25880            .iter()
25881            .any(|prefix| candidate.starts_with(*prefix))
25882        {
25883            return true;
25884        }
25885    }
25886
25887    let ordered_list_candidate: String = snapshot
25888        .chars_for_range(range)
25889        .skip(num_of_whitespaces)
25890        .take(ORDERED_LIST_MAX_MARKER_LEN)
25891        .collect();
25892    for ordered_config in language.ordered_list() {
25893        let regex = match Regex::new(&ordered_config.pattern) {
25894            Ok(r) => r,
25895            Err(_) => continue,
25896        };
25897        if let Some(captures) = regex.captures(&ordered_list_candidate) {
25898            return captures.get(0).is_some();
25899        }
25900    }
25901
25902    false
25903}
25904
25905#[derive(Debug)]
25906enum NewlineConfig {
25907    /// Insert newline with optional additional indent and optional extra blank line
25908    Newline {
25909        additional_indent: IndentSize,
25910        extra_line_additional_indent: Option<IndentSize>,
25911        prevent_auto_indent: bool,
25912    },
25913    /// Clear the current line
25914    ClearCurrentLine,
25915    /// Unindent the current line and add continuation
25916    UnindentCurrentLine { continuation: Arc<str> },
25917}
25918
25919impl NewlineConfig {
25920    fn has_extra_line(&self) -> bool {
25921        matches!(
25922            self,
25923            Self::Newline {
25924                extra_line_additional_indent: Some(_),
25925                ..
25926            }
25927        )
25928    }
25929
25930    fn insert_extra_newline_brackets(
25931        buffer: &MultiBufferSnapshot,
25932        range: Range<MultiBufferOffset>,
25933        language: &language::LanguageScope,
25934    ) -> bool {
25935        let leading_whitespace_len = buffer
25936            .reversed_chars_at(range.start)
25937            .take_while(|c| c.is_whitespace() && *c != '\n')
25938            .map(|c| c.len_utf8())
25939            .sum::<usize>();
25940        let trailing_whitespace_len = buffer
25941            .chars_at(range.end)
25942            .take_while(|c| c.is_whitespace() && *c != '\n')
25943            .map(|c| c.len_utf8())
25944            .sum::<usize>();
25945        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
25946
25947        language.brackets().any(|(pair, enabled)| {
25948            let pair_start = pair.start.trim_end();
25949            let pair_end = pair.end.trim_start();
25950
25951            enabled
25952                && pair.newline
25953                && buffer.contains_str_at(range.end, pair_end)
25954                && buffer.contains_str_at(
25955                    range.start.saturating_sub_usize(pair_start.len()),
25956                    pair_start,
25957                )
25958        })
25959    }
25960
25961    fn insert_extra_newline_tree_sitter(
25962        buffer: &MultiBufferSnapshot,
25963        range: Range<MultiBufferOffset>,
25964    ) -> bool {
25965        let (buffer, range) = match buffer
25966            .range_to_buffer_ranges(range.start..=range.end)
25967            .as_slice()
25968        {
25969            [(buffer, range, _)] => (*buffer, range.clone()),
25970            _ => return false,
25971        };
25972        let pair = {
25973            let mut result: Option<BracketMatch<usize>> = None;
25974
25975            for pair in buffer
25976                .all_bracket_ranges(range.start.0..range.end.0)
25977                .filter(move |pair| {
25978                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
25979                })
25980            {
25981                let len = pair.close_range.end - pair.open_range.start;
25982
25983                if let Some(existing) = &result {
25984                    let existing_len = existing.close_range.end - existing.open_range.start;
25985                    if len > existing_len {
25986                        continue;
25987                    }
25988                }
25989
25990                result = Some(pair);
25991            }
25992
25993            result
25994        };
25995        let Some(pair) = pair else {
25996            return false;
25997        };
25998        pair.newline_only
25999            && buffer
26000                .chars_for_range(pair.open_range.end..range.start.0)
26001                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26002                .all(|c| c.is_whitespace() && c != '\n')
26003    }
26004}
26005
26006fn update_uncommitted_diff_for_buffer(
26007    editor: Entity<Editor>,
26008    project: &Entity<Project>,
26009    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26010    buffer: Entity<MultiBuffer>,
26011    cx: &mut App,
26012) -> Task<()> {
26013    let mut tasks = Vec::new();
26014    project.update(cx, |project, cx| {
26015        for buffer in buffers {
26016            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26017                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26018            }
26019        }
26020    });
26021    cx.spawn(async move |cx| {
26022        let diffs = future::join_all(tasks).await;
26023        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26024            return;
26025        }
26026
26027        buffer.update(cx, |buffer, cx| {
26028            for diff in diffs.into_iter().flatten() {
26029                buffer.add_diff(diff, cx);
26030            }
26031        });
26032    })
26033}
26034
26035fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26036    let tab_size = tab_size.get() as usize;
26037    let mut width = offset;
26038
26039    for ch in text.chars() {
26040        width += if ch == '\t' {
26041            tab_size - (width % tab_size)
26042        } else {
26043            1
26044        };
26045    }
26046
26047    width - offset
26048}
26049
26050#[cfg(test)]
26051mod tests {
26052    use super::*;
26053
26054    #[test]
26055    fn test_string_size_with_expanded_tabs() {
26056        let nz = |val| NonZeroU32::new(val).unwrap();
26057        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26058        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26059        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26060        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26061        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26062        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26063        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26064        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26065    }
26066}
26067
26068/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26069struct WordBreakingTokenizer<'a> {
26070    input: &'a str,
26071}
26072
26073impl<'a> WordBreakingTokenizer<'a> {
26074    fn new(input: &'a str) -> Self {
26075        Self { input }
26076    }
26077}
26078
26079fn is_char_ideographic(ch: char) -> bool {
26080    use unicode_script::Script::*;
26081    use unicode_script::UnicodeScript;
26082    matches!(ch.script(), Han | Tangut | Yi)
26083}
26084
26085fn is_grapheme_ideographic(text: &str) -> bool {
26086    text.chars().any(is_char_ideographic)
26087}
26088
26089fn is_grapheme_whitespace(text: &str) -> bool {
26090    text.chars().any(|x| x.is_whitespace())
26091}
26092
26093fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26094    text.chars()
26095        .next()
26096        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26097}
26098
26099#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26100enum WordBreakToken<'a> {
26101    Word { token: &'a str, grapheme_len: usize },
26102    InlineWhitespace { token: &'a str, grapheme_len: usize },
26103    Newline,
26104}
26105
26106impl<'a> Iterator for WordBreakingTokenizer<'a> {
26107    /// Yields a span, the count of graphemes in the token, and whether it was
26108    /// whitespace. Note that it also breaks at word boundaries.
26109    type Item = WordBreakToken<'a>;
26110
26111    fn next(&mut self) -> Option<Self::Item> {
26112        use unicode_segmentation::UnicodeSegmentation;
26113        if self.input.is_empty() {
26114            return None;
26115        }
26116
26117        let mut iter = self.input.graphemes(true).peekable();
26118        let mut offset = 0;
26119        let mut grapheme_len = 0;
26120        if let Some(first_grapheme) = iter.next() {
26121            let is_newline = first_grapheme == "\n";
26122            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26123            offset += first_grapheme.len();
26124            grapheme_len += 1;
26125            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26126                if let Some(grapheme) = iter.peek().copied()
26127                    && should_stay_with_preceding_ideograph(grapheme)
26128                {
26129                    offset += grapheme.len();
26130                    grapheme_len += 1;
26131                }
26132            } else {
26133                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26134                let mut next_word_bound = words.peek().copied();
26135                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26136                    next_word_bound = words.next();
26137                }
26138                while let Some(grapheme) = iter.peek().copied() {
26139                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26140                        break;
26141                    };
26142                    if is_grapheme_whitespace(grapheme) != is_whitespace
26143                        || (grapheme == "\n") != is_newline
26144                    {
26145                        break;
26146                    };
26147                    offset += grapheme.len();
26148                    grapheme_len += 1;
26149                    iter.next();
26150                }
26151            }
26152            let token = &self.input[..offset];
26153            self.input = &self.input[offset..];
26154            if token == "\n" {
26155                Some(WordBreakToken::Newline)
26156            } else if is_whitespace {
26157                Some(WordBreakToken::InlineWhitespace {
26158                    token,
26159                    grapheme_len,
26160                })
26161            } else {
26162                Some(WordBreakToken::Word {
26163                    token,
26164                    grapheme_len,
26165                })
26166            }
26167        } else {
26168            None
26169        }
26170    }
26171}
26172
26173#[test]
26174fn test_word_breaking_tokenizer() {
26175    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26176        ("", &[]),
26177        ("  ", &[whitespace("  ", 2)]),
26178        ("Ʒ", &[word("Ʒ", 1)]),
26179        ("Ǽ", &[word("Ǽ", 1)]),
26180        ("", &[word("", 1)]),
26181        ("⋑⋑", &[word("⋑⋑", 2)]),
26182        (
26183            "原理,进而",
26184            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26185        ),
26186        (
26187            "hello world",
26188            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26189        ),
26190        (
26191            "hello, world",
26192            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26193        ),
26194        (
26195            "  hello world",
26196            &[
26197                whitespace("  ", 2),
26198                word("hello", 5),
26199                whitespace(" ", 1),
26200                word("world", 5),
26201            ],
26202        ),
26203        (
26204            "这是什么 \n 钢笔",
26205            &[
26206                word("", 1),
26207                word("", 1),
26208                word("", 1),
26209                word("", 1),
26210                whitespace(" ", 1),
26211                newline(),
26212                whitespace(" ", 1),
26213                word("", 1),
26214                word("", 1),
26215            ],
26216        ),
26217        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26218    ];
26219
26220    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26221        WordBreakToken::Word {
26222            token,
26223            grapheme_len,
26224        }
26225    }
26226
26227    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26228        WordBreakToken::InlineWhitespace {
26229            token,
26230            grapheme_len,
26231        }
26232    }
26233
26234    fn newline() -> WordBreakToken<'static> {
26235        WordBreakToken::Newline
26236    }
26237
26238    for (input, result) in tests {
26239        assert_eq!(
26240            WordBreakingTokenizer::new(input)
26241                .collect::<Vec<_>>()
26242                .as_slice(),
26243            *result,
26244        );
26245    }
26246}
26247
26248fn wrap_with_prefix(
26249    first_line_prefix: String,
26250    subsequent_lines_prefix: String,
26251    unwrapped_text: String,
26252    wrap_column: usize,
26253    tab_size: NonZeroU32,
26254    preserve_existing_whitespace: bool,
26255) -> String {
26256    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26257    let subsequent_lines_prefix_len =
26258        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26259    let mut wrapped_text = String::new();
26260    let mut current_line = first_line_prefix;
26261    let mut is_first_line = true;
26262
26263    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26264    let mut current_line_len = first_line_prefix_len;
26265    let mut in_whitespace = false;
26266    for token in tokenizer {
26267        let have_preceding_whitespace = in_whitespace;
26268        match token {
26269            WordBreakToken::Word {
26270                token,
26271                grapheme_len,
26272            } => {
26273                in_whitespace = false;
26274                let current_prefix_len = if is_first_line {
26275                    first_line_prefix_len
26276                } else {
26277                    subsequent_lines_prefix_len
26278                };
26279                if current_line_len + grapheme_len > wrap_column
26280                    && current_line_len != current_prefix_len
26281                {
26282                    wrapped_text.push_str(current_line.trim_end());
26283                    wrapped_text.push('\n');
26284                    is_first_line = false;
26285                    current_line = subsequent_lines_prefix.clone();
26286                    current_line_len = subsequent_lines_prefix_len;
26287                }
26288                current_line.push_str(token);
26289                current_line_len += grapheme_len;
26290            }
26291            WordBreakToken::InlineWhitespace {
26292                mut token,
26293                mut grapheme_len,
26294            } => {
26295                in_whitespace = true;
26296                if have_preceding_whitespace && !preserve_existing_whitespace {
26297                    continue;
26298                }
26299                if !preserve_existing_whitespace {
26300                    // Keep a single whitespace grapheme as-is
26301                    if let Some(first) =
26302                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26303                    {
26304                        token = first;
26305                    } else {
26306                        token = " ";
26307                    }
26308                    grapheme_len = 1;
26309                }
26310                let current_prefix_len = if is_first_line {
26311                    first_line_prefix_len
26312                } else {
26313                    subsequent_lines_prefix_len
26314                };
26315                if current_line_len + grapheme_len > wrap_column {
26316                    wrapped_text.push_str(current_line.trim_end());
26317                    wrapped_text.push('\n');
26318                    is_first_line = false;
26319                    current_line = subsequent_lines_prefix.clone();
26320                    current_line_len = subsequent_lines_prefix_len;
26321                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26322                    current_line.push_str(token);
26323                    current_line_len += grapheme_len;
26324                }
26325            }
26326            WordBreakToken::Newline => {
26327                in_whitespace = true;
26328                let current_prefix_len = if is_first_line {
26329                    first_line_prefix_len
26330                } else {
26331                    subsequent_lines_prefix_len
26332                };
26333                if preserve_existing_whitespace {
26334                    wrapped_text.push_str(current_line.trim_end());
26335                    wrapped_text.push('\n');
26336                    is_first_line = false;
26337                    current_line = subsequent_lines_prefix.clone();
26338                    current_line_len = subsequent_lines_prefix_len;
26339                } else if have_preceding_whitespace {
26340                    continue;
26341                } else if current_line_len + 1 > wrap_column
26342                    && current_line_len != current_prefix_len
26343                {
26344                    wrapped_text.push_str(current_line.trim_end());
26345                    wrapped_text.push('\n');
26346                    is_first_line = false;
26347                    current_line = subsequent_lines_prefix.clone();
26348                    current_line_len = subsequent_lines_prefix_len;
26349                } else if current_line_len != current_prefix_len {
26350                    current_line.push(' ');
26351                    current_line_len += 1;
26352                }
26353            }
26354        }
26355    }
26356
26357    if !current_line.is_empty() {
26358        wrapped_text.push_str(&current_line);
26359    }
26360    wrapped_text
26361}
26362
26363#[test]
26364fn test_wrap_with_prefix() {
26365    assert_eq!(
26366        wrap_with_prefix(
26367            "# ".to_string(),
26368            "# ".to_string(),
26369            "abcdefg".to_string(),
26370            4,
26371            NonZeroU32::new(4).unwrap(),
26372            false,
26373        ),
26374        "# abcdefg"
26375    );
26376    assert_eq!(
26377        wrap_with_prefix(
26378            "".to_string(),
26379            "".to_string(),
26380            "\thello world".to_string(),
26381            8,
26382            NonZeroU32::new(4).unwrap(),
26383            false,
26384        ),
26385        "hello\nworld"
26386    );
26387    assert_eq!(
26388        wrap_with_prefix(
26389            "// ".to_string(),
26390            "// ".to_string(),
26391            "xx \nyy zz aa bb cc".to_string(),
26392            12,
26393            NonZeroU32::new(4).unwrap(),
26394            false,
26395        ),
26396        "// xx yy zz\n// aa bb cc"
26397    );
26398    assert_eq!(
26399        wrap_with_prefix(
26400            String::new(),
26401            String::new(),
26402            "这是什么 \n 钢笔".to_string(),
26403            3,
26404            NonZeroU32::new(4).unwrap(),
26405            false,
26406        ),
26407        "这是什\n么 钢\n"
26408    );
26409    assert_eq!(
26410        wrap_with_prefix(
26411            String::new(),
26412            String::new(),
26413            format!("foo{}bar", '\u{2009}'), // thin space
26414            80,
26415            NonZeroU32::new(4).unwrap(),
26416            false,
26417        ),
26418        format!("foo{}bar", '\u{2009}')
26419    );
26420}
26421
26422pub trait CollaborationHub {
26423    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26424    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26425    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26426}
26427
26428impl CollaborationHub for Entity<Project> {
26429    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26430        self.read(cx).collaborators()
26431    }
26432
26433    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26434        self.read(cx).user_store().read(cx).participant_indices()
26435    }
26436
26437    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26438        let this = self.read(cx);
26439        let user_ids = this.collaborators().values().map(|c| c.user_id);
26440        this.user_store().read(cx).participant_names(user_ids, cx)
26441    }
26442}
26443
26444pub trait SemanticsProvider {
26445    fn hover(
26446        &self,
26447        buffer: &Entity<Buffer>,
26448        position: text::Anchor,
26449        cx: &mut App,
26450    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26451
26452    fn inline_values(
26453        &self,
26454        buffer_handle: Entity<Buffer>,
26455        range: Range<text::Anchor>,
26456        cx: &mut App,
26457    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26458
26459    fn applicable_inlay_chunks(
26460        &self,
26461        buffer: &Entity<Buffer>,
26462        ranges: &[Range<text::Anchor>],
26463        cx: &mut App,
26464    ) -> Vec<Range<BufferRow>>;
26465
26466    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26467
26468    fn inlay_hints(
26469        &self,
26470        invalidate: InvalidationStrategy,
26471        buffer: Entity<Buffer>,
26472        ranges: Vec<Range<text::Anchor>>,
26473        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26474        cx: &mut App,
26475    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26476
26477    fn semantic_tokens(
26478        &self,
26479        buffer: Entity<Buffer>,
26480        refresh: Option<RefreshForServer>,
26481        cx: &mut App,
26482    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
26483
26484    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26485
26486    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26487
26488    fn document_highlights(
26489        &self,
26490        buffer: &Entity<Buffer>,
26491        position: text::Anchor,
26492        cx: &mut App,
26493    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26494
26495    fn definitions(
26496        &self,
26497        buffer: &Entity<Buffer>,
26498        position: text::Anchor,
26499        kind: GotoDefinitionKind,
26500        cx: &mut App,
26501    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26502
26503    fn range_for_rename(
26504        &self,
26505        buffer: &Entity<Buffer>,
26506        position: text::Anchor,
26507        cx: &mut App,
26508    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26509
26510    fn perform_rename(
26511        &self,
26512        buffer: &Entity<Buffer>,
26513        position: text::Anchor,
26514        new_name: String,
26515        cx: &mut App,
26516    ) -> Option<Task<Result<ProjectTransaction>>>;
26517}
26518
26519pub trait CompletionProvider {
26520    fn completions(
26521        &self,
26522        excerpt_id: ExcerptId,
26523        buffer: &Entity<Buffer>,
26524        buffer_position: text::Anchor,
26525        trigger: CompletionContext,
26526        window: &mut Window,
26527        cx: &mut Context<Editor>,
26528    ) -> Task<Result<Vec<CompletionResponse>>>;
26529
26530    fn resolve_completions(
26531        &self,
26532        _buffer: Entity<Buffer>,
26533        _completion_indices: Vec<usize>,
26534        _completions: Rc<RefCell<Box<[Completion]>>>,
26535        _cx: &mut Context<Editor>,
26536    ) -> Task<Result<bool>> {
26537        Task::ready(Ok(false))
26538    }
26539
26540    fn apply_additional_edits_for_completion(
26541        &self,
26542        _buffer: Entity<Buffer>,
26543        _completions: Rc<RefCell<Box<[Completion]>>>,
26544        _completion_index: usize,
26545        _push_to_history: bool,
26546        _cx: &mut Context<Editor>,
26547    ) -> Task<Result<Option<language::Transaction>>> {
26548        Task::ready(Ok(None))
26549    }
26550
26551    fn is_completion_trigger(
26552        &self,
26553        buffer: &Entity<Buffer>,
26554        position: language::Anchor,
26555        text: &str,
26556        trigger_in_words: bool,
26557        cx: &mut Context<Editor>,
26558    ) -> bool;
26559
26560    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26561
26562    fn sort_completions(&self) -> bool {
26563        true
26564    }
26565
26566    fn filter_completions(&self) -> bool {
26567        true
26568    }
26569
26570    fn show_snippets(&self) -> bool {
26571        false
26572    }
26573}
26574
26575pub trait CodeActionProvider {
26576    fn id(&self) -> Arc<str>;
26577
26578    fn code_actions(
26579        &self,
26580        buffer: &Entity<Buffer>,
26581        range: Range<text::Anchor>,
26582        window: &mut Window,
26583        cx: &mut App,
26584    ) -> Task<Result<Vec<CodeAction>>>;
26585
26586    fn apply_code_action(
26587        &self,
26588        buffer_handle: Entity<Buffer>,
26589        action: CodeAction,
26590        excerpt_id: ExcerptId,
26591        push_to_history: bool,
26592        window: &mut Window,
26593        cx: &mut App,
26594    ) -> Task<Result<ProjectTransaction>>;
26595}
26596
26597impl CodeActionProvider for Entity<Project> {
26598    fn id(&self) -> Arc<str> {
26599        "project".into()
26600    }
26601
26602    fn code_actions(
26603        &self,
26604        buffer: &Entity<Buffer>,
26605        range: Range<text::Anchor>,
26606        _window: &mut Window,
26607        cx: &mut App,
26608    ) -> Task<Result<Vec<CodeAction>>> {
26609        self.update(cx, |project, cx| {
26610            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26611            let code_actions = project.code_actions(buffer, range, None, cx);
26612            cx.background_spawn(async move {
26613                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26614                Ok(code_lens_actions
26615                    .context("code lens fetch")?
26616                    .into_iter()
26617                    .flatten()
26618                    .chain(
26619                        code_actions
26620                            .context("code action fetch")?
26621                            .into_iter()
26622                            .flatten(),
26623                    )
26624                    .collect())
26625            })
26626        })
26627    }
26628
26629    fn apply_code_action(
26630        &self,
26631        buffer_handle: Entity<Buffer>,
26632        action: CodeAction,
26633        _excerpt_id: ExcerptId,
26634        push_to_history: bool,
26635        _window: &mut Window,
26636        cx: &mut App,
26637    ) -> Task<Result<ProjectTransaction>> {
26638        self.update(cx, |project, cx| {
26639            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26640        })
26641    }
26642}
26643
26644fn snippet_completions(
26645    project: &Project,
26646    buffer: &Entity<Buffer>,
26647    buffer_anchor: text::Anchor,
26648    classifier: CharClassifier,
26649    cx: &mut App,
26650) -> Task<Result<CompletionResponse>> {
26651    let languages = buffer.read(cx).languages_at(buffer_anchor);
26652    let snippet_store = project.snippets().read(cx);
26653
26654    let scopes: Vec<_> = languages
26655        .iter()
26656        .filter_map(|language| {
26657            let language_name = language.lsp_id();
26658            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26659
26660            if snippets.is_empty() {
26661                None
26662            } else {
26663                Some((language.default_scope(), snippets))
26664            }
26665        })
26666        .collect();
26667
26668    if scopes.is_empty() {
26669        return Task::ready(Ok(CompletionResponse {
26670            completions: vec![],
26671            display_options: CompletionDisplayOptions::default(),
26672            is_incomplete: false,
26673        }));
26674    }
26675
26676    let snapshot = buffer.read(cx).text_snapshot();
26677    let executor = cx.background_executor().clone();
26678
26679    cx.background_spawn(async move {
26680        let is_word_char = |c| classifier.is_word(c);
26681
26682        let mut is_incomplete = false;
26683        let mut completions: Vec<Completion> = Vec::new();
26684
26685        const MAX_PREFIX_LEN: usize = 128;
26686        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26687        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26688        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26689
26690        let max_buffer_window: String = snapshot
26691            .text_for_range(window_start..buffer_offset)
26692            .collect();
26693
26694        if max_buffer_window.is_empty() {
26695            return Ok(CompletionResponse {
26696                completions: vec![],
26697                display_options: CompletionDisplayOptions::default(),
26698                is_incomplete: true,
26699            });
26700        }
26701
26702        for (_scope, snippets) in scopes.into_iter() {
26703            // Sort snippets by word count to match longer snippet prefixes first.
26704            let mut sorted_snippet_candidates = snippets
26705                .iter()
26706                .enumerate()
26707                .flat_map(|(snippet_ix, snippet)| {
26708                    snippet
26709                        .prefix
26710                        .iter()
26711                        .enumerate()
26712                        .map(move |(prefix_ix, prefix)| {
26713                            let word_count =
26714                                snippet_candidate_suffixes(prefix, &is_word_char).count();
26715                            ((snippet_ix, prefix_ix), prefix, word_count)
26716                        })
26717                })
26718                .collect_vec();
26719            sorted_snippet_candidates
26720                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26721
26722            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26723
26724            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
26725                .take(
26726                    sorted_snippet_candidates
26727                        .first()
26728                        .map(|(_, _, word_count)| *word_count)
26729                        .unwrap_or_default(),
26730                )
26731                .collect_vec();
26732
26733            const MAX_RESULTS: usize = 100;
26734            // Each match also remembers how many characters from the buffer it consumed
26735            let mut matches: Vec<(StringMatch, usize)> = vec![];
26736
26737            let mut snippet_list_cutoff_index = 0;
26738            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26739                let word_count = buffer_index + 1;
26740                // Increase `snippet_list_cutoff_index` until we have all of the
26741                // snippets with sufficiently many words.
26742                while sorted_snippet_candidates
26743                    .get(snippet_list_cutoff_index)
26744                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26745                        *snippet_word_count >= word_count
26746                    })
26747                {
26748                    snippet_list_cutoff_index += 1;
26749                }
26750
26751                // Take only the candidates with at least `word_count` many words
26752                let snippet_candidates_at_word_len =
26753                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26754
26755                let candidates = snippet_candidates_at_word_len
26756                    .iter()
26757                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26758                    .enumerate() // index in `sorted_snippet_candidates`
26759                    // First char must match
26760                    .filter(|(_ix, prefix)| {
26761                        itertools::equal(
26762                            prefix
26763                                .chars()
26764                                .next()
26765                                .into_iter()
26766                                .flat_map(|c| c.to_lowercase()),
26767                            buffer_window
26768                                .chars()
26769                                .next()
26770                                .into_iter()
26771                                .flat_map(|c| c.to_lowercase()),
26772                        )
26773                    })
26774                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26775                    .collect::<Vec<StringMatchCandidate>>();
26776
26777                matches.extend(
26778                    fuzzy::match_strings(
26779                        &candidates,
26780                        &buffer_window,
26781                        buffer_window.chars().any(|c| c.is_uppercase()),
26782                        true,
26783                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26784                        &Default::default(),
26785                        executor.clone(),
26786                    )
26787                    .await
26788                    .into_iter()
26789                    .map(|string_match| (string_match, buffer_window.len())),
26790                );
26791
26792                if matches.len() >= MAX_RESULTS {
26793                    break;
26794                }
26795            }
26796
26797            let to_lsp = |point: &text::Anchor| {
26798                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26799                point_to_lsp(end)
26800            };
26801            let lsp_end = to_lsp(&buffer_anchor);
26802
26803            if matches.len() >= MAX_RESULTS {
26804                is_incomplete = true;
26805            }
26806
26807            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26808                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26809                    sorted_snippet_candidates[string_match.candidate_id];
26810                let snippet = &snippets[snippet_index];
26811                let start = buffer_offset - buffer_window_len;
26812                let start = snapshot.anchor_before(start);
26813                let range = start..buffer_anchor;
26814                let lsp_start = to_lsp(&start);
26815                let lsp_range = lsp::Range {
26816                    start: lsp_start,
26817                    end: lsp_end,
26818                };
26819                Completion {
26820                    replace_range: range,
26821                    new_text: snippet.body.clone(),
26822                    source: CompletionSource::Lsp {
26823                        insert_range: None,
26824                        server_id: LanguageServerId(usize::MAX),
26825                        resolved: true,
26826                        lsp_completion: Box::new(lsp::CompletionItem {
26827                            label: snippet.prefix.first().unwrap().clone(),
26828                            kind: Some(CompletionItemKind::SNIPPET),
26829                            label_details: snippet.description.as_ref().map(|description| {
26830                                lsp::CompletionItemLabelDetails {
26831                                    detail: Some(description.clone()),
26832                                    description: None,
26833                                }
26834                            }),
26835                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26836                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26837                                lsp::InsertReplaceEdit {
26838                                    new_text: snippet.body.clone(),
26839                                    insert: lsp_range,
26840                                    replace: lsp_range,
26841                                },
26842                            )),
26843                            filter_text: Some(snippet.body.clone()),
26844                            sort_text: Some(char::MAX.to_string()),
26845                            ..lsp::CompletionItem::default()
26846                        }),
26847                        lsp_defaults: None,
26848                    },
26849                    label: CodeLabel {
26850                        text: matching_prefix.clone(),
26851                        runs: Vec::new(),
26852                        filter_range: 0..matching_prefix.len(),
26853                    },
26854                    icon_path: None,
26855                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26856                        single_line: snippet.name.clone().into(),
26857                        plain_text: snippet
26858                            .description
26859                            .clone()
26860                            .map(|description| description.into()),
26861                    }),
26862                    insert_text_mode: None,
26863                    confirm: None,
26864                    match_start: Some(start),
26865                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
26866                }
26867            }));
26868        }
26869
26870        Ok(CompletionResponse {
26871            completions,
26872            display_options: CompletionDisplayOptions::default(),
26873            is_incomplete,
26874        })
26875    })
26876}
26877
26878impl CompletionProvider for Entity<Project> {
26879    fn completions(
26880        &self,
26881        _excerpt_id: ExcerptId,
26882        buffer: &Entity<Buffer>,
26883        buffer_position: text::Anchor,
26884        options: CompletionContext,
26885        _window: &mut Window,
26886        cx: &mut Context<Editor>,
26887    ) -> Task<Result<Vec<CompletionResponse>>> {
26888        self.update(cx, |project, cx| {
26889            let task = project.completions(buffer, buffer_position, options, cx);
26890            cx.background_spawn(task)
26891        })
26892    }
26893
26894    fn resolve_completions(
26895        &self,
26896        buffer: Entity<Buffer>,
26897        completion_indices: Vec<usize>,
26898        completions: Rc<RefCell<Box<[Completion]>>>,
26899        cx: &mut Context<Editor>,
26900    ) -> Task<Result<bool>> {
26901        self.update(cx, |project, cx| {
26902            project.lsp_store().update(cx, |lsp_store, cx| {
26903                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
26904            })
26905        })
26906    }
26907
26908    fn apply_additional_edits_for_completion(
26909        &self,
26910        buffer: Entity<Buffer>,
26911        completions: Rc<RefCell<Box<[Completion]>>>,
26912        completion_index: usize,
26913        push_to_history: bool,
26914        cx: &mut Context<Editor>,
26915    ) -> Task<Result<Option<language::Transaction>>> {
26916        self.update(cx, |project, cx| {
26917            project.lsp_store().update(cx, |lsp_store, cx| {
26918                lsp_store.apply_additional_edits_for_completion(
26919                    buffer,
26920                    completions,
26921                    completion_index,
26922                    push_to_history,
26923                    cx,
26924                )
26925            })
26926        })
26927    }
26928
26929    fn is_completion_trigger(
26930        &self,
26931        buffer: &Entity<Buffer>,
26932        position: language::Anchor,
26933        text: &str,
26934        trigger_in_words: bool,
26935        cx: &mut Context<Editor>,
26936    ) -> bool {
26937        let mut chars = text.chars();
26938        let char = if let Some(char) = chars.next() {
26939            char
26940        } else {
26941            return false;
26942        };
26943        if chars.next().is_some() {
26944            return false;
26945        }
26946
26947        let buffer = buffer.read(cx);
26948        let snapshot = buffer.snapshot();
26949        let classifier = snapshot
26950            .char_classifier_at(position)
26951            .scope_context(Some(CharScopeContext::Completion));
26952        if trigger_in_words && classifier.is_word(char) {
26953            return true;
26954        }
26955
26956        buffer.completion_triggers().contains(text)
26957    }
26958
26959    fn show_snippets(&self) -> bool {
26960        true
26961    }
26962}
26963
26964impl SemanticsProvider for WeakEntity<Project> {
26965    fn hover(
26966        &self,
26967        buffer: &Entity<Buffer>,
26968        position: text::Anchor,
26969        cx: &mut App,
26970    ) -> Option<Task<Option<Vec<project::Hover>>>> {
26971        self.update(cx, |project, cx| project.hover(buffer, position, cx))
26972            .ok()
26973    }
26974
26975    fn document_highlights(
26976        &self,
26977        buffer: &Entity<Buffer>,
26978        position: text::Anchor,
26979        cx: &mut App,
26980    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
26981        self.update(cx, |project, cx| {
26982            project.document_highlights(buffer, position, cx)
26983        })
26984        .ok()
26985    }
26986
26987    fn definitions(
26988        &self,
26989        buffer: &Entity<Buffer>,
26990        position: text::Anchor,
26991        kind: GotoDefinitionKind,
26992        cx: &mut App,
26993    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
26994        self.update(cx, |project, cx| match kind {
26995            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
26996            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
26997            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
26998            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
26999        })
27000        .ok()
27001    }
27002
27003    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27004        self.update(cx, |project, cx| {
27005            if project
27006                .active_debug_session(cx)
27007                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27008            {
27009                return true;
27010            }
27011
27012            buffer.update(cx, |buffer, cx| {
27013                project.any_language_server_supports_inlay_hints(buffer, cx)
27014            })
27015        })
27016        .unwrap_or(false)
27017    }
27018
27019    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27020        self.update(cx, |project, cx| {
27021            buffer.update(cx, |buffer, cx| {
27022                project.any_language_server_supports_semantic_tokens(buffer, cx)
27023            })
27024        })
27025        .unwrap_or(false)
27026    }
27027
27028    fn inline_values(
27029        &self,
27030        buffer_handle: Entity<Buffer>,
27031        range: Range<text::Anchor>,
27032        cx: &mut App,
27033    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27034        self.update(cx, |project, cx| {
27035            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27036
27037            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27038        })
27039        .ok()
27040        .flatten()
27041    }
27042
27043    fn applicable_inlay_chunks(
27044        &self,
27045        buffer: &Entity<Buffer>,
27046        ranges: &[Range<text::Anchor>],
27047        cx: &mut App,
27048    ) -> Vec<Range<BufferRow>> {
27049        self.update(cx, |project, cx| {
27050            project.lsp_store().update(cx, |lsp_store, cx| {
27051                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27052            })
27053        })
27054        .unwrap_or_default()
27055    }
27056
27057    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27058        self.update(cx, |project, cx| {
27059            project.lsp_store().update(cx, |lsp_store, _| {
27060                lsp_store.invalidate_inlay_hints(for_buffers)
27061            })
27062        })
27063        .ok();
27064    }
27065
27066    fn inlay_hints(
27067        &self,
27068        invalidate: InvalidationStrategy,
27069        buffer: Entity<Buffer>,
27070        ranges: Vec<Range<text::Anchor>>,
27071        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27072        cx: &mut App,
27073    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27074        self.update(cx, |project, cx| {
27075            project.lsp_store().update(cx, |lsp_store, cx| {
27076                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27077            })
27078        })
27079        .ok()
27080    }
27081
27082    fn semantic_tokens(
27083        &self,
27084        buffer: Entity<Buffer>,
27085        refresh: Option<RefreshForServer>,
27086        cx: &mut App,
27087    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27088        self.update(cx, |this, cx| {
27089            this.lsp_store().update(cx, |lsp_store, cx| {
27090                lsp_store.semantic_tokens(buffer, refresh, cx)
27091            })
27092        })
27093        .ok()
27094    }
27095
27096    fn range_for_rename(
27097        &self,
27098        buffer: &Entity<Buffer>,
27099        position: text::Anchor,
27100        cx: &mut App,
27101    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27102        self.update(cx, |project, cx| {
27103            let buffer = buffer.clone();
27104            let task = project.prepare_rename(buffer.clone(), position, cx);
27105            cx.spawn(async move |_, cx| {
27106                Ok(match task.await? {
27107                    PrepareRenameResponse::Success(range) => Some(range),
27108                    PrepareRenameResponse::InvalidPosition => None,
27109                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27110                        // Fallback on using TreeSitter info to determine identifier range
27111                        buffer.read_with(cx, |buffer, _| {
27112                            let snapshot = buffer.snapshot();
27113                            let (range, kind) = snapshot.surrounding_word(position, None);
27114                            if kind != Some(CharKind::Word) {
27115                                return None;
27116                            }
27117                            Some(
27118                                snapshot.anchor_before(range.start)
27119                                    ..snapshot.anchor_after(range.end),
27120                            )
27121                        })
27122                    }
27123                })
27124            })
27125        })
27126        .ok()
27127    }
27128
27129    fn perform_rename(
27130        &self,
27131        buffer: &Entity<Buffer>,
27132        position: text::Anchor,
27133        new_name: String,
27134        cx: &mut App,
27135    ) -> Option<Task<Result<ProjectTransaction>>> {
27136        self.update(cx, |project, cx| {
27137            project.perform_rename(buffer.clone(), position, new_name, cx)
27138        })
27139        .ok()
27140    }
27141}
27142
27143fn consume_contiguous_rows(
27144    contiguous_row_selections: &mut Vec<Selection<Point>>,
27145    selection: &Selection<Point>,
27146    display_map: &DisplaySnapshot,
27147    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27148) -> (MultiBufferRow, MultiBufferRow) {
27149    contiguous_row_selections.push(selection.clone());
27150    let start_row = starting_row(selection, display_map);
27151    let mut end_row = ending_row(selection, display_map);
27152
27153    while let Some(next_selection) = selections.peek() {
27154        if next_selection.start.row <= end_row.0 {
27155            end_row = ending_row(next_selection, display_map);
27156            contiguous_row_selections.push(selections.next().unwrap().clone());
27157        } else {
27158            break;
27159        }
27160    }
27161    (start_row, end_row)
27162}
27163
27164fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27165    if selection.start.column > 0 {
27166        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27167    } else {
27168        MultiBufferRow(selection.start.row)
27169    }
27170}
27171
27172fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27173    if next_selection.end.column > 0 || next_selection.is_empty() {
27174        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27175    } else {
27176        MultiBufferRow(next_selection.end.row)
27177    }
27178}
27179
27180impl EditorSnapshot {
27181    pub fn remote_selections_in_range<'a>(
27182        &'a self,
27183        range: &'a Range<Anchor>,
27184        collaboration_hub: &dyn CollaborationHub,
27185        cx: &'a App,
27186    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27187        let participant_names = collaboration_hub.user_names(cx);
27188        let participant_indices = collaboration_hub.user_participant_indices(cx);
27189        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27190        let collaborators_by_replica_id = collaborators_by_peer_id
27191            .values()
27192            .map(|collaborator| (collaborator.replica_id, collaborator))
27193            .collect::<HashMap<_, _>>();
27194        self.buffer_snapshot()
27195            .selections_in_range(range, false)
27196            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27197                if replica_id == ReplicaId::AGENT {
27198                    Some(RemoteSelection {
27199                        replica_id,
27200                        selection,
27201                        cursor_shape,
27202                        line_mode,
27203                        collaborator_id: CollaboratorId::Agent,
27204                        user_name: Some("Agent".into()),
27205                        color: cx.theme().players().agent(),
27206                    })
27207                } else {
27208                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27209                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27210                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27211                    Some(RemoteSelection {
27212                        replica_id,
27213                        selection,
27214                        cursor_shape,
27215                        line_mode,
27216                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27217                        user_name,
27218                        color: if let Some(index) = participant_index {
27219                            cx.theme().players().color_for_participant(index.0)
27220                        } else {
27221                            cx.theme().players().absent()
27222                        },
27223                    })
27224                }
27225            })
27226    }
27227
27228    pub fn hunks_for_ranges(
27229        &self,
27230        ranges: impl IntoIterator<Item = Range<Point>>,
27231    ) -> Vec<MultiBufferDiffHunk> {
27232        let mut hunks = Vec::new();
27233        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27234            HashMap::default();
27235        for query_range in ranges {
27236            let query_rows =
27237                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27238            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27239                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27240            ) {
27241                // Include deleted hunks that are adjacent to the query range, because
27242                // otherwise they would be missed.
27243                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27244                if hunk.status().is_deleted() {
27245                    intersects_range |= hunk.row_range.start == query_rows.end;
27246                    intersects_range |= hunk.row_range.end == query_rows.start;
27247                }
27248                if intersects_range {
27249                    if !processed_buffer_rows
27250                        .entry(hunk.buffer_id)
27251                        .or_default()
27252                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27253                    {
27254                        continue;
27255                    }
27256                    hunks.push(hunk);
27257                }
27258            }
27259        }
27260
27261        hunks
27262    }
27263
27264    fn display_diff_hunks_for_rows<'a>(
27265        &'a self,
27266        display_rows: Range<DisplayRow>,
27267        folded_buffers: &'a HashSet<BufferId>,
27268    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27269        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27270        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27271
27272        self.buffer_snapshot()
27273            .diff_hunks_in_range(buffer_start..buffer_end)
27274            .filter_map(|hunk| {
27275                if folded_buffers.contains(&hunk.buffer_id)
27276                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27277                {
27278                    return None;
27279                }
27280
27281                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27282                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27283                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27284                    let line_len = self.buffer_snapshot().line_len(last_row);
27285                    Point::new(last_row.0, line_len)
27286                } else {
27287                    Point::new(hunk.row_range.end.0, 0)
27288                };
27289
27290                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27291                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27292
27293                let display_hunk = if hunk_display_start.column() != 0 {
27294                    DisplayDiffHunk::Folded {
27295                        display_row: hunk_display_start.row(),
27296                    }
27297                } else {
27298                    let mut end_row = hunk_display_end.row();
27299                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27300                        end_row.0 += 1;
27301                    }
27302                    let is_created_file = hunk.is_created_file();
27303
27304                    DisplayDiffHunk::Unfolded {
27305                        status: hunk.status(),
27306                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27307                            ..hunk.diff_base_byte_range.end.0,
27308                        word_diffs: hunk.word_diffs,
27309                        display_row_range: hunk_display_start.row()..end_row,
27310                        multi_buffer_range: Anchor::range_in_buffer(
27311                            hunk.excerpt_id,
27312                            hunk.buffer_range,
27313                        ),
27314                        is_created_file,
27315                    }
27316                };
27317
27318                Some(display_hunk)
27319            })
27320    }
27321
27322    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27323        self.display_snapshot
27324            .buffer_snapshot()
27325            .language_at(position)
27326    }
27327
27328    pub fn is_focused(&self) -> bool {
27329        self.is_focused
27330    }
27331
27332    pub fn placeholder_text(&self) -> Option<String> {
27333        self.placeholder_display_snapshot
27334            .as_ref()
27335            .map(|display_map| display_map.text())
27336    }
27337
27338    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27339        self.scroll_anchor.scroll_position(&self.display_snapshot)
27340    }
27341
27342    pub fn gutter_dimensions(
27343        &self,
27344        font_id: FontId,
27345        font_size: Pixels,
27346        style: &EditorStyle,
27347        window: &mut Window,
27348        cx: &App,
27349    ) -> GutterDimensions {
27350        if self.show_gutter
27351            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27352            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27353        {
27354            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27355                matches!(
27356                    ProjectSettings::get_global(cx).git.git_gutter,
27357                    GitGutterSetting::TrackedFiles
27358                )
27359            });
27360            let gutter_settings = EditorSettings::get_global(cx).gutter;
27361            let show_line_numbers = self
27362                .show_line_numbers
27363                .unwrap_or(gutter_settings.line_numbers);
27364            let line_gutter_width = if show_line_numbers {
27365                // Avoid flicker-like gutter resizes when the line number gains another digit by
27366                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27367                let min_width_for_number_on_gutter =
27368                    ch_advance * gutter_settings.min_line_number_digits as f32;
27369                self.max_line_number_width(style, window)
27370                    .max(min_width_for_number_on_gutter)
27371            } else {
27372                0.0.into()
27373            };
27374
27375            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27376            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27377
27378            let git_blame_entries_width =
27379                self.git_blame_gutter_max_author_length
27380                    .map(|max_author_length| {
27381                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27382                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27383
27384                        /// The number of characters to dedicate to gaps and margins.
27385                        const SPACING_WIDTH: usize = 4;
27386
27387                        let max_char_count = max_author_length.min(renderer.max_author_length())
27388                            + ::git::SHORT_SHA_LENGTH
27389                            + MAX_RELATIVE_TIMESTAMP.len()
27390                            + SPACING_WIDTH;
27391
27392                        ch_advance * max_char_count
27393                    });
27394
27395            let is_singleton = self.buffer_snapshot().is_singleton();
27396
27397            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27398            left_padding += if !is_singleton {
27399                ch_width * 4.0
27400            } else if show_runnables || show_breakpoints {
27401                ch_width * 3.0
27402            } else if show_git_gutter && show_line_numbers {
27403                ch_width * 2.0
27404            } else if show_git_gutter || show_line_numbers {
27405                ch_width
27406            } else {
27407                px(0.)
27408            };
27409
27410            let shows_folds = is_singleton && gutter_settings.folds;
27411
27412            let right_padding = if shows_folds && show_line_numbers {
27413                ch_width * 4.0
27414            } else if shows_folds || (!is_singleton && show_line_numbers) {
27415                ch_width * 3.0
27416            } else if show_line_numbers {
27417                ch_width
27418            } else {
27419                px(0.)
27420            };
27421
27422            GutterDimensions {
27423                left_padding,
27424                right_padding,
27425                width: line_gutter_width + left_padding + right_padding,
27426                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27427                git_blame_entries_width,
27428            }
27429        } else if self.offset_content {
27430            GutterDimensions::default_with_margin(font_id, font_size, cx)
27431        } else {
27432            GutterDimensions::default()
27433        }
27434    }
27435
27436    pub fn render_crease_toggle(
27437        &self,
27438        buffer_row: MultiBufferRow,
27439        row_contains_cursor: bool,
27440        editor: Entity<Editor>,
27441        window: &mut Window,
27442        cx: &mut App,
27443    ) -> Option<AnyElement> {
27444        let folded = self.is_line_folded(buffer_row);
27445        let mut is_foldable = false;
27446
27447        if let Some(crease) = self
27448            .crease_snapshot
27449            .query_row(buffer_row, self.buffer_snapshot())
27450        {
27451            is_foldable = true;
27452            match crease {
27453                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27454                    if let Some(render_toggle) = render_toggle {
27455                        let toggle_callback =
27456                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27457                                if folded {
27458                                    editor.update(cx, |editor, cx| {
27459                                        editor.fold_at(buffer_row, window, cx)
27460                                    });
27461                                } else {
27462                                    editor.update(cx, |editor, cx| {
27463                                        editor.unfold_at(buffer_row, window, cx)
27464                                    });
27465                                }
27466                            });
27467                        return Some((render_toggle)(
27468                            buffer_row,
27469                            folded,
27470                            toggle_callback,
27471                            window,
27472                            cx,
27473                        ));
27474                    }
27475                }
27476            }
27477        }
27478
27479        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27480
27481        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27482            Some(
27483                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27484                    .toggle_state(folded)
27485                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27486                        if folded {
27487                            this.unfold_at(buffer_row, window, cx);
27488                        } else {
27489                            this.fold_at(buffer_row, window, cx);
27490                        }
27491                    }))
27492                    .into_any_element(),
27493            )
27494        } else {
27495            None
27496        }
27497    }
27498
27499    pub fn render_crease_trailer(
27500        &self,
27501        buffer_row: MultiBufferRow,
27502        window: &mut Window,
27503        cx: &mut App,
27504    ) -> Option<AnyElement> {
27505        let folded = self.is_line_folded(buffer_row);
27506        if let Crease::Inline { render_trailer, .. } = self
27507            .crease_snapshot
27508            .query_row(buffer_row, self.buffer_snapshot())?
27509        {
27510            let render_trailer = render_trailer.as_ref()?;
27511            Some(render_trailer(buffer_row, folded, window, cx))
27512        } else {
27513            None
27514        }
27515    }
27516
27517    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27518        let digit_count = self.widest_line_number().ilog10() + 1;
27519        column_pixels(style, digit_count as usize, window)
27520    }
27521
27522    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27523    ///
27524    /// This is positive if `base` is before `line`.
27525    fn relative_line_delta(
27526        &self,
27527        current_selection_head: DisplayRow,
27528        first_visible_row: DisplayRow,
27529        consider_wrapped_lines: bool,
27530    ) -> i64 {
27531        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27532        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27533
27534        if consider_wrapped_lines {
27535            let wrap_snapshot = self.wrap_snapshot();
27536            let base_wrap_row = wrap_snapshot
27537                .make_wrap_point(current_selection_head, Bias::Left)
27538                .row();
27539            let wrap_row = wrap_snapshot
27540                .make_wrap_point(first_visible_row, Bias::Left)
27541                .row();
27542
27543            wrap_row.0 as i64 - base_wrap_row.0 as i64
27544        } else {
27545            let fold_snapshot = self.fold_snapshot();
27546            let base_fold_row = fold_snapshot
27547                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27548                .row();
27549            let fold_row = fold_snapshot
27550                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27551                .row();
27552
27553            fold_row as i64 - base_fold_row as i64
27554        }
27555    }
27556
27557    /// Returns the unsigned relative line number to display for each row in `rows`.
27558    ///
27559    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27560    pub fn calculate_relative_line_numbers(
27561        &self,
27562        rows: &Range<DisplayRow>,
27563        current_selection_head: DisplayRow,
27564        count_wrapped_lines: bool,
27565    ) -> HashMap<DisplayRow, u32> {
27566        let initial_offset =
27567            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27568
27569        self.row_infos(rows.start)
27570            .take(rows.len())
27571            .enumerate()
27572            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27573            .filter(|(_row, row_info)| {
27574                row_info.buffer_row.is_some()
27575                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27576            })
27577            .enumerate()
27578            .filter_map(|(i, (row, row_info))| {
27579                // We want to ensure here that the current line has absolute
27580                // numbering, even if we are in a soft-wrapped line. With the
27581                // exception that if we are in a deleted line, we should number this
27582                // relative with 0, as otherwise it would have no line number at all
27583                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27584
27585                (relative_line_number != 0
27586                    || row_info
27587                        .diff_status
27588                        .is_some_and(|status| status.is_deleted()))
27589                .then_some((row, relative_line_number))
27590            })
27591            .collect()
27592    }
27593}
27594
27595pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27596    let font_size = style.text.font_size.to_pixels(window.rem_size());
27597    let layout = window.text_system().shape_line(
27598        SharedString::from(" ".repeat(column)),
27599        font_size,
27600        &[TextRun {
27601            len: column,
27602            font: style.text.font(),
27603            color: Hsla::default(),
27604            ..Default::default()
27605        }],
27606        None,
27607    );
27608
27609    layout.width
27610}
27611
27612impl Deref for EditorSnapshot {
27613    type Target = DisplaySnapshot;
27614
27615    fn deref(&self) -> &Self::Target {
27616        &self.display_snapshot
27617    }
27618}
27619
27620#[derive(Clone, Debug, PartialEq, Eq)]
27621pub enum EditorEvent {
27622    /// Emitted when the stored review comments change (added, removed, or updated).
27623    ReviewCommentsChanged {
27624        /// The new total count of review comments.
27625        total_count: usize,
27626    },
27627    InputIgnored {
27628        text: Arc<str>,
27629    },
27630    InputHandled {
27631        utf16_range_to_replace: Option<Range<isize>>,
27632        text: Arc<str>,
27633    },
27634    ExcerptsAdded {
27635        buffer: Entity<Buffer>,
27636        predecessor: ExcerptId,
27637        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27638    },
27639    ExcerptsRemoved {
27640        ids: Vec<ExcerptId>,
27641        removed_buffer_ids: Vec<BufferId>,
27642    },
27643    BufferFoldToggled {
27644        ids: Vec<ExcerptId>,
27645        folded: bool,
27646    },
27647    ExcerptsEdited {
27648        ids: Vec<ExcerptId>,
27649    },
27650    ExcerptsExpanded {
27651        ids: Vec<ExcerptId>,
27652    },
27653    ExpandExcerptsRequested {
27654        excerpt_ids: Vec<ExcerptId>,
27655        lines: u32,
27656        direction: ExpandExcerptDirection,
27657    },
27658    StageOrUnstageRequested {
27659        stage: bool,
27660        hunks: Vec<MultiBufferDiffHunk>,
27661    },
27662    OpenExcerptsRequested {
27663        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27664        split: bool,
27665    },
27666    RestoreRequested {
27667        hunks: Vec<MultiBufferDiffHunk>,
27668    },
27669    BufferEdited,
27670    Edited {
27671        transaction_id: clock::Lamport,
27672    },
27673    Reparsed(BufferId),
27674    Focused,
27675    FocusedIn,
27676    Blurred,
27677    DirtyChanged,
27678    Saved,
27679    TitleChanged,
27680    SelectionsChanged {
27681        local: bool,
27682    },
27683    ScrollPositionChanged {
27684        local: bool,
27685        autoscroll: bool,
27686    },
27687    TransactionUndone {
27688        transaction_id: clock::Lamport,
27689    },
27690    TransactionBegun {
27691        transaction_id: clock::Lamport,
27692    },
27693    CursorShapeChanged,
27694    BreadcrumbsChanged,
27695    OutlineSymbolsChanged,
27696    PushedToNavHistory {
27697        anchor: Anchor,
27698        is_deactivate: bool,
27699    },
27700}
27701
27702impl EventEmitter<EditorEvent> for Editor {}
27703
27704impl Focusable for Editor {
27705    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27706        self.focus_handle.clone()
27707    }
27708}
27709
27710impl Render for Editor {
27711    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27712        EditorElement::new(&cx.entity(), self.create_style(cx))
27713    }
27714}
27715
27716impl EntityInputHandler for Editor {
27717    fn text_for_range(
27718        &mut self,
27719        range_utf16: Range<usize>,
27720        adjusted_range: &mut Option<Range<usize>>,
27721        _: &mut Window,
27722        cx: &mut Context<Self>,
27723    ) -> Option<String> {
27724        let snapshot = self.buffer.read(cx).read(cx);
27725        let start = snapshot.clip_offset_utf16(
27726            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27727            Bias::Left,
27728        );
27729        let end = snapshot.clip_offset_utf16(
27730            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27731            Bias::Right,
27732        );
27733        if (start.0.0..end.0.0) != range_utf16 {
27734            adjusted_range.replace(start.0.0..end.0.0);
27735        }
27736        Some(snapshot.text_for_range(start..end).collect())
27737    }
27738
27739    fn selected_text_range(
27740        &mut self,
27741        ignore_disabled_input: bool,
27742        _: &mut Window,
27743        cx: &mut Context<Self>,
27744    ) -> Option<UTF16Selection> {
27745        // Prevent the IME menu from appearing when holding down an alphabetic key
27746        // while input is disabled.
27747        if !ignore_disabled_input && !self.input_enabled {
27748            return None;
27749        }
27750
27751        let selection = self
27752            .selections
27753            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27754        let range = selection.range();
27755
27756        Some(UTF16Selection {
27757            range: range.start.0.0..range.end.0.0,
27758            reversed: selection.reversed,
27759        })
27760    }
27761
27762    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27763        let snapshot = self.buffer.read(cx).read(cx);
27764        let range = self
27765            .text_highlights(HighlightKey::InputComposition, cx)?
27766            .1
27767            .first()?;
27768        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27769    }
27770
27771    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27772        self.clear_highlights(HighlightKey::InputComposition, cx);
27773        self.ime_transaction.take();
27774    }
27775
27776    fn replace_text_in_range(
27777        &mut self,
27778        range_utf16: Option<Range<usize>>,
27779        text: &str,
27780        window: &mut Window,
27781        cx: &mut Context<Self>,
27782    ) {
27783        if !self.input_enabled {
27784            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27785            return;
27786        }
27787
27788        self.transact(window, cx, |this, window, cx| {
27789            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27790                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27791                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27792                Some(this.selection_replacement_ranges(range_utf16, cx))
27793            } else {
27794                this.marked_text_ranges(cx)
27795            };
27796
27797            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27798                let newest_selection_id = this.selections.newest_anchor().id;
27799                this.selections
27800                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27801                    .iter()
27802                    .zip(ranges_to_replace.iter())
27803                    .find_map(|(selection, range)| {
27804                        if selection.id == newest_selection_id {
27805                            Some(
27806                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27807                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27808                            )
27809                        } else {
27810                            None
27811                        }
27812                    })
27813            });
27814
27815            cx.emit(EditorEvent::InputHandled {
27816                utf16_range_to_replace: range_to_replace,
27817                text: text.into(),
27818            });
27819
27820            if let Some(new_selected_ranges) = new_selected_ranges {
27821                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27822                    selections.select_ranges(new_selected_ranges)
27823                });
27824                this.backspace(&Default::default(), window, cx);
27825            }
27826
27827            this.handle_input(text, window, cx);
27828        });
27829
27830        if let Some(transaction) = self.ime_transaction {
27831            self.buffer.update(cx, |buffer, cx| {
27832                buffer.group_until_transaction(transaction, cx);
27833            });
27834        }
27835
27836        self.unmark_text(window, cx);
27837    }
27838
27839    fn replace_and_mark_text_in_range(
27840        &mut self,
27841        range_utf16: Option<Range<usize>>,
27842        text: &str,
27843        new_selected_range_utf16: Option<Range<usize>>,
27844        window: &mut Window,
27845        cx: &mut Context<Self>,
27846    ) {
27847        if !self.input_enabled {
27848            return;
27849        }
27850
27851        let transaction = self.transact(window, cx, |this, window, cx| {
27852            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27853                let snapshot = this.buffer.read(cx).read(cx);
27854                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27855                    for marked_range in &mut marked_ranges {
27856                        marked_range.end = marked_range.start + relative_range_utf16.end;
27857                        marked_range.start += relative_range_utf16.start;
27858                        marked_range.start =
27859                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27860                        marked_range.end =
27861                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27862                    }
27863                }
27864                Some(marked_ranges)
27865            } else if let Some(range_utf16) = range_utf16 {
27866                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27867                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27868                Some(this.selection_replacement_ranges(range_utf16, cx))
27869            } else {
27870                None
27871            };
27872
27873            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27874                let newest_selection_id = this.selections.newest_anchor().id;
27875                this.selections
27876                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27877                    .iter()
27878                    .zip(ranges_to_replace.iter())
27879                    .find_map(|(selection, range)| {
27880                        if selection.id == newest_selection_id {
27881                            Some(
27882                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27883                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27884                            )
27885                        } else {
27886                            None
27887                        }
27888                    })
27889            });
27890
27891            cx.emit(EditorEvent::InputHandled {
27892                utf16_range_to_replace: range_to_replace,
27893                text: text.into(),
27894            });
27895
27896            if let Some(ranges) = ranges_to_replace {
27897                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
27898                    s.select_ranges(ranges)
27899                });
27900            }
27901
27902            let marked_ranges = {
27903                let snapshot = this.buffer.read(cx).read(cx);
27904                this.selections
27905                    .disjoint_anchors_arc()
27906                    .iter()
27907                    .map(|selection| {
27908                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
27909                    })
27910                    .collect::<Vec<_>>()
27911            };
27912
27913            if text.is_empty() {
27914                this.unmark_text(window, cx);
27915            } else {
27916                this.highlight_text(
27917                    HighlightKey::InputComposition,
27918                    marked_ranges.clone(),
27919                    HighlightStyle {
27920                        underline: Some(UnderlineStyle {
27921                            thickness: px(1.),
27922                            color: None,
27923                            wavy: false,
27924                        }),
27925                        ..Default::default()
27926                    },
27927                    cx,
27928                );
27929            }
27930
27931            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
27932            let use_autoclose = this.use_autoclose;
27933            let use_auto_surround = this.use_auto_surround;
27934            this.set_use_autoclose(false);
27935            this.set_use_auto_surround(false);
27936            this.handle_input(text, window, cx);
27937            this.set_use_autoclose(use_autoclose);
27938            this.set_use_auto_surround(use_auto_surround);
27939
27940            if let Some(new_selected_range) = new_selected_range_utf16 {
27941                let snapshot = this.buffer.read(cx).read(cx);
27942                let new_selected_ranges = marked_ranges
27943                    .into_iter()
27944                    .map(|marked_range| {
27945                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
27946                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
27947                            insertion_start.0 + new_selected_range.start,
27948                        ));
27949                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
27950                            insertion_start.0 + new_selected_range.end,
27951                        ));
27952                        snapshot.clip_offset_utf16(new_start, Bias::Left)
27953                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
27954                    })
27955                    .collect::<Vec<_>>();
27956
27957                drop(snapshot);
27958                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27959                    selections.select_ranges(new_selected_ranges)
27960                });
27961            }
27962        });
27963
27964        self.ime_transaction = self.ime_transaction.or(transaction);
27965        if let Some(transaction) = self.ime_transaction {
27966            self.buffer.update(cx, |buffer, cx| {
27967                buffer.group_until_transaction(transaction, cx);
27968            });
27969        }
27970
27971        if self
27972            .text_highlights(HighlightKey::InputComposition, cx)
27973            .is_none()
27974        {
27975            self.ime_transaction.take();
27976        }
27977    }
27978
27979    fn bounds_for_range(
27980        &mut self,
27981        range_utf16: Range<usize>,
27982        element_bounds: gpui::Bounds<Pixels>,
27983        window: &mut Window,
27984        cx: &mut Context<Self>,
27985    ) -> Option<gpui::Bounds<Pixels>> {
27986        let text_layout_details = self.text_layout_details(window, cx);
27987        let CharacterDimensions {
27988            em_width,
27989            em_advance,
27990            line_height,
27991        } = self.character_dimensions(window, cx);
27992
27993        let snapshot = self.snapshot(window, cx);
27994        let scroll_position = snapshot.scroll_position();
27995        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
27996
27997        let start =
27998            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
27999        let x = Pixels::from(
28000            ScrollOffset::from(
28001                snapshot.x_for_display_point(start, &text_layout_details)
28002                    + self.gutter_dimensions.full_width(),
28003            ) - scroll_left,
28004        );
28005        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28006
28007        Some(Bounds {
28008            origin: element_bounds.origin + point(x, y),
28009            size: size(em_width, line_height),
28010        })
28011    }
28012
28013    fn character_index_for_point(
28014        &mut self,
28015        point: gpui::Point<Pixels>,
28016        _window: &mut Window,
28017        _cx: &mut Context<Self>,
28018    ) -> Option<usize> {
28019        let position_map = self.last_position_map.as_ref()?;
28020        if !position_map.text_hitbox.contains(&point) {
28021            return None;
28022        }
28023        let display_point = position_map.point_for_position(point).previous_valid;
28024        let anchor = position_map
28025            .snapshot
28026            .display_point_to_anchor(display_point, Bias::Left);
28027        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28028        Some(utf16_offset.0.0)
28029    }
28030
28031    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28032        self.expects_character_input
28033    }
28034}
28035
28036trait SelectionExt {
28037    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28038    fn spanned_rows(
28039        &self,
28040        include_end_if_at_line_start: bool,
28041        map: &DisplaySnapshot,
28042    ) -> Range<MultiBufferRow>;
28043}
28044
28045impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28046    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28047        let start = self
28048            .start
28049            .to_point(map.buffer_snapshot())
28050            .to_display_point(map);
28051        let end = self
28052            .end
28053            .to_point(map.buffer_snapshot())
28054            .to_display_point(map);
28055        if self.reversed {
28056            end..start
28057        } else {
28058            start..end
28059        }
28060    }
28061
28062    fn spanned_rows(
28063        &self,
28064        include_end_if_at_line_start: bool,
28065        map: &DisplaySnapshot,
28066    ) -> Range<MultiBufferRow> {
28067        let start = self.start.to_point(map.buffer_snapshot());
28068        let mut end = self.end.to_point(map.buffer_snapshot());
28069        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28070            end.row -= 1;
28071        }
28072
28073        let buffer_start = map.prev_line_boundary(start).0;
28074        let buffer_end = map.next_line_boundary(end).0;
28075        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28076    }
28077}
28078
28079impl<T: InvalidationRegion> InvalidationStack<T> {
28080    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28081    where
28082        S: Clone + ToOffset,
28083    {
28084        while let Some(region) = self.last() {
28085            let all_selections_inside_invalidation_ranges =
28086                if selections.len() == region.ranges().len() {
28087                    selections
28088                        .iter()
28089                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28090                        .all(|(selection, invalidation_range)| {
28091                            let head = selection.head().to_offset(buffer);
28092                            invalidation_range.start <= head && invalidation_range.end >= head
28093                        })
28094                } else {
28095                    false
28096                };
28097
28098            if all_selections_inside_invalidation_ranges {
28099                break;
28100            } else {
28101                self.pop();
28102            }
28103        }
28104    }
28105}
28106
28107#[derive(Clone)]
28108struct ErasedEditorImpl(Entity<Editor>);
28109
28110impl ui_input::ErasedEditor for ErasedEditorImpl {
28111    fn text(&self, cx: &App) -> String {
28112        self.0.read(cx).text(cx)
28113    }
28114
28115    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28116        self.0.update(cx, |this, cx| {
28117            this.set_text(text, window, cx);
28118        })
28119    }
28120
28121    fn clear(&self, window: &mut Window, cx: &mut App) {
28122        self.0.update(cx, |this, cx| this.clear(window, cx));
28123    }
28124
28125    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28126        self.0.update(cx, |this, cx| {
28127            this.set_placeholder_text(text, window, cx);
28128        });
28129    }
28130
28131    fn focus_handle(&self, cx: &App) -> FocusHandle {
28132        self.0.read(cx).focus_handle(cx)
28133    }
28134
28135    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28136        let settings = ThemeSettings::get_global(cx);
28137        let theme_color = cx.theme().colors();
28138
28139        let text_style = TextStyle {
28140            font_family: settings.ui_font.family.clone(),
28141            font_features: settings.ui_font.features.clone(),
28142            font_size: rems(0.875).into(),
28143            font_weight: settings.ui_font.weight,
28144            font_style: FontStyle::Normal,
28145            line_height: relative(1.2),
28146            color: theme_color.text,
28147            ..Default::default()
28148        };
28149        let editor_style = EditorStyle {
28150            background: theme_color.ghost_element_background,
28151            local_player: cx.theme().players().local(),
28152            syntax: cx.theme().syntax().clone(),
28153            text: text_style,
28154            ..Default::default()
28155        };
28156        EditorElement::new(&self.0, editor_style).into_any()
28157    }
28158
28159    fn as_any(&self) -> &dyn Any {
28160        &self.0
28161    }
28162
28163    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28164        self.0.update(cx, |editor, cx| {
28165            let editor_offset = editor.buffer().read(cx).len(cx);
28166            editor.change_selections(
28167                SelectionEffects::scroll(Autoscroll::Next),
28168                window,
28169                cx,
28170                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28171            );
28172        });
28173    }
28174
28175    fn subscribe(
28176        &self,
28177        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28178        window: &mut Window,
28179        cx: &mut App,
28180    ) -> Subscription {
28181        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28182            let event = match event {
28183                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28184                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28185                _ => return,
28186            };
28187            (callback)(event, window, cx);
28188        })
28189    }
28190
28191    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28192        self.0.update(cx, |editor, cx| {
28193            editor.set_masked(masked, cx);
28194        });
28195    }
28196}
28197impl<T> Default for InvalidationStack<T> {
28198    fn default() -> Self {
28199        Self(Default::default())
28200    }
28201}
28202
28203impl<T> Deref for InvalidationStack<T> {
28204    type Target = Vec<T>;
28205
28206    fn deref(&self) -> &Self::Target {
28207        &self.0
28208    }
28209}
28210
28211impl<T> DerefMut for InvalidationStack<T> {
28212    fn deref_mut(&mut self) -> &mut Self::Target {
28213        &mut self.0
28214    }
28215}
28216
28217impl InvalidationRegion for SnippetState {
28218    fn ranges(&self) -> &[Range<Anchor>] {
28219        &self.ranges[self.active_index]
28220    }
28221}
28222
28223fn edit_prediction_edit_text(
28224    current_snapshot: &BufferSnapshot,
28225    edits: &[(Range<Anchor>, impl AsRef<str>)],
28226    edit_preview: &EditPreview,
28227    include_deletions: bool,
28228    cx: &App,
28229) -> HighlightedText {
28230    let edits = edits
28231        .iter()
28232        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28233        .collect::<Vec<_>>();
28234
28235    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28236}
28237
28238fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28239    // Fallback for providers that don't provide edit_preview (like Copilot)
28240    // Just show the raw edit text with basic styling
28241    let mut text = String::new();
28242    let mut highlights = Vec::new();
28243
28244    let insertion_highlight_style = HighlightStyle {
28245        color: Some(cx.theme().colors().text),
28246        ..Default::default()
28247    };
28248
28249    for (_, edit_text) in edits {
28250        let start_offset = text.len();
28251        text.push_str(edit_text);
28252        let end_offset = text.len();
28253
28254        if start_offset < end_offset {
28255            highlights.push((start_offset..end_offset, insertion_highlight_style));
28256        }
28257    }
28258
28259    HighlightedText {
28260        text: text.into(),
28261        highlights,
28262    }
28263}
28264
28265pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28266    match severity {
28267        lsp::DiagnosticSeverity::ERROR => colors.error,
28268        lsp::DiagnosticSeverity::WARNING => colors.warning,
28269        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28270        lsp::DiagnosticSeverity::HINT => colors.info,
28271        _ => colors.ignored,
28272    }
28273}
28274
28275pub fn styled_runs_for_code_label<'a>(
28276    label: &'a CodeLabel,
28277    syntax_theme: &'a theme::SyntaxTheme,
28278    local_player: &'a theme::PlayerColor,
28279) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28280    let fade_out = HighlightStyle {
28281        fade_out: Some(0.35),
28282        ..Default::default()
28283    };
28284
28285    let mut prev_end = label.filter_range.end;
28286    label
28287        .runs
28288        .iter()
28289        .enumerate()
28290        .flat_map(move |(ix, (range, highlight_id))| {
28291            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28292                HighlightStyle {
28293                    color: Some(local_player.cursor),
28294                    ..Default::default()
28295                }
28296            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28297                HighlightStyle {
28298                    background_color: Some(local_player.selection),
28299                    ..Default::default()
28300                }
28301            } else if let Some(style) = highlight_id.style(syntax_theme) {
28302                style
28303            } else {
28304                return Default::default();
28305            };
28306            let muted_style = style.highlight(fade_out);
28307
28308            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28309            if range.start >= label.filter_range.end {
28310                if range.start > prev_end {
28311                    runs.push((prev_end..range.start, fade_out));
28312                }
28313                runs.push((range.clone(), muted_style));
28314            } else if range.end <= label.filter_range.end {
28315                runs.push((range.clone(), style));
28316            } else {
28317                runs.push((range.start..label.filter_range.end, style));
28318                runs.push((label.filter_range.end..range.end, muted_style));
28319            }
28320            prev_end = cmp::max(prev_end, range.end);
28321
28322            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28323                runs.push((prev_end..label.text.len(), fade_out));
28324            }
28325
28326            runs
28327        })
28328}
28329
28330pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28331    let mut prev_index = 0;
28332    let mut prev_codepoint: Option<char> = None;
28333    text.char_indices()
28334        .chain([(text.len(), '\0')])
28335        .filter_map(move |(index, codepoint)| {
28336            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28337            let is_boundary = index == text.len()
28338                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28339                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28340            if is_boundary {
28341                let chunk = &text[prev_index..index];
28342                prev_index = index;
28343                Some(chunk)
28344            } else {
28345                None
28346            }
28347        })
28348}
28349
28350/// Given a string of text immediately before the cursor, iterates over possible
28351/// strings a snippet could match to. More precisely: returns an iterator over
28352/// suffixes of `text` created by splitting at word boundaries (before & after
28353/// every non-word character).
28354///
28355/// Shorter suffixes are returned first.
28356pub(crate) fn snippet_candidate_suffixes<'a>(
28357    text: &'a str,
28358    is_word_char: &'a dyn Fn(char) -> bool,
28359) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28360    let mut prev_index = text.len();
28361    let mut prev_codepoint = None;
28362    text.char_indices()
28363        .rev()
28364        .chain([(0, '\0')])
28365        .filter_map(move |(index, codepoint)| {
28366            let prev_index = std::mem::replace(&mut prev_index, index);
28367            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28368            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28369                None
28370            } else {
28371                let chunk = &text[prev_index..]; // go to end of string
28372                Some(chunk)
28373            }
28374        })
28375}
28376
28377pub trait RangeToAnchorExt: Sized {
28378    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28379
28380    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28381        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28382        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28383    }
28384}
28385
28386impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28387    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28388        let start_offset = self.start.to_offset(snapshot);
28389        let end_offset = self.end.to_offset(snapshot);
28390        if start_offset == end_offset {
28391            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28392        } else {
28393            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28394        }
28395    }
28396}
28397
28398pub trait RowExt {
28399    fn as_f64(&self) -> f64;
28400
28401    fn next_row(&self) -> Self;
28402
28403    fn previous_row(&self) -> Self;
28404
28405    fn minus(&self, other: Self) -> u32;
28406}
28407
28408impl RowExt for DisplayRow {
28409    fn as_f64(&self) -> f64 {
28410        self.0 as _
28411    }
28412
28413    fn next_row(&self) -> Self {
28414        Self(self.0 + 1)
28415    }
28416
28417    fn previous_row(&self) -> Self {
28418        Self(self.0.saturating_sub(1))
28419    }
28420
28421    fn minus(&self, other: Self) -> u32 {
28422        self.0 - other.0
28423    }
28424}
28425
28426impl RowExt for MultiBufferRow {
28427    fn as_f64(&self) -> f64 {
28428        self.0 as _
28429    }
28430
28431    fn next_row(&self) -> Self {
28432        Self(self.0 + 1)
28433    }
28434
28435    fn previous_row(&self) -> Self {
28436        Self(self.0.saturating_sub(1))
28437    }
28438
28439    fn minus(&self, other: Self) -> u32 {
28440        self.0 - other.0
28441    }
28442}
28443
28444trait RowRangeExt {
28445    type Row;
28446
28447    fn len(&self) -> usize;
28448
28449    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28450}
28451
28452impl RowRangeExt for Range<MultiBufferRow> {
28453    type Row = MultiBufferRow;
28454
28455    fn len(&self) -> usize {
28456        (self.end.0 - self.start.0) as usize
28457    }
28458
28459    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28460        (self.start.0..self.end.0).map(MultiBufferRow)
28461    }
28462}
28463
28464impl RowRangeExt for Range<DisplayRow> {
28465    type Row = DisplayRow;
28466
28467    fn len(&self) -> usize {
28468        (self.end.0 - self.start.0) as usize
28469    }
28470
28471    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28472        (self.start.0..self.end.0).map(DisplayRow)
28473    }
28474}
28475
28476/// If select range has more than one line, we
28477/// just point the cursor to range.start.
28478fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28479    if range.start.row == range.end.row {
28480        range
28481    } else {
28482        range.start..range.start
28483    }
28484}
28485pub struct KillRing(ClipboardItem);
28486impl Global for KillRing {}
28487
28488const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28489
28490enum BreakpointPromptEditAction {
28491    Log,
28492    Condition,
28493    HitCondition,
28494}
28495
28496struct BreakpointPromptEditor {
28497    pub(crate) prompt: Entity<Editor>,
28498    editor: WeakEntity<Editor>,
28499    breakpoint_anchor: Anchor,
28500    breakpoint: Breakpoint,
28501    edit_action: BreakpointPromptEditAction,
28502    block_ids: HashSet<CustomBlockId>,
28503    editor_margins: Arc<Mutex<EditorMargins>>,
28504    _subscriptions: Vec<Subscription>,
28505}
28506
28507impl BreakpointPromptEditor {
28508    const MAX_LINES: u8 = 4;
28509
28510    fn new(
28511        editor: WeakEntity<Editor>,
28512        breakpoint_anchor: Anchor,
28513        breakpoint: Breakpoint,
28514        edit_action: BreakpointPromptEditAction,
28515        window: &mut Window,
28516        cx: &mut Context<Self>,
28517    ) -> Self {
28518        let base_text = match edit_action {
28519            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28520            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28521            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28522        }
28523        .map(|msg| msg.to_string())
28524        .unwrap_or_default();
28525
28526        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28527        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28528
28529        let prompt = cx.new(|cx| {
28530            let mut prompt = Editor::new(
28531                EditorMode::AutoHeight {
28532                    min_lines: 1,
28533                    max_lines: Some(Self::MAX_LINES as usize),
28534                },
28535                buffer,
28536                None,
28537                window,
28538                cx,
28539            );
28540            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28541            prompt.set_show_cursor_when_unfocused(false, cx);
28542            prompt.set_placeholder_text(
28543                match edit_action {
28544                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28545                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28546                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28547                },
28548                window,
28549                cx,
28550            );
28551
28552            prompt
28553        });
28554
28555        Self {
28556            prompt,
28557            editor,
28558            breakpoint_anchor,
28559            breakpoint,
28560            edit_action,
28561            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28562            block_ids: Default::default(),
28563            _subscriptions: vec![],
28564        }
28565    }
28566
28567    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28568        self.block_ids.extend(block_ids)
28569    }
28570
28571    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28572        if let Some(editor) = self.editor.upgrade() {
28573            let message = self
28574                .prompt
28575                .read(cx)
28576                .buffer
28577                .read(cx)
28578                .as_singleton()
28579                .expect("A multi buffer in breakpoint prompt isn't possible")
28580                .read(cx)
28581                .as_rope()
28582                .to_string();
28583
28584            editor.update(cx, |editor, cx| {
28585                editor.edit_breakpoint_at_anchor(
28586                    self.breakpoint_anchor,
28587                    self.breakpoint.clone(),
28588                    match self.edit_action {
28589                        BreakpointPromptEditAction::Log => {
28590                            BreakpointEditAction::EditLogMessage(message.into())
28591                        }
28592                        BreakpointPromptEditAction::Condition => {
28593                            BreakpointEditAction::EditCondition(message.into())
28594                        }
28595                        BreakpointPromptEditAction::HitCondition => {
28596                            BreakpointEditAction::EditHitCondition(message.into())
28597                        }
28598                    },
28599                    cx,
28600                );
28601
28602                editor.remove_blocks(self.block_ids.clone(), None, cx);
28603                cx.focus_self(window);
28604            });
28605        }
28606    }
28607
28608    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28609        self.editor
28610            .update(cx, |editor, cx| {
28611                editor.remove_blocks(self.block_ids.clone(), None, cx);
28612                window.focus(&editor.focus_handle, cx);
28613            })
28614            .log_err();
28615    }
28616
28617    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28618        let settings = ThemeSettings::get_global(cx);
28619        let text_style = TextStyle {
28620            color: if self.prompt.read(cx).read_only(cx) {
28621                cx.theme().colors().text_disabled
28622            } else {
28623                cx.theme().colors().text
28624            },
28625            font_family: settings.buffer_font.family.clone(),
28626            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28627            font_size: settings.buffer_font_size(cx).into(),
28628            font_weight: settings.buffer_font.weight,
28629            line_height: relative(settings.buffer_line_height.value()),
28630            ..Default::default()
28631        };
28632        EditorElement::new(
28633            &self.prompt,
28634            EditorStyle {
28635                background: cx.theme().colors().editor_background,
28636                local_player: cx.theme().players().local(),
28637                text: text_style,
28638                ..Default::default()
28639            },
28640        )
28641    }
28642
28643    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
28644        let focus_handle = self.prompt.focus_handle(cx);
28645        IconButton::new("cancel", IconName::Close)
28646            .icon_color(Color::Muted)
28647            .shape(IconButtonShape::Square)
28648            .tooltip(move |_window, cx| {
28649                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
28650            })
28651            .on_click(cx.listener(|this, _, window, cx| {
28652                this.cancel(&menu::Cancel, window, cx);
28653            }))
28654    }
28655
28656    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
28657        let focus_handle = self.prompt.focus_handle(cx);
28658        IconButton::new("confirm", IconName::Return)
28659            .icon_color(Color::Muted)
28660            .shape(IconButtonShape::Square)
28661            .tooltip(move |_window, cx| {
28662                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
28663            })
28664            .on_click(cx.listener(|this, _, window, cx| {
28665                this.confirm(&menu::Confirm, window, cx);
28666            }))
28667    }
28668}
28669
28670impl Render for BreakpointPromptEditor {
28671    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28672        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
28673        let editor_margins = *self.editor_margins.lock();
28674        let gutter_dimensions = editor_margins.gutter;
28675        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
28676        let right_padding = editor_margins.right + px(9.);
28677        h_flex()
28678            .key_context("Editor")
28679            .bg(cx.theme().colors().editor_background)
28680            .border_y_1()
28681            .border_color(cx.theme().status().info_border)
28682            .size_full()
28683            .py(window.line_height() / 2.5)
28684            .pr(right_padding)
28685            .on_action(cx.listener(Self::confirm))
28686            .on_action(cx.listener(Self::cancel))
28687            .child(
28688                WithRemSize::new(ui_font_size)
28689                    .h_full()
28690                    .w(left_gutter_width)
28691                    .flex()
28692                    .flex_row()
28693                    .flex_shrink_0()
28694                    .items_center()
28695                    .justify_center()
28696                    .gap_1()
28697                    .child(self.render_close_button(cx)),
28698            )
28699            .child(
28700                h_flex()
28701                    .w_full()
28702                    .justify_between()
28703                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
28704                    .child(
28705                        WithRemSize::new(ui_font_size)
28706                            .flex()
28707                            .flex_row()
28708                            .items_center()
28709                            .child(self.render_confirm_button(cx)),
28710                    ),
28711            )
28712    }
28713}
28714
28715impl Focusable for BreakpointPromptEditor {
28716    fn focus_handle(&self, cx: &App) -> FocusHandle {
28717        self.prompt.focus_handle(cx)
28718    }
28719}
28720
28721fn all_edits_insertions_or_deletions(
28722    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28723    snapshot: &MultiBufferSnapshot,
28724) -> bool {
28725    let mut all_insertions = true;
28726    let mut all_deletions = true;
28727
28728    for (range, new_text) in edits.iter() {
28729        let range_is_empty = range.to_offset(snapshot).is_empty();
28730        let text_is_empty = new_text.is_empty();
28731
28732        if range_is_empty != text_is_empty {
28733            if range_is_empty {
28734                all_deletions = false;
28735            } else {
28736                all_insertions = false;
28737            }
28738        } else {
28739            return false;
28740        }
28741
28742        if !all_insertions && !all_deletions {
28743            return false;
28744        }
28745    }
28746    all_insertions || all_deletions
28747}
28748
28749struct MissingEditPredictionKeybindingTooltip;
28750
28751impl Render for MissingEditPredictionKeybindingTooltip {
28752    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28753        ui::tooltip_container(cx, |container, cx| {
28754            container
28755                .flex_shrink_0()
28756                .max_w_80()
28757                .min_h(rems_from_px(124.))
28758                .justify_between()
28759                .child(
28760                    v_flex()
28761                        .flex_1()
28762                        .text_ui_sm(cx)
28763                        .child(Label::new("Conflict with Accept Keybinding"))
28764                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28765                )
28766                .child(
28767                    h_flex()
28768                        .pb_1()
28769                        .gap_1()
28770                        .items_end()
28771                        .w_full()
28772                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28773                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28774                        }))
28775                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28776                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28777                        })),
28778                )
28779        })
28780    }
28781}
28782
28783#[derive(Debug, Clone, Copy, PartialEq)]
28784pub struct LineHighlight {
28785    pub background: Background,
28786    pub border: Option<gpui::Hsla>,
28787    pub include_gutter: bool,
28788    pub type_id: Option<TypeId>,
28789}
28790
28791struct LineManipulationResult {
28792    pub new_text: String,
28793    pub line_count_before: usize,
28794    pub line_count_after: usize,
28795}
28796
28797fn render_diff_hunk_controls(
28798    row: u32,
28799    status: &DiffHunkStatus,
28800    hunk_range: Range<Anchor>,
28801    is_created_file: bool,
28802    line_height: Pixels,
28803    editor: &Entity<Editor>,
28804    _window: &mut Window,
28805    cx: &mut App,
28806) -> AnyElement {
28807    h_flex()
28808        .h(line_height)
28809        .mr_1()
28810        .gap_1()
28811        .px_0p5()
28812        .pb_1()
28813        .border_x_1()
28814        .border_b_1()
28815        .border_color(cx.theme().colors().border_variant)
28816        .rounded_b_lg()
28817        .bg(cx.theme().colors().editor_background)
28818        .gap_1()
28819        .block_mouse_except_scroll()
28820        .shadow_md()
28821        .child(if status.has_secondary_hunk() {
28822            Button::new(("stage", row as u64), "Stage")
28823                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28824                .tooltip({
28825                    let focus_handle = editor.focus_handle(cx);
28826                    move |_window, cx| {
28827                        Tooltip::for_action_in(
28828                            "Stage Hunk",
28829                            &::git::ToggleStaged,
28830                            &focus_handle,
28831                            cx,
28832                        )
28833                    }
28834                })
28835                .on_click({
28836                    let editor = editor.clone();
28837                    move |_event, _window, cx| {
28838                        editor.update(cx, |editor, cx| {
28839                            editor.stage_or_unstage_diff_hunks(
28840                                true,
28841                                vec![hunk_range.start..hunk_range.start],
28842                                cx,
28843                            );
28844                        });
28845                    }
28846                })
28847        } else {
28848            Button::new(("unstage", row as u64), "Unstage")
28849                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28850                .tooltip({
28851                    let focus_handle = editor.focus_handle(cx);
28852                    move |_window, cx| {
28853                        Tooltip::for_action_in(
28854                            "Unstage Hunk",
28855                            &::git::ToggleStaged,
28856                            &focus_handle,
28857                            cx,
28858                        )
28859                    }
28860                })
28861                .on_click({
28862                    let editor = editor.clone();
28863                    move |_event, _window, cx| {
28864                        editor.update(cx, |editor, cx| {
28865                            editor.stage_or_unstage_diff_hunks(
28866                                false,
28867                                vec![hunk_range.start..hunk_range.start],
28868                                cx,
28869                            );
28870                        });
28871                    }
28872                })
28873        })
28874        .child(
28875            Button::new(("restore", row as u64), "Restore")
28876                .tooltip({
28877                    let focus_handle = editor.focus_handle(cx);
28878                    move |_window, cx| {
28879                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28880                    }
28881                })
28882                .on_click({
28883                    let editor = editor.clone();
28884                    move |_event, window, cx| {
28885                        editor.update(cx, |editor, cx| {
28886                            let snapshot = editor.snapshot(window, cx);
28887                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28888                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28889                        });
28890                    }
28891                })
28892                .disabled(is_created_file),
28893        )
28894        .when(
28895            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28896            |el| {
28897                el.child(
28898                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28899                        .shape(IconButtonShape::Square)
28900                        .icon_size(IconSize::Small)
28901                        // .disabled(!has_multiple_hunks)
28902                        .tooltip({
28903                            let focus_handle = editor.focus_handle(cx);
28904                            move |_window, cx| {
28905                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28906                            }
28907                        })
28908                        .on_click({
28909                            let editor = editor.clone();
28910                            move |_event, window, cx| {
28911                                editor.update(cx, |editor, cx| {
28912                                    let snapshot = editor.snapshot(window, cx);
28913                                    let position =
28914                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28915                                    editor.go_to_hunk_before_or_after_position(
28916                                        &snapshot,
28917                                        position,
28918                                        Direction::Next,
28919                                        true,
28920                                        window,
28921                                        cx,
28922                                    );
28923                                    editor.expand_selected_diff_hunks(cx);
28924                                });
28925                            }
28926                        }),
28927                )
28928                .child(
28929                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28930                        .shape(IconButtonShape::Square)
28931                        .icon_size(IconSize::Small)
28932                        // .disabled(!has_multiple_hunks)
28933                        .tooltip({
28934                            let focus_handle = editor.focus_handle(cx);
28935                            move |_window, cx| {
28936                                Tooltip::for_action_in(
28937                                    "Previous Hunk",
28938                                    &GoToPreviousHunk,
28939                                    &focus_handle,
28940                                    cx,
28941                                )
28942                            }
28943                        })
28944                        .on_click({
28945                            let editor = editor.clone();
28946                            move |_event, window, cx| {
28947                                editor.update(cx, |editor, cx| {
28948                                    let snapshot = editor.snapshot(window, cx);
28949                                    let point =
28950                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
28951                                    editor.go_to_hunk_before_or_after_position(
28952                                        &snapshot,
28953                                        point,
28954                                        Direction::Prev,
28955                                        true,
28956                                        window,
28957                                        cx,
28958                                    );
28959                                    editor.expand_selected_diff_hunks(cx);
28960                                });
28961                            }
28962                        }),
28963                )
28964            },
28965        )
28966        .into_any_element()
28967}
28968
28969pub fn multibuffer_context_lines(cx: &App) -> u32 {
28970    EditorSettings::try_get(cx)
28971        .map(|settings| settings.excerpt_context_lines)
28972        .unwrap_or(2)
28973        .min(32)
28974}